This is an automated email from the ASF dual-hosted git repository.
kasakrisz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hive.git
The following commit(s) were added to refs/heads/master by this push:
new 224b3ec05d9 HIVE-29570: Fix MERGE rewrite parse failure by correctly
quoting qualified column names when they are function names(like date) (#6439)
224b3ec05d9 is described below
commit 224b3ec05d98dcc3ec3d07c7d658191bc7ff4d65
Author: kokila-19 <[email protected]>
AuthorDate: Tue Apr 21 10:36:04 2026 +0530
HIVE-29570: Fix MERGE rewrite parse failure by correctly quoting qualified
column names when they are function names(like date) (#6439)
---
.../apache/hadoop/hive/ql/metadata/HiveUtils.java | 26 +-
ql/src/test/queries/clientpositive/sqlmerge.q | 12 +
.../results/clientpositive/llap/sqlmerge.q.out | 279 +++++++++++++++++++++
.../clientpositive/tez/explainanalyze_3.q.out | 4 +-
4 files changed, 316 insertions(+), 5 deletions(-)
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/metadata/HiveUtils.java
b/ql/src/java/org/apache/hadoop/hive/ql/metadata/HiveUtils.java
index a75349bf544..d9fcc597a85 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/metadata/HiveUtils.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/metadata/HiveUtils.java
@@ -31,7 +31,6 @@
import com.google.common.collect.SetMultimap;
import org.antlr.runtime.TokenRewriteStream;
import org.apache.hadoop.fs.Path;
-import org.apache.hadoop.hive.ql.exec.FunctionRegistry;
import org.apache.hadoop.hive.ql.lib.CostLessRuleDispatcher;
import org.apache.hadoop.hive.ql.lib.ExpressionWalker;
import org.apache.hadoop.hive.ql.lib.Node;
@@ -354,14 +353,35 @@ public Object process(Node nd, Stack<Node> stack,
NodeProcessorCtx procCtx, Obje
throws SemanticException {
UnparseTranslator unparseTranslator =
((QuotedIdExpressionContext)procCtx).getUnparseTranslator();
ASTNode identifier = (ASTNode) nd;
- String id = identifier.getText();
- if (FunctionRegistry.getFunctionInfo(id) != null){
+ if (isFunctionNameToken(identifier)) {
return null;
}
unparseTranslator.addIdentifierTranslation(identifier);
return null;
}
+
+ /*
+ * Quote identifiers during unparse.
+ *
+ * Only skip quoting for function names.
+ * Always quote column names, even if they match function names.
+ * For example, use `alias`.`date` instead of `alias`.date.
+ */
+ private static boolean isFunctionNameToken(ASTNode identifier) {
+ ASTNode parent = (ASTNode) identifier.getParent();
+ if (parent == null || parent.getChildCount() == 0) {
+ return false;
+ }
+
+ int parentType = parent.getType();
+ boolean isFunctionNode =
+ parentType == HiveParser.TOK_FUNCTION
+ || parentType == HiveParser.TOK_FUNCTIONDI
+ || parentType == HiveParser.TOK_FUNCTIONSTAR;
+
+ return isFunctionNode && parent.getChild(0) == identifier;
+ }
}
static class QuotedIdExpressionContext implements NodeProcessorCtx {
diff --git a/ql/src/test/queries/clientpositive/sqlmerge.q
b/ql/src/test/queries/clientpositive/sqlmerge.q
index 412099d820a..baf35cb4d23 100644
--- a/ql/src/test/queries/clientpositive/sqlmerge.q
+++ b/ql/src/test/queries/clientpositive/sqlmerge.q
@@ -22,3 +22,15 @@ explain merge into acidTbl_n0 as t using nonAcidOrcTbl_n0 s
ON t.a = s.a
WHEN MATCHED AND s.a > 8 THEN DELETE
WHEN MATCHED THEN UPDATE SET b = 7
WHEN NOT MATCHED THEN INSERT VALUES(s.a, s.b);
+
+-- MERGE rewrite must preserve quoting for qualified identifiers like s.`date`
when column name is function keyword
+drop table if exists `count`;
+drop table if exists tgt_table;
+create table `count`(a int, `date` int) clustered by (a) into 2 buckets stored
as orc
+ TBLPROPERTIES ('transactional'='true');
+create table tgt_table(a int, `date` int) clustered by (a) into 2 buckets
stored as orc
+ TBLPROPERTIES ('transactional'='true');
+
+explain merge into tgt_table using `count` ON tgt_table.a = `count`.a
+WHEN MATCHED THEN UPDATE SET `date` = `count`.`date`
+WHEN NOT MATCHED THEN INSERT VALUES(`count`.a, `count`.`date`);
diff --git a/ql/src/test/results/clientpositive/llap/sqlmerge.q.out
b/ql/src/test/results/clientpositive/llap/sqlmerge.q.out
index 807948e470f..f2727b937a5 100644
--- a/ql/src/test/results/clientpositive/llap/sqlmerge.q.out
+++ b/ql/src/test/results/clientpositive/llap/sqlmerge.q.out
@@ -819,3 +819,282 @@ STAGE PLANS:
Stats Work
Basic Stats Work:
+PREHOOK: query: drop table if exists `count`
+PREHOOK: type: DROPTABLE
+PREHOOK: Output: database:default
+POSTHOOK: query: drop table if exists `count`
+POSTHOOK: type: DROPTABLE
+POSTHOOK: Output: database:default
+PREHOOK: query: drop table if exists tgt_table
+PREHOOK: type: DROPTABLE
+PREHOOK: Output: database:default
+POSTHOOK: query: drop table if exists tgt_table
+POSTHOOK: type: DROPTABLE
+POSTHOOK: Output: database:default
+PREHOOK: query: create table `count`(a int, `date` int) clustered by (a) into
2 buckets stored as orc
+ TBLPROPERTIES ('transactional'='true')
+PREHOOK: type: CREATETABLE
+PREHOOK: Output: database:default
+PREHOOK: Output: default@count
+POSTHOOK: query: create table `count`(a int, `date` int) clustered by (a) into
2 buckets stored as orc
+ TBLPROPERTIES ('transactional'='true')
+POSTHOOK: type: CREATETABLE
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@count
+PREHOOK: query: create table tgt_table(a int, `date` int) clustered by (a)
into 2 buckets stored as orc
+ TBLPROPERTIES ('transactional'='true')
+PREHOOK: type: CREATETABLE
+PREHOOK: Output: database:default
+PREHOOK: Output: default@tgt_table
+POSTHOOK: query: create table tgt_table(a int, `date` int) clustered by (a)
into 2 buckets stored as orc
+ TBLPROPERTIES ('transactional'='true')
+POSTHOOK: type: CREATETABLE
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@tgt_table
+PREHOOK: query: explain merge into tgt_table using `count` ON tgt_table.a =
`count`.a
+WHEN MATCHED THEN UPDATE SET `date` = `count`.`date`
+WHEN NOT MATCHED THEN INSERT VALUES(`count`.a, `count`.`date`)
+PREHOOK: type: QUERY
+PREHOOK: Input: default@count
+PREHOOK: Input: default@tgt_table
+PREHOOK: Output: default@merge_tmp_table
+PREHOOK: Output: default@tgt_table
+PREHOOK: Output: default@tgt_table
+POSTHOOK: query: explain merge into tgt_table using `count` ON tgt_table.a =
`count`.a
+WHEN MATCHED THEN UPDATE SET `date` = `count`.`date`
+WHEN NOT MATCHED THEN INSERT VALUES(`count`.a, `count`.`date`)
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@count
+POSTHOOK: Input: default@tgt_table
+POSTHOOK: Output: default@merge_tmp_table
+POSTHOOK: Output: default@tgt_table
+POSTHOOK: Output: default@tgt_table
+STAGE DEPENDENCIES:
+ Stage-3 is a root stage
+ Stage-4 depends on stages: Stage-3
+ Stage-0 depends on stages: Stage-4
+ Stage-5 depends on stages: Stage-0
+ Stage-1 depends on stages: Stage-4
+ Stage-6 depends on stages: Stage-1
+ Stage-2 depends on stages: Stage-4
+ Stage-7 depends on stages: Stage-2
+
+STAGE PLANS:
+ Stage: Stage-3
+ Tez
+#### A masked pattern was here ####
+ Edges:
+ Reducer 2 <- Map 1 (SIMPLE_EDGE), Map 6 (SIMPLE_EDGE)
+ Reducer 3 <- Reducer 2 (SIMPLE_EDGE)
+ Reducer 4 <- Reducer 2 (SIMPLE_EDGE)
+ Reducer 5 <- Reducer 2 (SIMPLE_EDGE)
+#### A masked pattern was here ####
+ Vertices:
+ Map 1
+ Map Operator Tree:
+ TableScan
+ alias: count
+ Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE
Column stats: NONE
+ Select Operator
+ expressions: a (type: int), date (type: int)
+ outputColumnNames: _col0, _col1
+ Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE
Column stats: NONE
+ Reduce Output Operator
+ key expressions: _col0 (type: int)
+ null sort order: z
+ sort order: +
+ Map-reduce partition columns: _col0 (type: int)
+ Statistics: Num rows: 1 Data size: 8 Basic stats:
COMPLETE Column stats: NONE
+ value expressions: _col1 (type: int)
+ Execution mode: vectorized, llap
+ LLAP IO: may be used (ACID table)
+ Map 6
+ Map Operator Tree:
+ TableScan
+ alias: tgt_table
+ filterExpr: a is not null (type: boolean)
+ Statistics: Num rows: 1 Data size: 4 Basic stats: COMPLETE
Column stats: NONE
+ Filter Operator
+ predicate: a is not null (type: boolean)
+ Statistics: Num rows: 1 Data size: 4 Basic stats: COMPLETE
Column stats: NONE
+ Select Operator
+ expressions: ROW__ID (type:
struct<writeid:bigint,bucketid:int,rowid:bigint>), a (type: int)
+ outputColumnNames: _col0, _col1
+ Statistics: Num rows: 1 Data size: 4 Basic stats:
COMPLETE Column stats: NONE
+ Reduce Output Operator
+ key expressions: _col1 (type: int)
+ null sort order: z
+ sort order: +
+ Map-reduce partition columns: _col1 (type: int)
+ Statistics: Num rows: 1 Data size: 4 Basic stats:
COMPLETE Column stats: NONE
+ value expressions: _col0 (type:
struct<writeid:bigint,bucketid:int,rowid:bigint>)
+ Execution mode: vectorized, llap
+ LLAP IO: may be used (ACID table)
+ Reducer 2
+ Execution mode: llap
+ Reduce Operator Tree:
+ Merge Join Operator
+ condition map:
+ Left Outer Join 0 to 1
+ keys:
+ 0 _col0 (type: int)
+ 1 _col1 (type: int)
+ outputColumnNames: _col0, _col1, _col2, _col3
+ Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE
Column stats: NONE
+ Select Operator
+ expressions: _col0 (type: int), _col3 (type: int), _col1
(type: int), _col2 (type: struct<writeid:bigint,bucketid:int,rowid:bigint>)
+ outputColumnNames: _col0, _col1, _col2, _col3
+ Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE
Column stats: NONE
+ Filter Operator
+ predicate: _col1 is null (type: boolean)
+ Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE
Column stats: NONE
+ Select Operator
+ expressions: _col0 (type: int), _col2 (type: int)
+ outputColumnNames: _col0, _col1
+ Statistics: Num rows: 1 Data size: 8 Basic stats:
COMPLETE Column stats: NONE
+ Reduce Output Operator
+ key expressions: _col0 (type: int)
+ null sort order: a
+ sort order: +
+ Map-reduce partition columns: _col0 (type: int)
+ Statistics: Num rows: 1 Data size: 8 Basic stats:
COMPLETE Column stats: NONE
+ value expressions: _col1 (type: int)
+ Filter Operator
+ predicate: (_col1 = _col0) (type: boolean)
+ Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE
Column stats: NONE
+ Select Operator
+ expressions: _col3 (type:
struct<writeid:bigint,bucketid:int,rowid:bigint>), _col1 (type: int), _col2
(type: int)
+ outputColumnNames: _col0, _col1, _col2
+ Statistics: Num rows: 1 Data size: 8 Basic stats:
COMPLETE Column stats: NONE
+ Reduce Output Operator
+ key expressions: _col0 (type:
struct<writeid:bigint,bucketid:int,rowid:bigint>)
+ null sort order: z
+ sort order: +
+ Map-reduce partition columns: UDFToInteger(_col0)
(type: int)
+ Statistics: Num rows: 1 Data size: 8 Basic stats:
COMPLETE Column stats: NONE
+ value expressions: _col1 (type: int), _col2 (type: int)
+ Filter Operator
+ predicate: (_col1 = _col0) (type: boolean)
+ Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE
Column stats: NONE
+ Select Operator
+ expressions: _col3 (type:
struct<writeid:bigint,bucketid:int,rowid:bigint>)
+ outputColumnNames: _col3
+ Statistics: Num rows: 1 Data size: 8 Basic stats:
COMPLETE Column stats: NONE
+ Group By Operator
+ aggregations: count()
+ keys: _col3 (type:
struct<writeid:bigint,bucketid:int,rowid:bigint>)
+ minReductionHashAggr: 0.99
+ mode: hash
+ outputColumnNames: _col0, _col1
+ Statistics: Num rows: 1 Data size: 8 Basic stats:
COMPLETE Column stats: NONE
+ Reduce Output Operator
+ key expressions: _col0 (type:
struct<writeid:bigint,bucketid:int,rowid:bigint>)
+ null sort order: z
+ sort order: +
+ Map-reduce partition columns: _col0 (type:
struct<writeid:bigint,bucketid:int,rowid:bigint>)
+ Statistics: Num rows: 1 Data size: 8 Basic stats:
COMPLETE Column stats: NONE
+ value expressions: _col1 (type: bigint)
+ Reducer 3
+ Execution mode: vectorized, llap
+ Reduce Operator Tree:
+ Select Operator
+ expressions: KEY.reducesinkkey0 (type: int), VALUE._col0
(type: int)
+ outputColumnNames: _col0, _col1
+ Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE
Column stats: NONE
+ File Output Operator
+ compressed: false
+ Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE
Column stats: NONE
+ table:
+ input format:
org.apache.hadoop.hive.ql.io.orc.OrcInputFormat
+ output format:
org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat
+ serde: org.apache.hadoop.hive.ql.io.orc.OrcSerde
+ name: default.tgt_table
+ Write Type: INSERT
+ Reducer 4
+ Execution mode: vectorized, llap
+ Reduce Operator Tree:
+ Select Operator
+ expressions: KEY.reducesinkkey0 (type:
struct<writeid:bigint,bucketid:int,rowid:bigint>), VALUE._col0 (type: int),
VALUE._col1 (type: int)
+ outputColumnNames: _col0, _col1, _col2
+ Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE
Column stats: NONE
+ File Output Operator
+ compressed: false
+ Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE
Column stats: NONE
+ table:
+ input format:
org.apache.hadoop.hive.ql.io.orc.OrcInputFormat
+ output format:
org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat
+ serde: org.apache.hadoop.hive.ql.io.orc.OrcSerde
+ name: default.tgt_table
+ Write Type: UPDATE
+ Reducer 5
+ Execution mode: llap
+ Reduce Operator Tree:
+ Group By Operator
+ aggregations: count(VALUE._col0)
+ keys: KEY._col0 (type:
struct<writeid:bigint,bucketid:int,rowid:bigint>)
+ mode: mergepartial
+ outputColumnNames: _col0, _col1
+ Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE
Column stats: NONE
+ Filter Operator
+ predicate: (_col1 > 1L) (type: boolean)
+ Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE
Column stats: NONE
+ Select Operator
+ expressions: cardinality_violation(_col0) (type: int)
+ outputColumnNames: _col0
+ Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE
Column stats: NONE
+ File Output Operator
+ compressed: false
+ Statistics: Num rows: 1 Data size: 8 Basic stats:
COMPLETE Column stats: NONE
+ table:
+ input format:
org.apache.hadoop.mapred.TextInputFormat
+ output format:
org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
+ serde:
org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
+ name: default.merge_tmp_table
+
+ Stage: Stage-4
+ Dependency Collection
+
+ Stage: Stage-0
+ Move Operator
+ tables:
+ replace: false
+ table:
+ input format: org.apache.hadoop.hive.ql.io.orc.OrcInputFormat
+ output format: org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat
+ serde: org.apache.hadoop.hive.ql.io.orc.OrcSerde
+ name: default.tgt_table
+ Write Type: INSERT
+
+ Stage: Stage-5
+ Stats Work
+ Basic Stats Work:
+
+ Stage: Stage-1
+ Move Operator
+ tables:
+ replace: false
+ table:
+ input format: org.apache.hadoop.hive.ql.io.orc.OrcInputFormat
+ output format: org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat
+ serde: org.apache.hadoop.hive.ql.io.orc.OrcSerde
+ name: default.tgt_table
+ Write Type: UPDATE
+
+ Stage: Stage-6
+ Stats Work
+ Basic Stats Work:
+
+ Stage: Stage-2
+ Move Operator
+ tables:
+ replace: false
+ table:
+ input format: org.apache.hadoop.mapred.TextInputFormat
+ output format:
org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
+ serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
+ name: default.merge_tmp_table
+
+ Stage: Stage-7
+ Stats Work
+ Basic Stats Work:
+
diff --git a/ql/src/test/results/clientpositive/tez/explainanalyze_3.q.out
b/ql/src/test/results/clientpositive/tez/explainanalyze_3.q.out
index 980d6cc6c78..1f065c1ba74 100644
--- a/ql/src/test/results/clientpositive/tez/explainanalyze_3.q.out
+++ b/ql/src/test/results/clientpositive/tez/explainanalyze_3.q.out
@@ -375,10 +375,10 @@ Stage-0
Number of rows:1
TableScan [TS_0]
-PREHOOK: query: DROP TEMPORARY MACRO SIGMOID
+PREHOOK: query: DROP TEMPORARY MACRO `SIGMOID`
PREHOOK: type: DROPMACRO
PREHOOK: Output: database:default
-POSTHOOK: query: DROP TEMPORARY MACRO SIGMOID
+POSTHOOK: query: DROP TEMPORARY MACRO `SIGMOID`
POSTHOOK: type: DROPMACRO
POSTHOOK: Output: database:default
PREHOOK: query: explain analyze DROP TEMPORARY MACRO SIGMOID