From 6701d5e0b6a5269c38760ee40271ee91a1a044a3 Mon Sep 17 00:00:00 2001 From: luolaquan Date: Thu, 15 Jun 2023 18:31:58 +0800 Subject: [PATCH] fix core dump for merge into clause --- src/common/backend/parser/parse_merge.cpp | 25 ++++++++++---- src/test/regress/expected/merge_1.out | 41 ++++++++++++++++++++++- src/test/regress/sql/merge_1.sql | 36 ++++++++++++++++++++ 3 files changed, 94 insertions(+), 8 deletions(-) diff --git a/src/common/backend/parser/parse_merge.cpp b/src/common/backend/parser/parse_merge.cpp index 2890029185..422f31c44e 100644 --- a/src/common/backend/parser/parse_merge.cpp +++ b/src/common/backend/parser/parse_merge.cpp @@ -129,14 +129,18 @@ static void transformMergeJoinClause(ParseState* pstate, Node* merge) ParseNamespaceItem* nitem1 = (ParseNamespaceItem*)lfirst(cell1); RangeTblEntry* entry1 = nitem1->p_rte; - foreach (cell2, pstate->p_relnamespace) { - ParseNamespaceItem* nitem2 = (ParseNamespaceItem*)lfirst(cell2); - RangeTblEntry* entry2 = nitem2->p_rte; + if (pstate->p_relnamespace == NULL) { + pstate->p_relnamespace = lappend(pstate->p_relnamespace, lfirst(cell1)); + } else { + foreach (cell2, pstate->p_relnamespace) { + ParseNamespaceItem* nitem2 = (ParseNamespaceItem*)lfirst(cell2); + RangeTblEntry* entry2 = nitem2->p_rte; - if (entry1->relid == entry2->relid) { - continue; + if (entry1->relid == entry2->relid) { + continue; + } + pstate->p_relnamespace = lappend(pstate->p_relnamespace, lfirst(cell1)); } - pstate->p_relnamespace = lappend(pstate->p_relnamespace, lfirst(cell1)); } } } @@ -1015,11 +1019,14 @@ Query* transformMergeStmt(ParseState* pstate, MergeStmt* stmt) * Note: Also, ExclusiveLock other than RowExclusiveLock is used for MERGE for now. * There are chances to use RowExclusiveLock to optimize the parallel performance * when the concurrent updates to be handled properly in the future. + * + * For LATERAL subquery, we can't add left-side RTE to pstate->p_varnamespace, which + * will be handled specifically when transform JoinExpr in transformMergeJoinClause. */ qry->resultRelation = setTargetTable(pstate, stmt->relation, false, /* do not expand inheritance */ - true, + false, targetPerms); checkUnsupportedCases(pstate, stmt); @@ -1051,8 +1058,12 @@ Query* transformMergeStmt(ParseState* pstate, MergeStmt* stmt) * * 2. rewriteTargetListMerge() requires the RTE of the underlying join in * order to add junk CTID and TABLEOID attributes. + * + * 3. After transform joinexpr, we need add left-side RTE to pstate->p_varnamespace, + * because the update or insert clause of merge may refer to columns of target table. */ transformMergeJoinClause(pstate, (Node*)joinexpr); + addRTEtoQuery(pstate, rt_fetch(qry->resultRelation, pstate->p_rtable), false, false, true); /* get var that used in ON clause, then use it to check some restriction */ join_var_list = pull_var_clause((Node*)pstate->p_joinlist, PVC_REJECT_AGGREGATES, PVC_RECURSE_PLACEHOLDERS); diff --git a/src/test/regress/expected/merge_1.out b/src/test/regress/expected/merge_1.out index 7054ab0cc4..c7598e0975 100644 --- a/src/test/regress/expected/merge_1.out +++ b/src/test/regress/expected/merge_1.out @@ -753,11 +753,50 @@ SELECT * FROM products_row ORDER BY 1; 1700 | wait interface | booksDEF | 400 (6 rows) +--error scene for values clause on merge into +drop table if exists hadoop_mrs_node_info; +NOTICE: table "hadoop_mrs_node_info" does not exist, skipping +create table hadoop_mrs_node_info(id varchar(50), ip varchar(50), role varchar(50), service varchar(50), createtime timestamp default pg_systimestamp(), +updatetime timestamp default pg_systimestamp(), clustername varchar(50), status varchar(50), clusterid varchar(50)); +insert into hadoop_mrs_node_info(id, ip, role, service, clustername, status, clusterid) values('id', 'ip', 'role', 'service', 'clustername', 'status', 'clusterid'); +merge into hadoop_mrs_node_info t1 using(values("id", "ip", "role", "service", "clustername", "status") + ) as obj ( + id, + ip, + role, + service, + clustername, + status + ) + on (t1.clustername = obj.clustername + and t1.service = obj.service + and t1.role = obj.role + and t1.ip = obj.ip + ) + when matched then + update set + status = obj.status + when not matched then + insert(id, ip, role, + service, + clustername, status + ) + values(obj.id, + obj.ip, + obj.role, + obj.service, + obj.clustername, + obj.status); +ERROR: column "id" does not exist +LINE 1: merge into hadoop_mrs_node_info t1 using(values("id", "ip", ... + ^ +HINT: There is a column named "id" in table "t1", but it cannot be referenced from this part of the query. -- clean up DROP SCHEMA mergeinto_1 CASCADE; -NOTICE: drop cascades to 5 other objects +NOTICE: drop cascades to 6 other objects DETAIL: drop cascades to table products_base drop cascades to table newproducts_base drop cascades to table products_row drop cascades to table newproducts_row drop cascades to table products_part +drop cascades to table hadoop_mrs_node_info diff --git a/src/test/regress/sql/merge_1.sql b/src/test/regress/sql/merge_1.sql index 51c8349fab..df582ce78d 100644 --- a/src/test/regress/sql/merge_1.sql +++ b/src/test/regress/sql/merge_1.sql @@ -472,5 +472,41 @@ WHEN NOT MATCHED THEN SELECT * FROM products_row ORDER BY 1; +--error scene for values clause on merge into +drop table if exists hadoop_mrs_node_info; +create table hadoop_mrs_node_info(id varchar(50), ip varchar(50), role varchar(50), service varchar(50), createtime timestamp default pg_systimestamp(), +updatetime timestamp default pg_systimestamp(), clustername varchar(50), status varchar(50), clusterid varchar(50)); + +insert into hadoop_mrs_node_info(id, ip, role, service, clustername, status, clusterid) values('id', 'ip', 'role', 'service', 'clustername', 'status', 'clusterid'); + +merge into hadoop_mrs_node_info t1 using(values("id", "ip", "role", "service", "clustername", "status") + ) as obj ( + id, + ip, + role, + service, + clustername, + status + ) + on (t1.clustername = obj.clustername + and t1.service = obj.service + and t1.role = obj.role + and t1.ip = obj.ip + ) + when matched then + update set + status = obj.status + when not matched then + insert(id, ip, role, + service, + clustername, status + ) + values(obj.id, + obj.ip, + obj.role, + obj.service, + obj.clustername, + obj.status); + -- clean up DROP SCHEMA mergeinto_1 CASCADE; -- Gitee