diff --git ql/src/java/org/apache/hadoop/hive/ql/ErrorMsg.java ql/src/java/org/apache/hadoop/hive/ql/ErrorMsg.java index cbc471e..f7c2812 100644 --- ql/src/java/org/apache/hadoop/hive/ql/ErrorMsg.java +++ ql/src/java/org/apache/hadoop/hive/ql/ErrorMsg.java @@ -295,6 +295,13 @@ public enum ErrorMsg { "Please use the following syntax if not sure " + "whether the property existed or not:\n" + "ALTER TABLE tableName UNSET TBLPROPERTIES IF EXISTS (key1, key2, ...)\n"), + ALTER_VIEW_AS_SELECT_NOT_EXIST(10216, + "Cannot ALTER VIEW AS SELECT if view currently does not exist\n"), + REPLACE_VIEW_WITH_PARTITION(10217, + "Cannot replace a view with CREATE VIEW or REPLACE VIEW or " + + "ALTER VIEW AS SELECT if the view has paritions\n"), + EXISTING_TABLE_IS_NOT_VIEW(10218, + "Existing table is not a view\n"), SCRIPT_INIT_ERROR(20000, "Unable to initialize custom script."), SCRIPT_IO_ERROR(20001, "An error occurred while reading or writing to your custom script. " diff --git ql/src/java/org/apache/hadoop/hive/ql/exec/DDLTask.java ql/src/java/org/apache/hadoop/hive/ql/exec/DDLTask.java index 93febc4..6015b5b 100644 --- ql/src/java/org/apache/hadoop/hive/ql/exec/DDLTask.java +++ ql/src/java/org/apache/hadoop/hive/ql/exec/DDLTask.java @@ -3837,21 +3837,6 @@ public class DDLTask extends Task implements Serializable { Table oldview = db.getTable(crtView.getViewName(), false); if (crtView.getOrReplace() && oldview != null) { // replace existing view - if (!oldview.getTableType().equals(TableType.VIRTUAL_VIEW)) { - throw new HiveException("Existing table is not a view"); - } - - if (crtView.getPartCols() == null - || crtView.getPartCols().isEmpty() - || !crtView.getPartCols().equals(oldview.getPartCols())) { - // if we are changing partition columns, check that partitions don't exist - if (!oldview.getPartCols().isEmpty() && - !db.getPartitions(oldview).isEmpty()) { - throw new HiveException( - "Cannot add or drop partition columns with CREATE OR REPLACE VIEW if partitions currently exist"); - } - } - // remove the existing partition columns from the field schema oldview.setViewOriginalText(crtView.getViewOriginalText()); oldview.setViewExpandedText(crtView.getViewExpandedText()); diff --git ql/src/java/org/apache/hadoop/hive/ql/parse/Hive.g ql/src/java/org/apache/hadoop/hive/ql/parse/Hive.g index ab27963..8f5ad23 100644 --- ql/src/java/org/apache/hadoop/hive/ql/parse/Hive.g +++ ql/src/java/org/apache/hadoop/hive/ql/parse/Hive.g @@ -199,6 +199,7 @@ TOK_CREATEFUNCTION; TOK_DROPFUNCTION; TOK_CREATEVIEW; TOK_DROPVIEW; +TOK_ALTERVIEW_AS; TOK_ALTERVIEW_PROPERTIES; TOK_DROPVIEW_PROPERTIES; TOK_ALTERVIEW_ADDPARTS; @@ -629,6 +630,8 @@ alterViewStatementSuffix -> ^(TOK_ALTERVIEW_ADDPARTS alterStatementSuffixAddPartitions) | alterStatementSuffixDropPartitions -> ^(TOK_ALTERVIEW_DROPPARTS alterStatementSuffixDropPartitions) + | name=tableName KW_AS selectStatement + -> ^(TOK_ALTERVIEW_AS $name selectStatement) ; alterIndexStatementSuffix diff --git ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java index f65ee96..f852ad5 100644 --- ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java +++ ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java @@ -8211,7 +8211,8 @@ public class SemanticAnalyzer extends BaseSemanticAnalyzer { } // analyze create view command - if (ast.getToken().getType() == HiveParser.TOK_CREATEVIEW) { + if (ast.getToken().getType() == HiveParser.TOK_CREATEVIEW || + ast.getToken().getType() == HiveParser.TOK_ALTERVIEW_AS) { child = analyzeCreateView(ast, qb); SessionState.get().setCommandType(HiveOperation.CREATEVIEW); if (child == null) { @@ -8918,6 +8919,7 @@ public class SemanticAnalyzer extends BaseSemanticAnalyzer { List cols = null; boolean ifNotExists = false; boolean orReplace = false; + boolean isAlterViewAs = false; String comment = null; ASTNode selectStmt = null; Map tblProps = null; @@ -8959,8 +8961,16 @@ public class SemanticAnalyzer extends BaseSemanticAnalyzer { throw new SemanticException("Can't combine IF NOT EXISTS and OR REPLACE."); } + if (ast.getToken().getType() == HiveParser.TOK_ALTERVIEW_AS) { + isAlterViewAs = true; + orReplace = true; + } + createVwDesc = new CreateViewDesc( - tableName, cols, comment, tblProps, partColNames, ifNotExists, orReplace); + tableName, cols, comment, tblProps, partColNames, + ifNotExists, orReplace, isAlterViewAs); + + validateCreateView(createVwDesc); unparseTranslator.enable(); rootTasks.add(TaskFactory.get(new DDLWork(getInputs(), getOutputs(), createVwDesc), conf)); @@ -8968,6 +8978,54 @@ public class SemanticAnalyzer extends BaseSemanticAnalyzer { return selectStmt; } + private void validateCreateView(CreateViewDesc createVwDesc) + throws SemanticException { + try { + Table oldView = db.getTable(createVwDesc.getViewName(), false); + + // ALTER VIEW AS SELECT requires the view must exist + if (createVwDesc.getIsAlterViewAs() && oldView == null) { + String viewNotExistErrorMsg = + "The following view does not exist: " + createVwDesc.getViewName(); + throw new SemanticException( + ErrorMsg.ALTER_VIEW_AS_SELECT_NOT_EXIST.getMsg(viewNotExistErrorMsg)); + } + + //replace view + if (createVwDesc.getOrReplace() && oldView != null) { + + // Existing table is not a view + if (!oldView.getTableType().equals(TableType.VIRTUAL_VIEW)) { + String tableNotViewErrorMsg = + "The following is an existing table, not a view: " + + createVwDesc.getViewName(); + throw new SemanticException( + ErrorMsg.EXISTING_TABLE_IS_NOT_VIEW.getMsg(tableNotViewErrorMsg)); + } + + // if old view has partitions, it could not be replaced + String partitionViewErrorMsg = + "The following view has partition, it could not be replaced: " + + createVwDesc.getViewName(); + try { + if ((createVwDesc.getPartCols() == null || + createVwDesc.getPartCols().isEmpty() || + !createVwDesc.getPartCols().equals(oldView.getPartCols())) && + !oldView.getPartCols().isEmpty() && + !db.getPartitions(oldView).isEmpty()) { + throw new SemanticException( + ErrorMsg.REPLACE_VIEW_WITH_PARTITION.getMsg(partitionViewErrorMsg)); + } + } catch (HiveException e) { + throw new SemanticException( + ErrorMsg.REPLACE_VIEW_WITH_PARTITION.getMsg(partitionViewErrorMsg)); + } + } + } catch (HiveException e) { + throw new SemanticException(e.getMessage()); + } + } + private void decideExecMode(List> rootTasks, Context ctx, GlobalLimitCtx globalLimitCtx) throws SemanticException { diff --git ql/src/java/org/apache/hadoop/hive/ql/plan/CreateViewDesc.java ql/src/java/org/apache/hadoop/hive/ql/plan/CreateViewDesc.java index cf398dd..30549e7 100644 --- ql/src/java/org/apache/hadoop/hive/ql/plan/CreateViewDesc.java +++ ql/src/java/org/apache/hadoop/hive/ql/plan/CreateViewDesc.java @@ -43,6 +43,7 @@ public class CreateViewDesc extends DDLDesc implements Serializable { private String comment; private boolean ifNotExists; private boolean orReplace; + private boolean isAlterViewAs; /** * For serialization only. @@ -52,7 +53,8 @@ public class CreateViewDesc extends DDLDesc implements Serializable { public CreateViewDesc(String viewName, List schema, String comment, Map tblProps, - List partColNames, boolean ifNotExists, boolean orReplace) { + List partColNames, boolean ifNotExists, + boolean orReplace, boolean isAlterViewAs) { this.viewName = viewName; this.schema = schema; this.comment = comment; @@ -60,6 +62,7 @@ public class CreateViewDesc extends DDLDesc implements Serializable { this.partColNames = partColNames; this.ifNotExists = ifNotExists; this.orReplace = orReplace; + this.isAlterViewAs = isAlterViewAs; } @Explain(displayName = "name") @@ -158,4 +161,13 @@ public class CreateViewDesc extends DDLDesc implements Serializable { public void setOrReplace(boolean orReplace) { this.orReplace = orReplace; } + + @Explain(displayName = "is alter view as select") + public boolean getIsAlterViewAs() { + return isAlterViewAs; + } + + public void setIsAlterViewAs(boolean isAlterViewAs) { + this.isAlterViewAs = isAlterViewAs; + } } diff --git ql/src/test/queries/clientnegative/alter_view_as_select_not_exist.q ql/src/test/queries/clientnegative/alter_view_as_select_not_exist.q new file mode 100644 index 0000000..30fe4d9 --- /dev/null +++ ql/src/test/queries/clientnegative/alter_view_as_select_not_exist.q @@ -0,0 +1,4 @@ +DROP VIEW testView; + +-- Cannot ALTER VIEW AS SELECT if view currently does not exist +ALTER VIEW testView AS SELECT * FROM srcpart; diff --git ql/src/test/queries/clientnegative/alter_view_as_select_with_partition.q ql/src/test/queries/clientnegative/alter_view_as_select_with_partition.q new file mode 100644 index 0000000..dca6770 --- /dev/null +++ ql/src/test/queries/clientnegative/alter_view_as_select_with_partition.q @@ -0,0 +1,12 @@ +CREATE VIEW testViewPart PARTITIONED ON (value) +AS +SELECT key, value +FROM src +WHERE key=86; + +ALTER VIEW testViewPart +ADD PARTITION (value='val_86') PARTITION (value='val_xyz'); +DESCRIBE FORMATTED testViewPart; + +-- If a view has partition, could not replace it with ALTER VIEW AS SELECT +ALTER VIEW testViewPart as SELECT * FROM srcpart; diff --git ql/src/test/queries/clientpositive/alter_view_as_select.q ql/src/test/queries/clientpositive/alter_view_as_select.q new file mode 100644 index 0000000..dcab3ca --- /dev/null +++ ql/src/test/queries/clientpositive/alter_view_as_select.q @@ -0,0 +1,13 @@ +DROP VIEW testView; +CREATE VIEW testView as SELECT * FROM srcpart; +DESCRIBE FORMATTED testView; + +ALTER VIEW testView AS SELECT value FROM src WHERE key=86; +DESCRIBE FORMATTED testView; + +ALTER VIEW testView AS +SELECT * FROM src +WHERE key > 80 AND key < 100 +ORDER BY key, value +LIMIT 10; +DESCRIBE FORMATTED testView; diff --git ql/src/test/results/clientnegative/alter_view_as_select_not_exist.q.out ql/src/test/results/clientnegative/alter_view_as_select_not_exist.q.out new file mode 100644 index 0000000..66deaac --- /dev/null +++ ql/src/test/results/clientnegative/alter_view_as_select_not_exist.q.out @@ -0,0 +1,6 @@ +PREHOOK: query: DROP VIEW testView +PREHOOK: type: DROPVIEW +POSTHOOK: query: DROP VIEW testView +POSTHOOK: type: DROPVIEW +FAILED: SemanticException [Error 10216]: Cannot ALTER VIEW AS SELECT if view currently does not exist + The following view does not exist: testView diff --git ql/src/test/results/clientnegative/alter_view_as_select_with_partition.q.out ql/src/test/results/clientnegative/alter_view_as_select_with_partition.q.out new file mode 100644 index 0000000..2b8b516 --- /dev/null +++ ql/src/test/results/clientnegative/alter_view_as_select_with_partition.q.out @@ -0,0 +1,67 @@ +PREHOOK: query: CREATE VIEW testViewPart PARTITIONED ON (value) +AS +SELECT key, value +FROM src +WHERE key=86 +PREHOOK: type: CREATEVIEW +#### A masked pattern was here #### +POSTHOOK: query: CREATE VIEW testViewPart PARTITIONED ON (value) +AS +SELECT key, value +FROM src +WHERE key=86 +POSTHOOK: type: CREATEVIEW +POSTHOOK: Output: default@testViewPart +#### A masked pattern was here #### +PREHOOK: query: ALTER VIEW testViewPart +ADD PARTITION (value='val_86') PARTITION (value='val_xyz') +PREHOOK: type: ALTERTABLE_ADDPARTS +PREHOOK: Input: default@src +PREHOOK: Input: default@testviewpart +POSTHOOK: query: ALTER VIEW testViewPart +ADD PARTITION (value='val_86') PARTITION (value='val_xyz') +POSTHOOK: type: ALTERTABLE_ADDPARTS +POSTHOOK: Input: default@src +POSTHOOK: Input: default@testviewpart +POSTHOOK: Output: default@testviewpart@value=val_86 +POSTHOOK: Output: default@testviewpart@value=val_xyz +PREHOOK: query: DESCRIBE FORMATTED testViewPart +PREHOOK: type: DESCTABLE +POSTHOOK: query: DESCRIBE FORMATTED testViewPart +POSTHOOK: type: DESCTABLE +# col_name data_type comment + +key string None + +# Partition Information +# col_name data_type comment + +value string None + +# Detailed Table Information +Database: default +#### A masked pattern was here #### +Protect Mode: None +Retention: 0 +Table Type: VIRTUAL_VIEW +Table Parameters: +#### A masked pattern was here #### + +# Storage Information +SerDe Library: null +InputFormat: org.apache.hadoop.mapred.SequenceFileInputFormat +OutputFormat: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat +Compressed: No +Num Buckets: -1 +Bucket Columns: [] +Sort Columns: [] + +# View Information +View Original Text: SELECT key, value +FROM src +WHERE key=86 +View Expanded Text: SELECT `src`.`key`, `src`.`value` +FROM `default`.`src` +WHERE `src`.`key`=86 +FAILED: SemanticException [Error 10217]: Cannot replace a view with CREATE VIEW or REPLACE VIEW or ALTER VIEW AS SELECT if the view has paritions + The following view has partition, it could not be replaced: testViewPart diff --git ql/src/test/results/clientnegative/create_or_replace_view1.q.out ql/src/test/results/clientnegative/create_or_replace_view1.q.out index e0c3e05..4456ff8 100644 --- ql/src/test/results/clientnegative/create_or_replace_view1.q.out +++ ql/src/test/results/clientnegative/create_or_replace_view1.q.out @@ -20,8 +20,5 @@ POSTHOOK: query: alter view v add partition (ds='1',hr='2') POSTHOOK: type: ALTERTABLE_ADDPARTS POSTHOOK: Input: default@v POSTHOOK: Output: default@v@ds=1/hr=2 -PREHOOK: query: create or replace view v as select * from srcpart -PREHOOK: type: CREATEVIEW -#### A masked pattern was here #### -FAILED: Error in metadata: Cannot add or drop partition columns with CREATE OR REPLACE VIEW if partitions currently exist -FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.DDLTask +FAILED: SemanticException [Error 10217]: Cannot replace a view with CREATE VIEW or REPLACE VIEW or ALTER VIEW AS SELECT if the view has paritions + The following view has partition, it could not be replaced: v diff --git ql/src/test/results/clientnegative/create_or_replace_view2.q.out ql/src/test/results/clientnegative/create_or_replace_view2.q.out index 611ee4a..f50bb21 100644 --- ql/src/test/results/clientnegative/create_or_replace_view2.q.out +++ ql/src/test/results/clientnegative/create_or_replace_view2.q.out @@ -20,8 +20,5 @@ POSTHOOK: query: alter view v add partition (ds='1',hr='2') POSTHOOK: type: ALTERTABLE_ADDPARTS POSTHOOK: Input: default@v POSTHOOK: Output: default@v@ds=1/hr=2 -PREHOOK: query: create or replace view v partitioned on (hr) as select * from srcpart -PREHOOK: type: CREATEVIEW -#### A masked pattern was here #### -FAILED: Error in metadata: Cannot add or drop partition columns with CREATE OR REPLACE VIEW if partitions currently exist -FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.DDLTask +FAILED: SemanticException [Error 10217]: Cannot replace a view with CREATE VIEW or REPLACE VIEW or ALTER VIEW AS SELECT if the view has paritions + The following view has partition, it could not be replaced: v diff --git ql/src/test/results/clientnegative/create_or_replace_view3.q.out ql/src/test/results/clientnegative/create_or_replace_view3.q.out index e468ed2..f19dcf1 100644 --- ql/src/test/results/clientnegative/create_or_replace_view3.q.out +++ ql/src/test/results/clientnegative/create_or_replace_view3.q.out @@ -1,7 +1,2 @@ -PREHOOK: query: -- Existing table is not a view - -create or replace view src as select ds, hr from srcpart -PREHOOK: type: CREATEVIEW -#### A masked pattern was here #### -FAILED: Error in metadata: Existing table is not a view -FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.DDLTask +FAILED: SemanticException [Error 10218]: Existing table is not a view + The following is an existing table, not a view: src diff --git ql/src/test/results/clientpositive/alter_view_as_select.q.out ql/src/test/results/clientpositive/alter_view_as_select.q.out new file mode 100644 index 0000000..bc00e1e --- /dev/null +++ ql/src/test/results/clientpositive/alter_view_as_select.q.out @@ -0,0 +1,130 @@ +PREHOOK: query: DROP VIEW testView +PREHOOK: type: DROPVIEW +POSTHOOK: query: DROP VIEW testView +POSTHOOK: type: DROPVIEW +PREHOOK: query: CREATE VIEW testView as SELECT * FROM srcpart +PREHOOK: type: CREATEVIEW +#### A masked pattern was here #### +POSTHOOK: query: CREATE VIEW testView as SELECT * FROM srcpart +POSTHOOK: type: CREATEVIEW +POSTHOOK: Output: default@testView +#### A masked pattern was here #### +PREHOOK: query: DESCRIBE FORMATTED testView +PREHOOK: type: DESCTABLE +POSTHOOK: query: DESCRIBE FORMATTED testView +POSTHOOK: type: DESCTABLE +# col_name data_type comment + +key string None +value string None +ds string None +hr string None + +# Detailed Table Information +Database: default +#### A masked pattern was here #### +Protect Mode: None +Retention: 0 +Table Type: VIRTUAL_VIEW +Table Parameters: +#### A masked pattern was here #### + +# Storage Information +SerDe Library: null +InputFormat: org.apache.hadoop.mapred.SequenceFileInputFormat +OutputFormat: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat +Compressed: No +Num Buckets: -1 +Bucket Columns: [] +Sort Columns: [] + +# View Information +View Original Text: SELECT * FROM srcpart +View Expanded Text: SELECT `srcpart`.`key`, `srcpart`.`value`, `srcpart`.`ds`, `srcpart`.`hr` FROM `default`.`srcpart` +PREHOOK: query: ALTER VIEW testView AS SELECT value FROM src WHERE key=86 +PREHOOK: type: CREATEVIEW +#### A masked pattern was here #### +POSTHOOK: query: ALTER VIEW testView AS SELECT value FROM src WHERE key=86 +POSTHOOK: type: CREATEVIEW +POSTHOOK: Output: default@testview +#### A masked pattern was here #### +PREHOOK: query: DESCRIBE FORMATTED testView +PREHOOK: type: DESCTABLE +POSTHOOK: query: DESCRIBE FORMATTED testView +POSTHOOK: type: DESCTABLE +# col_name data_type comment + +value string None + +# Detailed Table Information +Database: default +#### A masked pattern was here #### +Protect Mode: None +Retention: 0 +Table Type: VIRTUAL_VIEW +Table Parameters: +#### A masked pattern was here #### + +# Storage Information +SerDe Library: null +InputFormat: org.apache.hadoop.mapred.SequenceFileInputFormat +OutputFormat: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat +Compressed: No +Num Buckets: -1 +Bucket Columns: [] +Sort Columns: [] + +# View Information +View Original Text: SELECT value FROM src WHERE key=86 +View Expanded Text: SELECT `src`.`value` FROM `default`.`src` WHERE `src`.`key`=86 +PREHOOK: query: ALTER VIEW testView AS +SELECT * FROM src +WHERE key > 80 AND key < 100 +ORDER BY key, value +LIMIT 10 +PREHOOK: type: CREATEVIEW +#### A masked pattern was here #### +POSTHOOK: query: ALTER VIEW testView AS +SELECT * FROM src +WHERE key > 80 AND key < 100 +ORDER BY key, value +LIMIT 10 +POSTHOOK: type: CREATEVIEW +POSTHOOK: Output: default@testview +#### A masked pattern was here #### +PREHOOK: query: DESCRIBE FORMATTED testView +PREHOOK: type: DESCTABLE +POSTHOOK: query: DESCRIBE FORMATTED testView +POSTHOOK: type: DESCTABLE +# col_name data_type comment + +key string None +value string None + +# Detailed Table Information +Database: default +#### A masked pattern was here #### +Protect Mode: None +Retention: 0 +Table Type: VIRTUAL_VIEW +Table Parameters: +#### A masked pattern was here #### + +# Storage Information +SerDe Library: null +InputFormat: org.apache.hadoop.mapred.SequenceFileInputFormat +OutputFormat: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat +Compressed: No +Num Buckets: -1 +Bucket Columns: [] +Sort Columns: [] + +# View Information +View Original Text: SELECT * FROM src +WHERE key > 80 AND key < 100 +ORDER BY key, value +LIMIT 10 +View Expanded Text: SELECT `src`.`key`, `src`.`value` FROM `default`.`src` +WHERE `src`.`key` > 80 AND `src`.`key` < 100 +ORDER BY `src`.`key`, `src`.`value` +LIMIT 10