@@ -20,7 +20,6 @@ package org.apache.spark.sql.catalyst.parser
20
20
import java .util .Locale
21
21
import java .util .concurrent .TimeUnit
22
22
23
- import scala .collection .mutable
24
23
import scala .collection .mutable .{ArrayBuffer , Set }
25
24
import scala .jdk .CollectionConverters ._
26
25
import scala .util .{Left , Right }
@@ -44,7 +43,7 @@ import org.apache.spark.sql.catalyst.plans.logical._
44
43
import org .apache .spark .sql .catalyst .trees .CurrentOrigin
45
44
import org .apache .spark .sql .catalyst .trees .TreePattern .PARAMETER
46
45
import org .apache .spark .sql .catalyst .types .DataTypeUtils
47
- import org .apache .spark .sql .catalyst .util .{CharVarcharUtils , DateTimeUtils , GeneratedColumn , IntervalUtils , ResolveDefaultColumns }
46
+ import org .apache .spark .sql .catalyst .util .{CharVarcharUtils , DateTimeUtils , IntervalUtils }
48
47
import org .apache .spark .sql .catalyst .util .DateTimeUtils .{convertSpecialDate , convertSpecialTimestamp , convertSpecialTimestampNTZ , getZoneId , stringToDate , stringToTimestamp , stringToTimestampWithoutTimeZone }
49
48
import org .apache .spark .sql .connector .catalog .{CatalogV2Util , SupportsNamespaces , TableCatalog }
50
49
import org .apache .spark .sql .connector .catalog .TableChange .ColumnPosition
@@ -3123,24 +3122,26 @@ class AstBuilder extends DataTypeAstBuilder with SQLConfHelper with Logging {
3123
3122
* Create top level table schema.
3124
3123
*/
3125
3124
protected def createSchema (ctx : CreateOrReplaceTableColTypeListContext ): StructType = {
3126
- StructType (Option (ctx).toArray.flatMap(visitCreateOrReplaceTableColTypeList))
3125
+ val columns = Option (ctx).toArray.flatMap(visitCreateOrReplaceTableColTypeList)
3126
+ StructType (columns.map(_.toV1Column))
3127
3127
}
3128
3128
3129
3129
/**
3130
- * Create a [[ StructType ]] from a number of CREATE TABLE column definitions.
3130
+ * Get CREATE TABLE column definitions.
3131
3131
*/
3132
3132
override def visitCreateOrReplaceTableColTypeList (
3133
- ctx : CreateOrReplaceTableColTypeListContext ): Seq [StructField ] = withOrigin(ctx) {
3133
+ ctx : CreateOrReplaceTableColTypeListContext ): Seq [ColumnDefinition ] = withOrigin(ctx) {
3134
3134
ctx.createOrReplaceTableColType().asScala.map(visitCreateOrReplaceTableColType).toSeq
3135
3135
}
3136
3136
3137
3137
/**
3138
- * Create a top level [[ StructField ]] from a CREATE TABLE column definition.
3138
+ * Get a CREATE TABLE column definition.
3139
3139
*/
3140
3140
override def visitCreateOrReplaceTableColType (
3141
- ctx : CreateOrReplaceTableColTypeContext ): StructField = withOrigin(ctx) {
3141
+ ctx : CreateOrReplaceTableColTypeContext ): ColumnDefinition = withOrigin(ctx) {
3142
3142
import ctx ._
3143
3143
3144
+ val name : String = colName.getText
3144
3145
// Check that no duplicates exist among any CREATE TABLE column options specified.
3145
3146
var nullable = true
3146
3147
var defaultExpression : Option [DefaultExpressionContext ] = None
@@ -3150,61 +3151,44 @@ class AstBuilder extends DataTypeAstBuilder with SQLConfHelper with Logging {
3150
3151
if (option.NULL != null ) {
3151
3152
if (! nullable) {
3152
3153
throw QueryParsingErrors .duplicateTableColumnDescriptor(
3153
- option, colName.getText , " NOT NULL" )
3154
+ option, name , " NOT NULL" )
3154
3155
}
3155
3156
nullable = false
3156
3157
}
3157
3158
Option (option.defaultExpression()).foreach { expr =>
3159
+ if (! conf.getConf(SQLConf .ENABLE_DEFAULT_COLUMNS )) {
3160
+ throw QueryParsingErrors .defaultColumnNotEnabledError(ctx)
3161
+ }
3158
3162
if (defaultExpression.isDefined) {
3159
3163
throw QueryParsingErrors .duplicateTableColumnDescriptor(
3160
- option, colName.getText , " DEFAULT" )
3164
+ option, name , " DEFAULT" )
3161
3165
}
3162
3166
defaultExpression = Some (expr)
3163
3167
}
3164
3168
Option (option.generationExpression()).foreach { expr =>
3165
3169
if (generationExpression.isDefined) {
3166
3170
throw QueryParsingErrors .duplicateTableColumnDescriptor(
3167
- option, colName.getText , " GENERATED ALWAYS AS" )
3171
+ option, name , " GENERATED ALWAYS AS" )
3168
3172
}
3169
3173
generationExpression = Some (expr)
3170
3174
}
3171
3175
Option (option.commentSpec()).foreach { spec =>
3172
3176
if (commentSpec.isDefined) {
3173
3177
throw QueryParsingErrors .duplicateTableColumnDescriptor(
3174
- option, colName.getText , " COMMENT" )
3178
+ option, name , " COMMENT" )
3175
3179
}
3176
3180
commentSpec = Some (spec)
3177
3181
}
3178
3182
}
3179
3183
3180
- val builder = new MetadataBuilder
3181
- // Add comment to metadata
3182
- commentSpec.map(visitCommentSpec).foreach {
3183
- builder.putString(" comment" , _)
3184
- }
3185
- // Add the 'DEFAULT expression' clause in the column definition, if any, to the column metadata.
3186
- defaultExpression.map(visitDefaultExpression).foreach { field =>
3187
- if (conf.getConf(SQLConf .ENABLE_DEFAULT_COLUMNS )) {
3188
- // Add default to metadata
3189
- builder.putString(ResolveDefaultColumns .CURRENT_DEFAULT_COLUMN_METADATA_KEY , field)
3190
- builder.putString(ResolveDefaultColumns .EXISTS_DEFAULT_COLUMN_METADATA_KEY , field)
3191
- } else {
3192
- throw QueryParsingErrors .defaultColumnNotEnabledError(ctx)
3193
- }
3194
- }
3195
- // Add the 'GENERATED ALWAYS AS expression' clause in the column definition, if any, to the
3196
- // column metadata.
3197
- generationExpression.map(visitGenerationExpression).foreach { field =>
3198
- builder.putString(GeneratedColumn .GENERATION_EXPRESSION_METADATA_KEY , field)
3199
- }
3200
-
3201
- val name : String = colName.getText
3202
-
3203
- StructField (
3184
+ ColumnDefinition (
3204
3185
name = name,
3205
3186
dataType = typedVisit[DataType ](ctx.dataType),
3206
3187
nullable = nullable,
3207
- metadata = builder.build())
3188
+ comment = commentSpec.map(visitCommentSpec),
3189
+ defaultValue = defaultExpression.map(visitDefaultExpression),
3190
+ generationExpression = generationExpression.map(visitGenerationExpression)
3191
+ )
3208
3192
}
3209
3193
3210
3194
/**
@@ -3240,11 +3224,11 @@ class AstBuilder extends DataTypeAstBuilder with SQLConfHelper with Logging {
3240
3224
}
3241
3225
3242
3226
/**
3243
- * Create a default string .
3227
+ * Create `DefaultValueExpression` for a column .
3244
3228
*/
3245
- override def visitDefaultExpression (ctx : DefaultExpressionContext ): String =
3229
+ override def visitDefaultExpression (ctx : DefaultExpressionContext ): DefaultValueExpression =
3246
3230
withOrigin(ctx) {
3247
- getDefaultExpression(ctx.expression(), " DEFAULT" ).originalSQL
3231
+ getDefaultExpression(ctx.expression(), " DEFAULT" )
3248
3232
}
3249
3233
3250
3234
/**
@@ -3410,7 +3394,7 @@ class AstBuilder extends DataTypeAstBuilder with SQLConfHelper with Logging {
3410
3394
* types like `i INT`, which should be appended to the existing table schema.
3411
3395
*/
3412
3396
type TableClauses = (
3413
- Seq [Transform ], Seq [StructField ], Option [BucketSpec ], Map [String , String ],
3397
+ Seq [Transform ], Seq [ColumnDefinition ], Option [BucketSpec ], Map [String , String ],
3414
3398
OptionList , Option [String ], Option [String ], Option [SerdeInfo ], Option [ClusterBySpec ])
3415
3399
3416
3400
/**
@@ -3437,12 +3421,15 @@ class AstBuilder extends DataTypeAstBuilder with SQLConfHelper with Logging {
3437
3421
* Parse a list of transforms or columns.
3438
3422
*/
3439
3423
override def visitPartitionFieldList (
3440
- ctx : PartitionFieldListContext ): (Seq [Transform ], Seq [StructField ]) = withOrigin(ctx) {
3424
+ ctx : PartitionFieldListContext ): (Seq [Transform ], Seq [ColumnDefinition ]) = withOrigin(ctx) {
3441
3425
val (transforms, columns) = ctx.fields.asScala.map {
3442
3426
case transform : PartitionTransformContext =>
3443
3427
(Some (visitPartitionTransform(transform)), None )
3444
3428
case field : PartitionColumnContext =>
3445
- (None , Some (visitColType(field.colType)))
3429
+ val f = visitColType(field.colType)
3430
+ // The parser rule of `visitColType` only supports basic column info with comment.
3431
+ val col = ColumnDefinition (f.name, f.dataType, f.nullable, f.getComment())
3432
+ (None , Some (col))
3446
3433
}.unzip
3447
3434
3448
3435
(transforms.flatten.toSeq, columns.flatten.toSeq)
@@ -3918,13 +3905,13 @@ class AstBuilder extends DataTypeAstBuilder with SQLConfHelper with Logging {
3918
3905
3919
3906
private def partitionExpressions (
3920
3907
partTransforms : Seq [Transform ],
3921
- partCols : Seq [StructField ],
3908
+ partCols : Seq [ColumnDefinition ],
3922
3909
ctx : ParserRuleContext ): Seq [Transform ] = {
3923
3910
if (partTransforms.nonEmpty) {
3924
3911
if (partCols.nonEmpty) {
3925
3912
val references = partTransforms.map(_.describe()).mkString(" , " )
3926
3913
val columns = partCols
3927
- .map(field => s " ${field .name} ${field .dataType.simpleString}" )
3914
+ .map(column => s " ${column .name} ${column .dataType.simpleString}" )
3928
3915
.mkString(" , " )
3929
3916
operationNotAllowed(
3930
3917
s """ PARTITION BY: Cannot mix partition expressions and partition columns:
@@ -3998,22 +3985,6 @@ class AstBuilder extends DataTypeAstBuilder with SQLConfHelper with Logging {
3998
3985
val tableSpec = UnresolvedTableSpec (properties, provider, options, location, comment,
3999
3986
serdeInfo, external)
4000
3987
4001
- // Parse column defaults from the table into separate expressions in the CREATE TABLE operator.
4002
- val specifiedDefaults : mutable.Map [Int , Expression ] = mutable.Map .empty
4003
- Option (ctx.createOrReplaceTableColTypeList()).foreach {
4004
- _.createOrReplaceTableColType().asScala.zipWithIndex.foreach { case (typeContext, index) =>
4005
- typeContext.colDefinitionOption().asScala.foreach { option =>
4006
- Option (option.defaultExpression()).foreach { defaultExprContext =>
4007
- specifiedDefaults.update(index, expression(defaultExprContext.expression()))
4008
- }
4009
- }
4010
- }
4011
- }
4012
- val defaultValueExpressions : Seq [Option [Expression ]] =
4013
- (0 until columns.size).map { index : Int =>
4014
- specifiedDefaults.get(index)
4015
- }
4016
-
4017
3988
Option (ctx.query).map(plan) match {
4018
3989
case Some (_) if columns.nonEmpty =>
4019
3990
operationNotAllowed(
@@ -4033,9 +4004,10 @@ class AstBuilder extends DataTypeAstBuilder with SQLConfHelper with Logging {
4033
4004
case _ =>
4034
4005
// Note: table schema includes both the table columns list and the partition columns
4035
4006
// with data type.
4036
- val schema = StructType (columns ++ partCols)
4037
- CreateTable (withIdentClause(identifierContext, UnresolvedIdentifier (_)),
4038
- schema, partitioning, tableSpec, ignoreIfExists = ifNotExists, defaultValueExpressions)
4007
+ val allColumns = columns ++ partCols
4008
+ CreateTable (
4009
+ withIdentClause(identifierContext, UnresolvedIdentifier (_)),
4010
+ allColumns, partitioning, tableSpec, ignoreIfExists = ifNotExists)
4039
4011
}
4040
4012
}
4041
4013
@@ -4107,10 +4079,10 @@ class AstBuilder extends DataTypeAstBuilder with SQLConfHelper with Logging {
4107
4079
case _ =>
4108
4080
// Note: table schema includes both the table columns list and the partition columns
4109
4081
// with data type.
4110
- val schema = StructType ( columns ++ partCols)
4082
+ val allColumns = columns ++ partCols
4111
4083
ReplaceTable (
4112
4084
withIdentClause(ctx.replaceTableHeader.identifierReference(), UnresolvedIdentifier (_)),
4113
- schema , partitioning, tableSpec, orCreate = orCreate)
4085
+ allColumns , partitioning, tableSpec, orCreate = orCreate)
4114
4086
}
4115
4087
}
4116
4088
@@ -4246,7 +4218,7 @@ class AstBuilder extends DataTypeAstBuilder with SQLConfHelper with Logging {
4246
4218
// Add the 'DEFAULT expression' clause in the column definition, if any, to the column metadata.
4247
4219
val defaultExpr = defaultExpression.map(visitDefaultExpression).map { field =>
4248
4220
if (conf.getConf(SQLConf .ENABLE_DEFAULT_COLUMNS )) {
4249
- field
4221
+ field.originalSQL
4250
4222
} else {
4251
4223
throw QueryParsingErrors .defaultColumnNotEnabledError(ctx)
4252
4224
}
@@ -4343,7 +4315,7 @@ class AstBuilder extends DataTypeAstBuilder with SQLConfHelper with Logging {
4343
4315
}
4344
4316
val setDefaultExpression : Option [String ] =
4345
4317
if (action.defaultExpression != null ) {
4346
- Option (action.defaultExpression()).map(visitDefaultExpression)
4318
+ Option (action.defaultExpression()).map(visitDefaultExpression).map(_.originalSQL)
4347
4319
} else if (action.dropDefault != null ) {
4348
4320
Some (" " )
4349
4321
} else {
0 commit comments