diff --git a/src/common/backend/nodes/copyfuncs.cpp b/src/common/backend/nodes/copyfuncs.cpp index 2acc183cb..99614c2a6 100644 --- a/src/common/backend/nodes/copyfuncs.cpp +++ b/src/common/backend/nodes/copyfuncs.cpp @@ -309,6 +309,7 @@ static ModifyTable* _copyModifyTable(const ModifyTable* from) COPY_NODE_FIELD(updateTlist); COPY_NODE_FIELD(exclRelTlist); COPY_SCALAR_FIELD(exclRelRTIndex); + COPY_SCALAR_FIELD(partKeyUpsert); return newnode; } @@ -3489,7 +3490,7 @@ static UpsertExpr* _copyUpsertExpr(const UpsertExpr* from) COPY_NODE_FIELD(updateTlist); COPY_NODE_FIELD(exclRelTlist); COPY_SCALAR_FIELD(exclRelIndex); - + COPY_SCALAR_FIELD(partKeyUpsert); return newnode; } static CommonTableExpr* _copyCommonTableExpr(const CommonTableExpr* from) diff --git a/src/common/backend/nodes/equalfuncs.cpp b/src/common/backend/nodes/equalfuncs.cpp index 05a415510..7120075de 100644 --- a/src/common/backend/nodes/equalfuncs.cpp +++ b/src/common/backend/nodes/equalfuncs.cpp @@ -694,7 +694,7 @@ static bool _equalUpsertExpr(const UpsertExpr* a, const UpsertExpr* b) COMPARE_NODE_FIELD(updateTlist); COMPARE_NODE_FIELD(exclRelTlist); COMPARE_SCALAR_FIELD(exclRelIndex); - + COMPARE_SCALAR_FIELD(partKeyUpsert); return true; } /* diff --git a/src/common/backend/nodes/outfuncs.cpp b/src/common/backend/nodes/outfuncs.cpp index 1c25fd72f..145a4ceb8 100644 --- a/src/common/backend/nodes/outfuncs.cpp +++ b/src/common/backend/nodes/outfuncs.cpp @@ -757,12 +757,14 @@ static void _outModifyTable(StringInfo str, ModifyTable* node) WRITE_NODE_FIELD(updateTlist); WRITE_NODE_FIELD(exclRelTlist); WRITE_INT_FIELD(exclRelRTIndex); + WRITE_BOOL_FIELD(partKeyUpsert); } #else WRITE_ENUM_FIELD(upsertAction, UpsertAction); WRITE_NODE_FIELD(updateTlist); WRITE_NODE_FIELD(exclRelTlist); WRITE_INT_FIELD(exclRelRTIndex); + WRITE_BOOL_FIELD(partKeyUpsert); #endif } @@ -782,6 +784,7 @@ static void _outUpsertExpr(StringInfo str, const UpsertExpr* node) WRITE_NODE_FIELD(updateTlist); WRITE_NODE_FIELD(exclRelTlist); WRITE_INT_FIELD(exclRelIndex); + WRITE_BOOL_FIELD(partKeyUpsert); } static void _outMergeWhenClause(StringInfo str, const MergeWhenClause* node) { diff --git a/src/common/backend/nodes/readfuncs.cpp b/src/common/backend/nodes/readfuncs.cpp index 759feb1d5..abaf3873d 100644 --- a/src/common/backend/nodes/readfuncs.cpp +++ b/src/common/backend/nodes/readfuncs.cpp @@ -3682,7 +3682,7 @@ static ModifyTable* _readModifyTable(ModifyTable* local_node) IF_EXIST(exclRelRTIndex) { READ_INT_FIELD(exclRelRTIndex); } - + READ_BOOL_FIELD(partKeyUpsert); READ_DONE(); } @@ -3694,7 +3694,7 @@ static UpsertExpr* _readUpsertExpr(void) READ_NODE_FIELD(updateTlist); READ_NODE_FIELD(exclRelTlist); READ_INT_FIELD(exclRelIndex); - + READ_BOOL_FIELD(partKeyUpsert); READ_DONE(); } diff --git a/src/common/backend/parser/analyze.cpp b/src/common/backend/parser/analyze.cpp index e8dd15118..761163ce1 100644 --- a/src/common/backend/parser/analyze.cpp +++ b/src/common/backend/parser/analyze.cpp @@ -1845,6 +1845,7 @@ static UpsertExpr* transformUpsertClause(ParseState* pstate, UpsertClause* upser List* exclRelTlist = NIL; UpsertAction action = UPSERT_NOTHING; Relation targetrel = pstate->p_target_relation; + bool partKeyUpsert = false; #ifdef ENABLE_MULTIPLE_NODES if (targetrel->rd_rel->relhastriggers) { @@ -1872,7 +1873,6 @@ static UpsertExpr* transformUpsertClause(ParseState* pstate, UpsertClause* upser * adds have to be reset. markVarForSelectPriv() will add the exact * required permissions back. */ - exclRelTlist = BuildExcludedTargetlist(targetrel, exclRelIndex); exclRte->requiredPerms = 0; exclRte->selectedCols = NULL; @@ -1886,6 +1886,7 @@ static UpsertExpr* transformUpsertClause(ParseState* pstate, UpsertClause* upser updateTlist = transformTargetList(pstate, upsertClause->targetList); updateTlist = transformUpdateTargetList(pstate, updateTlist, upsertClause->targetList, relation); + /* We can't update primary or unique key in upsert, check it here */ #ifdef ENABLE_MULTIPLE_NODES if (IS_PGXC_COORDINATOR && !u_sess->attr.attr_sql.enable_upsert_to_merge) { @@ -1894,6 +1895,7 @@ static UpsertExpr* transformUpsertClause(ParseState* pstate, UpsertClause* upser #else checkUpsertTargetlist(pstate->p_target_relation, updateTlist); #endif + partKeyUpsert = RELATION_IS_PARTITIONED(targetrel) && targetListHasPartitionKey(updateTlist, targetrel->rd_id); } /* Finally, build DUPLICATE KEY UPDATE [NOTHING | ... ] expression */ @@ -1902,6 +1904,7 @@ static UpsertExpr* transformUpsertClause(ParseState* pstate, UpsertClause* upser result->exclRelIndex = exclRelIndex; result->exclRelTlist = exclRelTlist; result->upsertAction = action; + result->partKeyUpsert = partKeyUpsert; return result; } diff --git a/src/gausskernel/optimizer/plan/createplan.cpp b/src/gausskernel/optimizer/plan/createplan.cpp index 7369e697a..30b330317 100644 --- a/src/gausskernel/optimizer/plan/createplan.cpp +++ b/src/gausskernel/optimizer/plan/createplan.cpp @@ -8760,6 +8760,7 @@ ModifyTable* make_modifytable(CmdType operation, bool canSetTag, List* resultRel node->updateTlist = upsertClause->updateTlist; node->exclRelTlist = upsertClause->exclRelTlist; node->exclRelRTIndex = upsertClause->exclRelIndex; + node->partKeyUpsert = upsertClause->partKeyUpsert; } else { node->upsertAction = UPSERT_NONE; node->updateTlist = NIL; diff --git a/src/gausskernel/runtime/executor/execUtils.cpp b/src/gausskernel/runtime/executor/execUtils.cpp index 8599f604f..e1d60e801 100644 --- a/src/gausskernel/runtime/executor/execUtils.cpp +++ b/src/gausskernel/runtime/executor/execUtils.cpp @@ -1304,6 +1304,11 @@ bool ExecCheckIndexConstraints(TupleTableSlot* slot, EState* estate, ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("INSERT ON DUPLICATE KEY UPDATE does not support deferrable" " unique constraints/exclusion constraints."))); + + /* + * We consider a partitioned table with a global index as a normal table, + * because conflicts can be between multiple partitions. + */ if (isPartitioned && !RelationIsGlobalIndex(indexRelation)) { partitionedindexid = RelationGetRelid(indexRelation); if (!PointerIsValid(partitionIndexOidList)) { diff --git a/src/gausskernel/runtime/executor/nodeModifyTable.cpp b/src/gausskernel/runtime/executor/nodeModifyTable.cpp index bcda79ebd..f18b9521f 100644 --- a/src/gausskernel/runtime/executor/nodeModifyTable.cpp +++ b/src/gausskernel/runtime/executor/nodeModifyTable.cpp @@ -533,7 +533,7 @@ checktest: *returning = ExecUpdate(conflictTid, oldPartitionOid, bucketid, NULL, upsertState->us_updateproj, planSlot, &mtstate->mt_epqstate, - mtstate, canSetTag, false); + mtstate, canSetTag, ((ModifyTable*)mtstate->ps.plan)->partKeyUpsert); ReleaseBuffer(buffer); return true; } diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 7412af285..358f0658b 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -432,6 +432,7 @@ typedef struct ModifyTable { List* updateTlist; /* List of UPDATE target */ List* exclRelTlist; /* target list of the EXECLUDED pseudo relation */ Index exclRelRTIndex; /* RTI of the EXCLUDED pseudo relation */ + bool partKeyUpsert; OpMemInfo mem_info; /* Memory info for modify node */ } ModifyTable; diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 501b61adc..9d82352d6 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -1398,6 +1398,7 @@ typedef struct UpsertExpr { List* updateTlist; /* List of UPDATE TargetEntrys */ List* exclRelTlist; /* tlist of the 'EXCLUDED' pseudo relation */ int exclRelIndex; /* RT index of 'EXCLUDED' relation */ + bool partKeyUpsert; /* we allow upsert index key and partition key in B_FORMAT */ } UpsertExpr; #endif /* PRIMNODES_H */ diff --git a/src/test/regress/expected/upsert_prepare.out b/src/test/regress/expected/upsert_prepare.out index bcd9988ea..82bad2097 100644 --- a/src/test/regress/expected/upsert_prepare.out +++ b/src/test/regress/expected/upsert_prepare.out @@ -74,6 +74,8 @@ create table pkt (a int primary key, b int, c int); NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pkt_pkey" for table "pkt" create table fkt (a int primary key, b int references pkt, c int); NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "fkt_pkey" for table "fkt" +create table up_neg_11(c1 int, c2 int) partition by range(c1)(partition p1 values less than(10), partition p2 values less than(maxvalue)); +create unique index index_up_neg_11 on up_neg_11(c2); -- procedure test CREATE SCHEMA upsert_test_procedure; -- explain test diff --git a/src/test/regress/expected/upsert_restriction.out b/src/test/regress/expected/upsert_restriction.out index 5d64ea9c8..b0ef06518 100644 --- a/src/test/regress/expected/upsert_restriction.out +++ b/src/test/regress/expected/upsert_restriction.out @@ -32,6 +32,7 @@ insert into up_neg_07 select a,a,a from generate_series(1,20) as a; insert into up_neg_08 select a,a,a from generate_series(1,20) as a; insert into up_neg_09 select a,a,a from generate_series(1,20) as a; insert into up_neg_10 select a,a,a from generate_series(1,20) as a; +insert into up_neg_11 values(1,1),(2,2),(11,11),(12,12); -- table cases ---- col table insert into up_neg_01 values(1,2,3) on duplicate key update c3 = 1; @@ -120,6 +121,22 @@ insert into up_neg_09 values(101, 1, 1) on duplicate key update c2=101; ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. insert into up_neg_09 values(101, 1, 1) on duplicate key update c2 =1,c3=101; ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key. +---- cross-partition upsert +insert into up_neg_11 values(1, 1) on duplicate key update c1 = 15; +select * from up_neg_11 partition(p1); + c1 | c2 +----+---- + 2 | 2 +(1 row) + +select * from up_neg_11 partition(p2); + c1 | c2 +----+---- + 11 | 11 + 12 | 12 + 15 | 1 +(3 rows) + --update unique key mul type plans EXPLAIN (VERBOSE on, COSTS off) insert into up_neg_10 values(101, 1, 300) on duplicate key update c1=101; QUERY PLAN diff --git a/src/test/regress/sql/upsert_prepare.sql b/src/test/regress/sql/upsert_prepare.sql index 72794228a..d91eccd64 100644 --- a/src/test/regress/sql/upsert_prepare.sql +++ b/src/test/regress/sql/upsert_prepare.sql @@ -59,6 +59,8 @@ create view up_view as select *from up_neg_01; create materialized view mat_view as select *from up_neg_01; create table pkt (a int primary key, b int, c int); create table fkt (a int primary key, b int references pkt, c int); +create table up_neg_11(c1 int, c2 int) partition by range(c1)(partition p1 values less than(10), partition p2 values less than(maxvalue)); +create unique index index_up_neg_11 on up_neg_11(c2); -- procedure test diff --git a/src/test/regress/sql/upsert_restriction.sql b/src/test/regress/sql/upsert_restriction.sql index 14a362f4c..e8c45012d 100644 --- a/src/test/regress/sql/upsert_restriction.sql +++ b/src/test/regress/sql/upsert_restriction.sql @@ -33,6 +33,7 @@ insert into up_neg_07 select a,a,a from generate_series(1,20) as a; insert into up_neg_08 select a,a,a from generate_series(1,20) as a; insert into up_neg_09 select a,a,a from generate_series(1,20) as a; insert into up_neg_10 select a,a,a from generate_series(1,20) as a; +insert into up_neg_11 values(1,1),(2,2),(11,11),(12,12); -- table cases ---- col table @@ -98,6 +99,11 @@ insert into up_neg_09 values(101, 1, 1) on duplicate key update c3=101; insert into up_neg_09 values(101, 1, 1) on duplicate key update c2=101; insert into up_neg_09 values(101, 1, 1) on duplicate key update c2 =1,c3=101; +---- cross-partition upsert +insert into up_neg_11 values(1, 1) on duplicate key update c1 = 15; +select * from up_neg_11 partition(p1); +select * from up_neg_11 partition(p2); + --update unique key mul type plans EXPLAIN (VERBOSE on, COSTS off) insert into up_neg_10 values(101, 1, 300) on duplicate key update c1=101; insert into up_neg_10 values(101, 1, 300) on duplicate key update c3=101;