@@ -383,11 +383,11 @@ class SparkSqlAstBuilder(conf: SQLConf) extends AstBuilder(conf) {
383383 * {{{
384384 * CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db_name.]table_name
385385 * USING table_provider
386- * [OPTIONS table_property_list]
387386 * create_table_clauses
388387 * [[AS] select_statement];
389388 *
390389 * create_table_clauses (order insensitive):
390+ * [OPTIONS table_property_list]
391391 * [PARTITIONED BY (col_name, col_name, ...)]
392392 * [CLUSTERED BY (col_name, col_name, ...)
393393 * [SORTED BY (col_name [ASC|DESC], ...)]
@@ -408,6 +408,8 @@ class SparkSqlAstBuilder(conf: SQLConf) extends AstBuilder(conf) {
408408 checkDuplicateClauses(ctx.OPTIONS , " OPTIONS" , ctx)
409409 checkDuplicateClauses(ctx.PARTITIONED , " PARTITIONED BY" , ctx)
410410 checkDuplicateClauses(ctx.COMMENT , " COMMENT" , ctx)
411+ checkDuplicateClauses(ctx.bucketSpec(), " CLUSTERED BY" , ctx)
412+ checkDuplicateClauses(ctx.locationSpec, " LOCATION" , ctx)
411413
412414 val options = Option (ctx.options).map(visitPropertyKeyValues).getOrElse(Map .empty)
413415 val provider = ctx.tableProvider.qualifiedName.getText
@@ -417,17 +419,9 @@ class SparkSqlAstBuilder(conf: SQLConf) extends AstBuilder(conf) {
417419 .map(visitIdentifierList(_).toArray)
418420 .getOrElse(Array .empty[String ])
419421 val properties = Option (ctx.tableProps).map(visitPropertyKeyValues).getOrElse(Map .empty)
420- val bucketSpec = if (ctx.bucketSpec().size > 1 ) {
421- duplicateClausesNotAllowed(" CLUSTERED BY" , ctx)
422- } else {
423- ctx.bucketSpec().asScala.headOption.map(visitBucketSpec)
424- }
422+ val bucketSpec = ctx.bucketSpec().asScala.headOption.map(visitBucketSpec)
425423
426- val location = if (ctx.locationSpec.size > 1 ) {
427- duplicateClausesNotAllowed(" LOCATION" , ctx)
428- } else {
429- ctx.locationSpec.asScala.headOption.map(visitLocationSpec)
430- }
424+ val location = ctx.locationSpec.asScala.headOption.map(visitLocationSpec)
431425 val storage = DataSource .buildStorageFormatFromOptions(options)
432426
433427 if (location.isDefined && storage.locationUri.isDefined) {
@@ -1131,16 +1125,16 @@ class SparkSqlAstBuilder(conf: SQLConf) extends AstBuilder(conf) {
11311125 checkDuplicateClauses(ctx.TBLPROPERTIES , " TBLPROPERTIES" , ctx)
11321126 checkDuplicateClauses(ctx.PARTITIONED , " PARTITIONED BY" , ctx)
11331127 checkDuplicateClauses(ctx.COMMENT , " COMMENT" , ctx)
1128+ checkDuplicateClauses(ctx.bucketSpec(), " CLUSTERED BY" , ctx)
1129+ checkDuplicateClauses(ctx.createFileFormat, " STORED AS/BY" , ctx)
1130+ checkDuplicateClauses(ctx.rowFormat, " ROW FORMAT" , ctx)
1131+ checkDuplicateClauses(ctx.locationSpec, " LOCATION" , ctx)
11341132
11351133 val dataCols = Option (ctx.columns).map(visitColTypeList).getOrElse(Nil )
11361134 val partitionCols = Option (ctx.partitionColumns).map(visitColTypeList).getOrElse(Nil )
11371135 val properties = Option (ctx.tableProps).map(visitPropertyKeyValues).getOrElse(Map .empty)
11381136 val selectQuery = Option (ctx.query).map(plan)
1139- val bucketSpec = if (ctx.bucketSpec().size > 1 ) {
1140- duplicateClausesNotAllowed(" CLUSTERED BY" , ctx)
1141- } else {
1142- ctx.bucketSpec().asScala.headOption.map(visitBucketSpec)
1143- }
1137+ val bucketSpec = ctx.bucketSpec().asScala.headOption.map(visitBucketSpec)
11441138
11451139 // Note: Hive requires partition columns to be distinct from the schema, so we need
11461140 // to include the partition columns here explicitly
@@ -1149,23 +1143,11 @@ class SparkSqlAstBuilder(conf: SQLConf) extends AstBuilder(conf) {
11491143 // Storage format
11501144 val defaultStorage = HiveSerDe .getDefaultStorage(conf)
11511145 validateRowFormatFileFormat(ctx.rowFormat.asScala, ctx.createFileFormat.asScala, ctx)
1152- val fileStorage = if (ctx.createFileFormat.size > 1 ) {
1153- duplicateClausesNotAllowed(" STORED AS/BY" , ctx)
1154- } else {
1155- ctx.createFileFormat.asScala.headOption.map(visitCreateFileFormat)
1156- .getOrElse(CatalogStorageFormat .empty)
1157- }
1158- val rowStorage = if (ctx.rowFormat.size > 1 ) {
1159- duplicateClausesNotAllowed(" ROW FORMAT" , ctx)
1160- } else {
1161- ctx.rowFormat.asScala.headOption.map(visitRowFormat)
1162- .getOrElse(CatalogStorageFormat .empty)
1163- }
1164- val location = if (ctx.locationSpec.size > 1 ) {
1165- duplicateClausesNotAllowed(" LOCATION" , ctx)
1166- } else {
1167- ctx.locationSpec.asScala.headOption.map(visitLocationSpec)
1168- }
1146+ val fileStorage = ctx.createFileFormat.asScala.headOption.map(visitCreateFileFormat)
1147+ .getOrElse(CatalogStorageFormat .empty)
1148+ val rowStorage = ctx.rowFormat.asScala.headOption.map(visitRowFormat)
1149+ .getOrElse(CatalogStorageFormat .empty)
1150+ val location = ctx.locationSpec.asScala.headOption.map(visitLocationSpec)
11691151 // If we are creating an EXTERNAL table, then the LOCATION field is required
11701152 if (external && location.isEmpty) {
11711153 operationNotAllowed(" CREATE EXTERNAL TABLE must be accompanied by LOCATION" , ctx)
0 commit comments