diff --git a/sql/catalyst/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBase.g4 b/sql/catalyst/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBase.g4 index eb42d9f85bf3..b2a2b0f31a9c 100644 --- a/sql/catalyst/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBase.g4 +++ b/sql/catalyst/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBase.g4 @@ -161,6 +161,9 @@ statement | ALTER TABLE table=multipartIdentifier (ALTER | CHANGE) COLUMN? column=multipartIdentifier (TYPE dataType)? (COMMENT comment=STRING)? colPosition? #alterTableColumn + | ALTER TABLE table=multipartIdentifier partitionSpec? + CHANGE COLUMN? + colName=multipartIdentifier colType colPosition? #hiveChangeColumn | ALTER TABLE multipartIdentifier (partitionSpec)? SET SERDE STRING (WITH SERDEPROPERTIES tablePropertyList)? #setTableSerDe | ALTER TABLE multipartIdentifier (partitionSpec)? diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala index e903c9cd8211..42253c17dc8d 100644 --- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala +++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala @@ -2872,6 +2872,34 @@ class AstBuilder(conf: SQLConf) extends SqlBaseBaseVisitor[AnyRef] with Logging Option(ctx.colPosition).map(typedVisit[ColumnPosition])) } + /** + * Parse a [[AlterTableAlterColumnStatement]] command. This is Hive SQL syntax. + * + * For example: + * {{{ + * ALTER TABLE table [PARTITION partition_spec] + * CHANGE [COLUMN] column_old_name column_new_name column_dataType [COMMENT column_comment] + * [FIRST | AFTER column_name]; + * }}} + */ + override def visitHiveChangeColumn(ctx: HiveChangeColumnContext): LogicalPlan = withOrigin(ctx) { + if (ctx.partitionSpec != null) { + operationNotAllowed("ALTER TABLE table PARTITION partition_spec CHANGE COLUMN", ctx) + } + val columnNameParts = typedVisit[Seq[String]](ctx.colName) + if (!conf.resolver(columnNameParts.last, ctx.colType().colName.getText)) { + throw new AnalysisException("Renaming column is not supported in Hive-style ALTER COLUMN, " + + "please run RENAME COLUMN instead.") + } + + AlterTableAlterColumnStatement( + typedVisit[Seq[String]](ctx.table), + columnNameParts, + Option(ctx.colType().dataType()).map(typedVisit[DataType]), + Option(ctx.colType().STRING()).map(string), + Option(ctx.colPosition).map(typedVisit[ColumnPosition])) + } + /** * Parse a [[AlterTableDropColumnsStatement]] command. * diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/DDLParserSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/DDLParserSuite.scala index ec3a731b38a8..d6b2dcb7156b 100644 --- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/DDLParserSuite.scala +++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/DDLParserSuite.scala @@ -664,6 +664,46 @@ class DDLParserSuite extends AnalysisTest { } } + test("alter table: hive style") { + val sql1 = "ALTER TABLE table_name CHANGE COLUMN a.b.c c INT" + val sql2 = "ALTER TABLE table_name CHANGE COLUMN a.b.c c INT COMMENT 'new_comment'" + val sql3 = "ALTER TABLE table_name CHANGE COLUMN a.b.c c INT AFTER other_col" + + comparePlans( + parsePlan(sql1), + AlterTableAlterColumnStatement( + Seq("table_name"), + Seq("a", "b", "c"), + Some(IntegerType), + None, + None)) + + comparePlans( + parsePlan(sql2), + AlterTableAlterColumnStatement( + Seq("table_name"), + Seq("a", "b", "c"), + Some(IntegerType), + Some("new_comment"), + None)) + + comparePlans( + parsePlan(sql3), + AlterTableAlterColumnStatement( + Seq("table_name"), + Seq("a", "b", "c"), + Some(IntegerType), + None, + Some(after("other_col")))) + + // renaming column not supported in hive style ALTER COLUMN. + intercept("ALTER TABLE table_name CHANGE COLUMN a.b.c new_name INT", + "please run RENAME COLUMN instead") + + // ALTER COLUMN for a partition is not supported. + intercept("ALTER TABLE table_name PARTITION (a='1') CHANGE COLUMN a.b.c c INT") + } + test("alter table/view: rename table/view") { comparePlans( parsePlan("ALTER TABLE a.b.c RENAME TO x.y.z"),