diff --git a/src/backend/distributed/commands/multi_copy.c b/src/backend/distributed/commands/multi_copy.c index 573d49a32..bc632e8b7 100644 --- a/src/backend/distributed/commands/multi_copy.c +++ b/src/backend/distributed/commands/multi_copy.c @@ -346,6 +346,7 @@ static LocalCopyStatus GetLocalCopyStatus(void); static bool ShardIntervalListHasLocalPlacements(List *shardIntervalList); static void LogLocalCopyToRelationExecution(uint64 shardId); static void LogLocalCopyToFileExecution(uint64 shardId); +static void ErrorIfMergeInCopy(CopyStmt *copyStatement); /* exports for SQL callable functions */ @@ -2823,6 +2824,32 @@ CopyStatementHasFormat(CopyStmt *copyStatement, char *formatName) } +/* + * ErrorIfMergeInCopy Raises an exception if the MERGE is called in the COPY + * where Citus tables are involved, as we don't support this yet + * Relevant PG17 commit: c649fa24a + */ +static void +ErrorIfMergeInCopy(CopyStmt *copyStatement) +{ +#if PG_VERSION_NUM < 170000 + return; +#else + if (!copyStatement->relation && (IsA(copyStatement->query, MergeStmt))) + { + /* + * This path is currently not reachable because Merge in COPY can + * only work with a RETURNING clause, and a RETURNING check + * will error out sooner for Citus + */ + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("MERGE with Citus tables " + "is not yet supported in COPY"))); + } +#endif +} + + /* * ProcessCopyStmt handles Citus specific concerns for COPY like supporting * COPYing from distributed tables and preventing unsupported actions. The @@ -2860,6 +2887,8 @@ ProcessCopyStmt(CopyStmt *copyStatement, QueryCompletion *completionTag, const */ if (copyStatement->relation != NULL) { + ErrorIfMergeInCopy(copyStatement); + bool isFrom = copyStatement->is_from; /* consider using RangeVarGetRelidExtended to check perms before locking */ diff --git a/src/backend/distributed/planner/merge_planner.c b/src/backend/distributed/planner/merge_planner.c index e3b35f53e..800047aab 100644 --- a/src/backend/distributed/planner/merge_planner.c +++ b/src/backend/distributed/planner/merge_planner.c @@ -97,6 +97,8 @@ static DistributedPlan * CreateNonPushableMergePlan(Oid targetRelationId, uint64 plannerRestrictionContext, ParamListInfo boundParams); static char * MergeCommandResultIdPrefix(uint64 planId); +static void ErrorIfMergeHasReturningList(Query *query); +static Node * GetMergeJoinCondition(Query *mergeQuery); #endif @@ -156,8 +158,51 @@ CreateMergePlan(uint64 planId, Query *originalQuery, Query *query, } +/* + * GetMergeJoinTree constructs and returns the jointree for a MERGE query. + */ +FromExpr * +GetMergeJoinTree(Query *mergeQuery) +{ + FromExpr *mergeJointree = NULL; +#if PG_VERSION_NUM >= PG_VERSION_17 + + /* + * In Postgres 17, the query tree has a specific field for the merge condition. + * For deriving the WhereClauseList from the merge condition, we construct a dummy + * jointree with an empty fromlist. This works because the fromlist of a merge query + * join tree consists of range table references only, and range table references are + * disregarded by the WhereClauseList() walker. + * Relevant PG17 commit: 0294df2f1 + */ + mergeJointree = makeFromExpr(NIL, mergeQuery->mergeJoinCondition); +#else + mergeJointree = mergeQuery->jointree; +#endif + + return mergeJointree; +} + + #if PG_VERSION_NUM >= PG_VERSION_15 + +/* + * GetMergeJoinCondition returns the quals of the ON condition + */ +static Node * +GetMergeJoinCondition(Query *mergeQuery) +{ + Node *joinCondition = NULL; +#if PG_VERSION_NUM >= PG_VERSION_17 + joinCondition = (Node *) mergeQuery->mergeJoinCondition; +#else + joinCondition = (Node *) mergeQuery->jointree->quals; +#endif + return joinCondition; +} + + /* * CreateRouterMergePlan attempts to create a pushable plan for the given MERGE * SQL statement. If the planning fails, the ->planningError is set to a description @@ -562,7 +607,7 @@ MergeQualAndTargetListFunctionsSupported(Oid resultRelationId, Query *query, List *targetList, CmdType commandType) { uint32 targetRangeTableIndex = query->resultRelation; - FromExpr *joinTree = query->jointree; + FromExpr *joinTree = GetMergeJoinTree(query); Var *distributionColumn = NULL; if (IsCitusTable(resultRelationId) && HasDistributionKey(resultRelationId)) { @@ -722,8 +767,9 @@ ErrorIfRepartitionMergeNotSupported(Oid targetRelationId, Query *mergeQuery, /* * Sub-queries and CTEs are not allowed in actions and ON clause */ - if (FindNodeMatchingCheckFunction((Node *) mergeQuery->jointree->quals, - IsNodeSubquery)) + Node *joinCondition = GetMergeJoinCondition(mergeQuery); + + if (FindNodeMatchingCheckFunction(joinCondition, IsNodeSubquery)) { ereport(ERROR, (errmsg("Sub-queries and CTEs are not allowed in ON clause for MERGE " @@ -949,9 +995,26 @@ ConvertSourceRTEIntoSubquery(Query *mergeQuery, RangeTblEntry *sourceRte, } +/* + * ErrorIfMergeHasReturningList raises an exception if the MERGE has + * a RETURNING clause, as we don't support this yet for Citus tables + * Relevant PG17 commit: c649fa24a + */ +static void +ErrorIfMergeHasReturningList(Query *query) +{ + if (query->returningList) + { + ereport(ERROR, (errmsg("MERGE with RETURNING is not yet supported " + "for Citus tables"))); + } +} + + /* * ErrorIfMergeNotSupported Checks for conditions that are not supported in either * the routable or repartition strategies. It checks for + * - MERGE with a RETURNING clause * - Supported table types and their combinations * - Check the target lists and quals of both the query and merge actions * - Supported CTEs @@ -959,6 +1022,7 @@ ConvertSourceRTEIntoSubquery(Query *mergeQuery, RangeTblEntry *sourceRte, static void ErrorIfMergeNotSupported(Query *query, Oid targetRelationId, List *rangeTableList) { + ErrorIfMergeHasReturningList(query); ErrorIfMergeHasUnsupportedTables(targetRelationId, rangeTableList); ErrorIfMergeQueryQualAndTargetListNotSupported(targetRelationId, query); ErrorIfUnsupportedCTEs(query); @@ -1207,12 +1271,15 @@ ErrorIfMergeQueryQualAndTargetListNotSupported(Oid targetRelationId, Query *orig "supported in MERGE sql with distributed tables"))); } + Node *joinCondition = GetMergeJoinCondition(originalQuery); + DeferredErrorMessage *deferredError = - MergeQualAndTargetListFunctionsSupported(targetRelationId, - originalQuery, - originalQuery->jointree->quals, - originalQuery->targetList, - originalQuery->commandType); + MergeQualAndTargetListFunctionsSupported( + targetRelationId, + originalQuery, + joinCondition, + originalQuery->targetList, + originalQuery->commandType); if (deferredError) { @@ -1286,8 +1353,7 @@ static int SourceResultPartitionColumnIndex(Query *mergeQuery, List *sourceTargetList, CitusTableCacheEntry *targetRelation) { - /* Get all the Join conditions from the ON clause */ - List *mergeJoinConditionList = WhereClauseList(mergeQuery->jointree); + List *mergeJoinConditionList = WhereClauseList(GetMergeJoinTree(mergeQuery)); Var *targetColumn = targetRelation->partitionColumn; Var *sourceRepartitionVar = NULL; bool foundTypeMismatch = false; diff --git a/src/backend/distributed/planner/multi_router_planner.c b/src/backend/distributed/planner/multi_router_planner.c index 96a946a34..298acec70 100644 --- a/src/backend/distributed/planner/multi_router_planner.c +++ b/src/backend/distributed/planner/multi_router_planner.c @@ -598,8 +598,7 @@ TargetlistAndFunctionsSupported(Oid resultRelationId, FromExpr *joinTree, Node * } if (commandType == CMD_UPDATE && targetEntryPartitionColumn && - TargetEntryChangesValue(targetEntry, partitionColumn, - joinTree)) + TargetEntryChangesValue(targetEntry, partitionColumn, joinTree)) { return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, "modifying the partition value of rows is not " diff --git a/src/include/distributed/merge_planner.h b/src/include/distributed/merge_planner.h index b6636687a..53d451ea6 100644 --- a/src/include/distributed/merge_planner.h +++ b/src/include/distributed/merge_planner.h @@ -32,6 +32,7 @@ extern void NonPushableMergeCommandExplainScan(CustomScanState *node, List *ance struct ExplainState *es); extern Var * FetchAndValidateInsertVarIfExists(Oid targetRelationId, Query *query); extern RangeTblEntry * ExtractMergeSourceRangeTableEntry(Query *query, bool joinSourceOk); +extern FromExpr * GetMergeJoinTree(Query *mergeQuery); #endif /* MERGE_PLANNER_H */ diff --git a/src/test/regress/expected/merge_unsupported.out b/src/test/regress/expected/merge_unsupported.out new file mode 100644 index 000000000..62f51a679 --- /dev/null +++ b/src/test/regress/expected/merge_unsupported.out @@ -0,0 +1,101 @@ +SHOW server_version \gset +SELECT CASE + WHEN substring(current_setting('server_version'), '\d+')::int >= 17 THEN '17+' + WHEN substring(current_setting('server_version'), '\d+')::int IN (15, 16) THEN '15_16' + WHEN substring(current_setting('server_version'), '\d+')::int = 14 THEN '14' + ELSE 'Unsupported version' + END AS version_category; + version_category +--------------------------------------------------------------------- + 17+ +(1 row) + +SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15 +\gset +\if :server_version_ge_15 +\else +\q +\endif +-- +-- MERGE test from PG community (adapted to Citus by converting all tables to Citus local) +-- +DROP SCHEMA IF EXISTS pgmerge_schema CASCADE; +NOTICE: schema "pgmerge_schema" does not exist, skipping +CREATE SCHEMA pgmerge_schema; +SET search_path TO pgmerge_schema; +SET citus.use_citus_managed_tables to true; +CREATE TABLE target (tid integer, balance integer) + WITH (autovacuum_enabled=off); +CREATE TABLE source (sid integer, delta integer) -- no index + WITH (autovacuum_enabled=off); +\set SHOW_CONTEXT errors +-- used in a CTE +WITH foo AS ( + MERGE INTO target USING source ON (true) + WHEN MATCHED THEN DELETE +) SELECT * FROM foo; +ERROR: WITH query "foo" does not have a RETURNING clause +-- used in COPY +COPY ( + MERGE INTO target USING source ON (true) + WHEN MATCHED THEN DELETE +) TO stdout; +ERROR: COPY query must have a RETURNING clause +-- used in a CTE with RETURNING +WITH foo AS ( + MERGE INTO target USING source ON (true) + WHEN MATCHED THEN DELETE RETURNING target.* +) SELECT * FROM foo; +ERROR: MERGE with RETURNING is not yet supported for Citus tables +-- used in COPY with RETURNING +COPY ( + MERGE INTO target USING source ON (true) + WHEN MATCHED THEN DELETE RETURNING target.* +) TO stdout; +ERROR: MERGE with RETURNING is not yet supported for Citus tables +-- unsupported relation types +-- view +CREATE VIEW tv AS SELECT count(tid) AS tid FROM target; +MERGE INTO tv t +USING source s +ON t.tid = s.sid +WHEN NOT MATCHED THEN + INSERT DEFAULT VALUES; +ERROR: cannot insert into view "tv" +DETAIL: Views that return aggregate functions are not automatically updatable. +HINT: To enable inserting into the view using MERGE, provide an INSTEAD OF INSERT trigger. +DROP VIEW tv; +CREATE TABLE sq_target (tid integer NOT NULL, balance integer) + WITH (autovacuum_enabled=off); +CREATE TABLE sq_source (delta integer, sid integer, balance integer DEFAULT 0) + WITH (autovacuum_enabled=off); +SELECT citus_add_local_table_to_metadata('sq_target'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_add_local_table_to_metadata('sq_source'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO sq_target(tid, balance) VALUES (1,100), (2,200), (3,300); +INSERT INTO sq_source(sid, delta) VALUES (1,10), (2,20), (4,40); +CREATE VIEW v AS SELECT * FROM sq_source WHERE sid < 2; +-- RETURNING +BEGIN; +INSERT INTO sq_source (sid, balance, delta) VALUES (-1, -1, -10); +MERGE INTO sq_target t +USING v +ON tid = sid +WHEN MATCHED AND tid > 2 THEN + UPDATE SET balance = t.balance + delta +WHEN NOT MATCHED THEN + INSERT (balance, tid) VALUES (balance + delta, sid) +WHEN MATCHED AND tid < 2 THEN + DELETE +RETURNING *; +ERROR: MERGE with RETURNING is not yet supported for Citus tables +ROLLBACK; diff --git a/src/test/regress/expected/merge_unsupported_0.out b/src/test/regress/expected/merge_unsupported_0.out new file mode 100644 index 000000000..b788c1670 --- /dev/null +++ b/src/test/regress/expected/merge_unsupported_0.out @@ -0,0 +1,100 @@ +SHOW server_version \gset +SELECT CASE + WHEN substring(current_setting('server_version'), '\d+')::int >= 17 THEN '17+' + WHEN substring(current_setting('server_version'), '\d+')::int IN (15, 16) THEN '15_16' + WHEN substring(current_setting('server_version'), '\d+')::int = 14 THEN '14' + ELSE 'Unsupported version' + END AS version_category; + version_category +--------------------------------------------------------------------- + 15_16 +(1 row) + +SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15 +\gset +\if :server_version_ge_15 +\else +\q +\endif +-- +-- MERGE test from PG community (adapted to Citus by converting all tables to Citus local) +-- +DROP SCHEMA IF EXISTS pgmerge_schema CASCADE; +NOTICE: schema "pgmerge_schema" does not exist, skipping +CREATE SCHEMA pgmerge_schema; +SET search_path TO pgmerge_schema; +SET citus.use_citus_managed_tables to true; +CREATE TABLE target (tid integer, balance integer) + WITH (autovacuum_enabled=off); +CREATE TABLE source (sid integer, delta integer) -- no index + WITH (autovacuum_enabled=off); +\set SHOW_CONTEXT errors +-- used in a CTE +WITH foo AS ( + MERGE INTO target USING source ON (true) + WHEN MATCHED THEN DELETE +) SELECT * FROM foo; +ERROR: MERGE not supported in WITH query +-- used in COPY +COPY ( + MERGE INTO target USING source ON (true) + WHEN MATCHED THEN DELETE +) TO stdout; +ERROR: MERGE not supported in COPY +-- used in a CTE with RETURNING +WITH foo AS ( + MERGE INTO target USING source ON (true) + WHEN MATCHED THEN DELETE RETURNING target.* +) SELECT * FROM foo; +ERROR: syntax error at or near "RETURNING" +-- used in COPY with RETURNING +COPY ( + MERGE INTO target USING source ON (true) + WHEN MATCHED THEN DELETE RETURNING target.* +) TO stdout; +ERROR: syntax error at or near "RETURNING" +-- unsupported relation types +-- view +CREATE VIEW tv AS SELECT count(tid) AS tid FROM target; +MERGE INTO tv t +USING source s +ON t.tid = s.sid +WHEN NOT MATCHED THEN + INSERT DEFAULT VALUES; +ERROR: cannot execute MERGE on relation "tv" +DETAIL: This operation is not supported for views. +DROP VIEW tv; +CREATE TABLE sq_target (tid integer NOT NULL, balance integer) + WITH (autovacuum_enabled=off); +CREATE TABLE sq_source (delta integer, sid integer, balance integer DEFAULT 0) + WITH (autovacuum_enabled=off); +SELECT citus_add_local_table_to_metadata('sq_target'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_add_local_table_to_metadata('sq_source'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO sq_target(tid, balance) VALUES (1,100), (2,200), (3,300); +INSERT INTO sq_source(sid, delta) VALUES (1,10), (2,20), (4,40); +CREATE VIEW v AS SELECT * FROM sq_source WHERE sid < 2; +-- RETURNING +BEGIN; +INSERT INTO sq_source (sid, balance, delta) VALUES (-1, -1, -10); +MERGE INTO sq_target t +USING v +ON tid = sid +WHEN MATCHED AND tid > 2 THEN + UPDATE SET balance = t.balance + delta +WHEN NOT MATCHED THEN + INSERT (balance, tid) VALUES (balance + delta, sid) +WHEN MATCHED AND tid < 2 THEN + DELETE +RETURNING *; +ERROR: syntax error at or near "RETURNING" +ROLLBACK; diff --git a/src/test/regress/expected/merge_unsupported_1.out b/src/test/regress/expected/merge_unsupported_1.out new file mode 100644 index 000000000..187c5d630 --- /dev/null +++ b/src/test/regress/expected/merge_unsupported_1.out @@ -0,0 +1,17 @@ +SHOW server_version \gset +SELECT CASE + WHEN substring(current_setting('server_version'), '\d+')::int >= 17 THEN '17+' + WHEN substring(current_setting('server_version'), '\d+')::int IN (15, 16) THEN '15_16' + WHEN substring(current_setting('server_version'), '\d+')::int = 14 THEN '14' + ELSE 'Unsupported version' + END AS version_category; + version_category +--------------------------------------------------------------------- + 14 +(1 row) + +SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15 +\gset +\if :server_version_ge_15 +\else +\q diff --git a/src/test/regress/expected/pg15.out b/src/test/regress/expected/pg15.out index 28e746a91..f5d4327a8 100644 --- a/src/test/regress/expected/pg15.out +++ b/src/test/regress/expected/pg15.out @@ -437,16 +437,6 @@ MERGE INTO tbl1 USING targq ON (true) WHEN MATCHED THEN DELETE; ERROR: The required join operation is missing between the target's distribution column and any expression originating from the source. The issue may arise from a non-equi-join. DETAIL: Without a equi-join condition on the target's distribution column, the source rows cannot be efficiently redistributed, and the NOT-MATCHED condition cannot be evaluated unambiguously. This can result in incorrect or unexpected results when attempting to merge tables in a distributed setting -WITH foo AS ( - MERGE INTO tbl1 USING tbl2 ON (true) - WHEN MATCHED THEN DELETE -) SELECT * FROM foo; -ERROR: MERGE not supported in WITH query -COPY ( - MERGE INTO tbl1 USING tbl2 ON (true) - WHEN MATCHED THEN DELETE -) TO stdout; -ERROR: MERGE not supported in COPY MERGE INTO tbl1 t USING tbl2 ON (true) diff --git a/src/test/regress/expected/pgmerge.out b/src/test/regress/expected/pgmerge.out index a0f5d0c86..0c2f9b741 100644 --- a/src/test/regress/expected/pgmerge.out +++ b/src/test/regress/expected/pgmerge.out @@ -162,29 +162,7 @@ ON tid = tid WHEN MATCHED THEN DO NOTHING; ERROR: name "target" specified more than once DETAIL: The name is used both as MERGE target table and data source. --- used in a CTE -WITH foo AS ( - MERGE INTO target USING source ON (true) - WHEN MATCHED THEN DELETE -) SELECT * FROM foo; -ERROR: MERGE not supported in WITH query --- used in COPY -COPY ( - MERGE INTO target USING source ON (true) - WHEN MATCHED THEN DELETE -) TO stdout; -ERROR: MERGE not supported in COPY -- unsupported relation types --- view -CREATE VIEW tv AS SELECT * FROM target; -MERGE INTO tv t -USING source s -ON t.tid = s.sid -WHEN NOT MATCHED THEN - INSERT DEFAULT VALUES; -ERROR: cannot execute MERGE on relation "tv" -DETAIL: This operation is not supported for views. -DROP VIEW tv; -- materialized view CREATE MATERIALIZED VIEW mv AS SELECT * FROM target; MERGE INTO mv t @@ -1376,21 +1354,6 @@ WHEN NOT MATCHED THEN WHEN MATCHED AND tid < 2 THEN DELETE; ROLLBACK; --- RETURNING -BEGIN; -INSERT INTO sq_source (sid, balance, delta) VALUES (-1, -1, -10); -MERGE INTO sq_target t -USING v -ON tid = sid -WHEN MATCHED AND tid > 2 THEN - UPDATE SET balance = t.balance + delta -WHEN NOT MATCHED THEN - INSERT (balance, tid) VALUES (balance + delta, sid) -WHEN MATCHED AND tid < 2 THEN - DELETE -RETURNING *; -ERROR: syntax error at or near "RETURNING" -ROLLBACK; -- EXPLAIN CREATE TABLE ex_mtarget (a int, b int) WITH (autovacuum_enabled=off); diff --git a/src/test/regress/multi_schedule b/src/test/regress/multi_schedule index 0a809d236..18edcb39b 100644 --- a/src/test/regress/multi_schedule +++ b/src/test/regress/multi_schedule @@ -120,7 +120,7 @@ test: merge pgmerge test: merge_repartition2 test: merge_repartition1 merge_schema_sharding test: merge_partition_tables -test: merge_vcore +test: merge_vcore merge_unsupported # --------- # test that no tests leaked intermediate results. This should always be last diff --git a/src/test/regress/sql/merge_unsupported.sql b/src/test/regress/sql/merge_unsupported.sql new file mode 100644 index 000000000..ef95e01ea --- /dev/null +++ b/src/test/regress/sql/merge_unsupported.sql @@ -0,0 +1,89 @@ + + +SHOW server_version \gset +SELECT CASE + WHEN substring(current_setting('server_version'), '\d+')::int >= 17 THEN '17+' + WHEN substring(current_setting('server_version'), '\d+')::int IN (15, 16) THEN '15_16' + WHEN substring(current_setting('server_version'), '\d+')::int = 14 THEN '14' + ELSE 'Unsupported version' + END AS version_category; +SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15 +\gset +\if :server_version_ge_15 +\else +\q +\endif + +-- +-- MERGE test from PG community (adapted to Citus by converting all tables to Citus local) +-- +DROP SCHEMA IF EXISTS pgmerge_schema CASCADE; +CREATE SCHEMA pgmerge_schema; +SET search_path TO pgmerge_schema; + +SET citus.use_citus_managed_tables to true; + +CREATE TABLE target (tid integer, balance integer) + WITH (autovacuum_enabled=off); +CREATE TABLE source (sid integer, delta integer) -- no index + WITH (autovacuum_enabled=off); + +\set SHOW_CONTEXT errors +-- used in a CTE +WITH foo AS ( + MERGE INTO target USING source ON (true) + WHEN MATCHED THEN DELETE +) SELECT * FROM foo; +-- used in COPY +COPY ( + MERGE INTO target USING source ON (true) + WHEN MATCHED THEN DELETE +) TO stdout; +-- used in a CTE with RETURNING +WITH foo AS ( + MERGE INTO target USING source ON (true) + WHEN MATCHED THEN DELETE RETURNING target.* +) SELECT * FROM foo; +-- used in COPY with RETURNING +COPY ( + MERGE INTO target USING source ON (true) + WHEN MATCHED THEN DELETE RETURNING target.* +) TO stdout; + +-- unsupported relation types +-- view +CREATE VIEW tv AS SELECT count(tid) AS tid FROM target; +MERGE INTO tv t +USING source s +ON t.tid = s.sid +WHEN NOT MATCHED THEN + INSERT DEFAULT VALUES; +DROP VIEW tv; + +CREATE TABLE sq_target (tid integer NOT NULL, balance integer) + WITH (autovacuum_enabled=off); +CREATE TABLE sq_source (delta integer, sid integer, balance integer DEFAULT 0) + WITH (autovacuum_enabled=off); + +SELECT citus_add_local_table_to_metadata('sq_target'); +SELECT citus_add_local_table_to_metadata('sq_source'); + +INSERT INTO sq_target(tid, balance) VALUES (1,100), (2,200), (3,300); +INSERT INTO sq_source(sid, delta) VALUES (1,10), (2,20), (4,40); + +CREATE VIEW v AS SELECT * FROM sq_source WHERE sid < 2; + +-- RETURNING +BEGIN; +INSERT INTO sq_source (sid, balance, delta) VALUES (-1, -1, -10); +MERGE INTO sq_target t +USING v +ON tid = sid +WHEN MATCHED AND tid > 2 THEN + UPDATE SET balance = t.balance + delta +WHEN NOT MATCHED THEN + INSERT (balance, tid) VALUES (balance + delta, sid) +WHEN MATCHED AND tid < 2 THEN + DELETE +RETURNING *; +ROLLBACK; diff --git a/src/test/regress/sql/pg15.sql b/src/test/regress/sql/pg15.sql index 3773151fd..a2e79ba5a 100644 --- a/src/test/regress/sql/pg15.sql +++ b/src/test/regress/sql/pg15.sql @@ -287,16 +287,6 @@ WITH targq AS ( MERGE INTO tbl1 USING targq ON (true) WHEN MATCHED THEN DELETE; -WITH foo AS ( - MERGE INTO tbl1 USING tbl2 ON (true) - WHEN MATCHED THEN DELETE -) SELECT * FROM foo; - -COPY ( - MERGE INTO tbl1 USING tbl2 ON (true) - WHEN MATCHED THEN DELETE -) TO stdout; - MERGE INTO tbl1 t USING tbl2 ON (true) diff --git a/src/test/regress/sql/pgmerge.sql b/src/test/regress/sql/pgmerge.sql index e1f3c7aab..69a0210bc 100644 --- a/src/test/regress/sql/pgmerge.sql +++ b/src/test/regress/sql/pgmerge.sql @@ -116,27 +116,8 @@ MERGE INTO target USING target ON tid = tid WHEN MATCHED THEN DO NOTHING; --- used in a CTE -WITH foo AS ( - MERGE INTO target USING source ON (true) - WHEN MATCHED THEN DELETE -) SELECT * FROM foo; --- used in COPY -COPY ( - MERGE INTO target USING source ON (true) - WHEN MATCHED THEN DELETE -) TO stdout; -- unsupported relation types --- view -CREATE VIEW tv AS SELECT * FROM target; -MERGE INTO tv t -USING source s -ON t.tid = s.sid -WHEN NOT MATCHED THEN - INSERT DEFAULT VALUES; -DROP VIEW tv; - -- materialized view CREATE MATERIALIZED VIEW mv AS SELECT * FROM target; MERGE INTO mv t @@ -905,21 +886,6 @@ WHEN MATCHED AND tid < 2 THEN DELETE; ROLLBACK; --- RETURNING -BEGIN; -INSERT INTO sq_source (sid, balance, delta) VALUES (-1, -1, -10); -MERGE INTO sq_target t -USING v -ON tid = sid -WHEN MATCHED AND tid > 2 THEN - UPDATE SET balance = t.balance + delta -WHEN NOT MATCHED THEN - INSERT (balance, tid) VALUES (balance + delta, sid) -WHEN MATCHED AND tid < 2 THEN - DELETE -RETURNING *; -ROLLBACK; - -- EXPLAIN CREATE TABLE ex_mtarget (a int, b int) WITH (autovacuum_enabled=off);