-
Notifications
You must be signed in to change notification settings - Fork 29k
[SPARK-17910][SQL] Allow users to update the comment of a column #15717
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e764be0
d134b5f
a4aa6f1
dea5c27
49d74fb
f667942
ce6cfa3
9515a09
3e4be49
8c46704
945aa28
f49b0a0
b9e8894
a71683c
57934d4
73c82fc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -274,6 +274,77 @@ case class AlterTableUnsetPropertiesCommand( | |
|
|
||
| } | ||
|
|
||
|
|
||
| /** | ||
| * A command to change the column for a table, only support changing the comment of a non-partition | ||
| * column for now. | ||
| * | ||
| * The syntax of using this command in SQL is: | ||
| * {{{ | ||
| * ALTER TABLE table_identifier | ||
| * CHANGE [COLUMN] column_old_name column_new_name column_dataType [COMMENT column_comment] | ||
| * [FIRST | AFTER column_name]; | ||
| * }}} | ||
| */ | ||
| case class AlterTableChangeColumnCommand( | ||
| tableName: TableIdentifier, | ||
| columnName: String, | ||
| newColumn: StructField) extends RunnableCommand { | ||
|
|
||
| // TODO: support change column name/dataType/metadata/position. | ||
| override def run(sparkSession: SparkSession): Seq[Row] = { | ||
| val catalog = sparkSession.sessionState.catalog | ||
| val table = catalog.getTableMetadata(tableName) | ||
| val resolver = sparkSession.sessionState.conf.resolver | ||
| DDLUtils.verifyAlterTableType(catalog, table, isView = false) | ||
|
|
||
| // Find the origin column from schema by column name. | ||
| val originColumn = findColumnByName(table.schema, columnName, resolver) | ||
| // Throw an AnalysisException if the column name/dataType is changed. | ||
| if (!columnEqual(originColumn, newColumn, resolver)) { | ||
| throw new AnalysisException( | ||
| "ALTER TABLE CHANGE COLUMN is not supported for changing column " + | ||
| s"'${originColumn.name}' with type '${originColumn.dataType}' to " + | ||
| s"'${newColumn.name}' with type '${newColumn.dataType}'") | ||
| } | ||
|
|
||
| val newSchema = table.schema.fields.map { field => | ||
| if (field.name == originColumn.name) { | ||
| // Create a new column from the origin column with the new comment. | ||
| addComment(field, newColumn.getComment) | ||
| } else { | ||
| field | ||
| } | ||
| } | ||
| val newTable = table.copy(schema = StructType(newSchema)) | ||
| catalog.alterTable(newTable) | ||
|
|
||
| Seq.empty[Row] | ||
| } | ||
|
|
||
| // Find the origin column from schema by column name, throw an AnalysisException if the column | ||
| // reference is invalid. | ||
| private def findColumnByName( | ||
| schema: StructType, name: String, resolver: Resolver): StructField = { | ||
| schema.fields.collectFirst { | ||
| case field if resolver(field.name, name) => field | ||
| }.getOrElse(throw new AnalysisException( | ||
| s"Invalid column reference '$name', table schema is '${schema}'")) | ||
| } | ||
|
|
||
| // Add the comment to a column, if comment is empty, return the original column. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if the comment is empty, shall we remove the existing comment? what's the behavior of hive?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hive won't change the original comment if the COMMENT statement is empty. We keep the same with this behavior.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as we only check name and data type, how about
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To drop a comment, I think users can do something like ALTER TABLE test_change CHANGE a A INT COMMENT '' |
||
| private def addComment(column: StructField, comment: Option[String]): StructField = { | ||
| comment.map(column.withComment(_)).getOrElse(column) | ||
| } | ||
|
|
||
| // Compare a [[StructField]] to another, return true if they have the same column | ||
| // name(by resolver) and dataType. | ||
| private def columnEqual( | ||
| field: StructField, other: StructField, resolver: Resolver): Boolean = { | ||
| resolver(field.name, other.name) && field.dataType == other.dataType | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * A command that sets the serde class and/or serde properties of a table/view. | ||
| * | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| -- Create the origin table | ||
| CREATE TABLE test_change(a INT, b STRING, c INT); | ||
| DESC test_change; | ||
|
|
||
| -- Change column name (not supported yet) | ||
| ALTER TABLE test_change CHANGE a a1 INT; | ||
| DESC test_change; | ||
|
|
||
| -- Change column dataType (not supported yet) | ||
| ALTER TABLE test_change CHANGE a a STRING; | ||
| DESC test_change; | ||
|
|
||
| -- Change column position (not supported yet) | ||
| ALTER TABLE test_change CHANGE a a INT AFTER b; | ||
| ALTER TABLE test_change CHANGE b b STRING FIRST; | ||
| DESC test_change; | ||
|
|
||
| -- Change column comment | ||
| ALTER TABLE test_change CHANGE a a INT COMMENT 'this is column a'; | ||
| ALTER TABLE test_change CHANGE b b STRING COMMENT '#*02?`'; | ||
| ALTER TABLE test_change CHANGE c c INT COMMENT ''; | ||
| DESC test_change; | ||
|
|
||
| -- Don't change anything. | ||
| ALTER TABLE test_change CHANGE a a INT COMMENT 'this is column a'; | ||
| DESC test_change; | ||
|
|
||
| -- Change a invalid column | ||
| ALTER TABLE test_change CHANGE invalid_col invalid_col INT; | ||
| DESC test_change; | ||
|
|
||
| -- Change column name/dataType/position/comment together (not supported yet) | ||
| ALTER TABLE test_change CHANGE a a1 STRING COMMENT 'this is column a1' AFTER b; | ||
| DESC test_change; | ||
|
|
||
| -- Check the behavior with different values of CASE_SENSITIVE | ||
| SET spark.sql.caseSensitive=false; | ||
| ALTER TABLE test_change CHANGE a A INT COMMENT 'this is column A'; | ||
| SET spark.sql.caseSensitive=true; | ||
| ALTER TABLE test_change CHANGE a A INT COMMENT 'this is column A1'; | ||
| DESC test_change; | ||
|
|
||
| -- Change column can't apply to a temporary/global_temporary view | ||
| CREATE TEMPORARY VIEW temp_view(a, b) AS SELECT 1, "one"; | ||
| ALTER TABLE temp_view CHANGE a a INT COMMENT 'this is column a'; | ||
| CREATE GLOBAL TEMPORARY VIEW global_temp_view(a, b) AS SELECT 1, "one"; | ||
| ALTER TABLE global_temp.global_temp_view CHANGE a a INT COMMENT 'this is column a'; | ||
|
|
||
| -- Change column in partition spec (not supported yet) | ||
| CREATE TABLE partition_table(a INT, b STRING) PARTITIONED BY (c INT, d STRING); | ||
| ALTER TABLE partition_table PARTITION (c = 1) CHANGE COLUMN a new_a INT; | ||
|
|
||
| -- DROP TEST TABLE | ||
| DROP TABLE test_change; | ||
| DROP TABLE partition_table; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the right Hive syntax, but
SparkSqlParser.scalais using the different one.