diff --git a/itests/src/test/resources/testconfiguration.properties b/itests/src/test/resources/testconfiguration.properties index 93e2a445f4..94d2b6ea60 100644 --- a/itests/src/test/resources/testconfiguration.properties +++ b/itests/src/test/resources/testconfiguration.properties @@ -713,6 +713,7 @@ minillaplocal.query.files=\ tez_union_multiinsert.q,\ tez_vector_dynpart_hashjoin_1.q,\ tez_vector_dynpart_hashjoin_2.q,\ + truncate_external_force.q,\ uber_reduce.q,\ udaf_collect_set_2.q,\ udaf_all_keyword.q,\ diff --git a/ql/src/java/org/apache/hadoop/hive/ql/parse/DDLSemanticAnalyzer.java b/ql/src/java/org/apache/hadoop/hive/ql/parse/DDLSemanticAnalyzer.java index 2dea254c87..f966f4431c 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/parse/DDLSemanticAnalyzer.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/parse/DDLSemanticAnalyzer.java @@ -1449,8 +1449,11 @@ private void analyzeTruncateTable(ASTNode ast) throws SemanticException { String tableName = getUnescapedName((ASTNode) root.getChild(0)); Table table = getTable(tableName, true); - if (table.getTableType() != TableType.MANAGED_TABLE) { - throw new SemanticException(ErrorMsg.TRUNCATE_FOR_NON_MANAGED_TABLE.format(tableName)); + boolean isForce = ast.getFirstChildWithType(HiveParser.TOK_FORCE) != null; + if (!isForce) { + if (table.getTableType() != TableType.MANAGED_TABLE) { + throw new SemanticException(ErrorMsg.TRUNCATE_FOR_NON_MANAGED_TABLE.format(tableName)); + } } if (table.isNonNative()) { throw new SemanticException(ErrorMsg.TRUNCATE_FOR_NON_NATIVE_TABLE.format(tableName)); //TODO @@ -1487,9 +1490,10 @@ private void analyzeTruncateTable(ASTNode ast) throws SemanticException { // Is this a truncate column command List columnNames = null; - if (ast.getChildCount() == 2) { + ASTNode colNamesNode = (ASTNode) ast.getFirstChildWithType(HiveParser.TOK_TABCOLNAME); + if (colNamesNode != null) { try { - columnNames = getColumnNames((ASTNode)ast.getChild(1)); + columnNames = getColumnNames(colNamesNode); // It would be possible to support this, but this is such a pointless command. if (AcidUtils.isInsertOnlyTable(table.getParameters())) { diff --git a/ql/src/java/org/apache/hadoop/hive/ql/parse/HiveLexer.g b/ql/src/java/org/apache/hadoop/hive/ql/parse/HiveLexer.g index 43ad7dd632..ab386cdb72 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/parse/HiveLexer.g +++ b/ql/src/java/org/apache/hadoop/hive/ql/parse/HiveLexer.g @@ -334,6 +334,7 @@ KW_PRIMARY: 'PRIMARY'; KW_FOREIGN: 'FOREIGN'; KW_REFERENCES: 'REFERENCES'; KW_CONSTRAINT: 'CONSTRAINT'; +KW_FORCE: 'FORCE'; KW_ENFORCED: 'ENFORCED'; KW_VALIDATE: 'VALIDATE'; KW_NOVALIDATE: 'NOVALIDATE'; diff --git a/ql/src/java/org/apache/hadoop/hive/ql/parse/HiveParser.g b/ql/src/java/org/apache/hadoop/hive/ql/parse/HiveParser.g index 324c80487e..ee3a7e19c4 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/parse/HiveParser.g +++ b/ql/src/java/org/apache/hadoop/hive/ql/parse/HiveParser.g @@ -333,6 +333,7 @@ TOK_TABNAME; TOK_TABSRC; TOK_RESTRICT; TOK_CASCADE; +TOK_FORCE; TOK_TABLESKEWED; TOK_TABCOLVALUE; TOK_TABCOLVALUE_PAIR; @@ -983,6 +984,13 @@ ifNotExists -> ^(TOK_IFNOTEXISTS) ; +force +@init { msgs.push("force clause"); } +@after { msgs.pop(); } + : KW_FORCE + -> ^(TOK_FORCE) + ; + rewriteEnabled @init { pushMsg("rewrite enabled clause", state); } @after { popMsg(state); } @@ -1104,7 +1112,8 @@ createTableStatement truncateTableStatement @init { pushMsg("truncate table statement", state); } @after { popMsg(state); } - : KW_TRUNCATE KW_TABLE tablePartitionPrefix (KW_COLUMNS LPAREN columnNameList RPAREN)? -> ^(TOK_TRUNCATETABLE tablePartitionPrefix columnNameList?); + : KW_TRUNCATE KW_TABLE tablePartitionPrefix (KW_COLUMNS LPAREN columnNameList RPAREN)? force? + -> ^(TOK_TRUNCATETABLE tablePartitionPrefix columnNameList? force?); dropTableStatement @init { pushMsg("drop statement", state); } diff --git a/ql/src/test/queries/clientpositive/truncate_external_force.q b/ql/src/test/queries/clientpositive/truncate_external_force.q new file mode 100644 index 0000000000..834cfaad38 --- /dev/null +++ b/ql/src/test/queries/clientpositive/truncate_external_force.q @@ -0,0 +1,16 @@ +--! qt:dataset:src + +create external table external1 (key string, value string) stored as textfile; +load data local inpath '../../data/files/kv1.txt' into table external1; +select count(*) from external1; +truncate table external1 force; +select count(*) from external1; + + +-- Partitioned table +create external table external2 (key string, value string) partitioned by (p1 string) stored as textfile; +load data local inpath '../../data/files/kv1.txt' into table external2 partition (p1='abc'); +select count(*) from external2; +truncate table external2 partition (p1='abc') force; +select count(*) from external2; + diff --git a/ql/src/test/results/clientpositive/llap/truncate_external_force.q.out b/ql/src/test/results/clientpositive/llap/truncate_external_force.q.out new file mode 100644 index 0000000000..f1f04d9976 --- /dev/null +++ b/ql/src/test/results/clientpositive/llap/truncate_external_force.q.out @@ -0,0 +1,85 @@ +PREHOOK: query: create external table external1 (key string, value string) stored as textfile +PREHOOK: type: CREATETABLE +PREHOOK: Output: database:default +PREHOOK: Output: default@external1 +POSTHOOK: query: create external table external1 (key string, value string) stored as textfile +POSTHOOK: type: CREATETABLE +POSTHOOK: Output: database:default +POSTHOOK: Output: default@external1 +PREHOOK: query: load data local inpath '../../data/files/kv1.txt' into table external1 +PREHOOK: type: LOAD +#### A masked pattern was here #### +PREHOOK: Output: default@external1 +POSTHOOK: query: load data local inpath '../../data/files/kv1.txt' into table external1 +POSTHOOK: type: LOAD +#### A masked pattern was here #### +POSTHOOK: Output: default@external1 +PREHOOK: query: select count(*) from external1 +PREHOOK: type: QUERY +PREHOOK: Input: default@external1 +#### A masked pattern was here #### +POSTHOOK: query: select count(*) from external1 +POSTHOOK: type: QUERY +POSTHOOK: Input: default@external1 +#### A masked pattern was here #### +500 +PREHOOK: query: truncate table external1 force +PREHOOK: type: TRUNCATETABLE +PREHOOK: Output: default@external1 +POSTHOOK: query: truncate table external1 force +POSTHOOK: type: TRUNCATETABLE +POSTHOOK: Output: default@external1 +PREHOOK: query: select count(*) from external1 +PREHOOK: type: QUERY +PREHOOK: Input: default@external1 +#### A masked pattern was here #### +POSTHOOK: query: select count(*) from external1 +POSTHOOK: type: QUERY +POSTHOOK: Input: default@external1 +#### A masked pattern was here #### +0 +PREHOOK: query: create external table external2 (key string, value string) partitioned by (p1 string) stored as textfile +PREHOOK: type: CREATETABLE +PREHOOK: Output: database:default +PREHOOK: Output: default@external2 +POSTHOOK: query: create external table external2 (key string, value string) partitioned by (p1 string) stored as textfile +POSTHOOK: type: CREATETABLE +POSTHOOK: Output: database:default +POSTHOOK: Output: default@external2 +PREHOOK: query: load data local inpath '../../data/files/kv1.txt' into table external2 partition (p1='abc') +PREHOOK: type: LOAD +#### A masked pattern was here #### +PREHOOK: Output: default@external2 +POSTHOOK: query: load data local inpath '../../data/files/kv1.txt' into table external2 partition (p1='abc') +POSTHOOK: type: LOAD +#### A masked pattern was here #### +POSTHOOK: Output: default@external2 +POSTHOOK: Output: default@external2@p1=abc +PREHOOK: query: select count(*) from external2 +PREHOOK: type: QUERY +PREHOOK: Input: default@external2 +PREHOOK: Input: default@external2@p1=abc +#### A masked pattern was here #### +POSTHOOK: query: select count(*) from external2 +POSTHOOK: type: QUERY +POSTHOOK: Input: default@external2 +POSTHOOK: Input: default@external2@p1=abc +#### A masked pattern was here #### +500 +PREHOOK: query: truncate table external2 partition (p1='abc') force +PREHOOK: type: TRUNCATETABLE +PREHOOK: Output: default@external2@p1=abc +POSTHOOK: query: truncate table external2 partition (p1='abc') force +POSTHOOK: type: TRUNCATETABLE +POSTHOOK: Output: default@external2@p1=abc +PREHOOK: query: select count(*) from external2 +PREHOOK: type: QUERY +PREHOOK: Input: default@external2 +PREHOOK: Input: default@external2@p1=abc +#### A masked pattern was here #### +POSTHOOK: query: select count(*) from external2 +POSTHOOK: type: QUERY +POSTHOOK: Input: default@external2 +POSTHOOK: Input: default@external2@p1=abc +#### A masked pattern was here #### +0