Skip to content

Commit

Permalink
Fix PG community merge tests (pgmerge)
Browse files Browse the repository at this point in the history
pgmerge.sql:
This is a clone of the PostgreSQL community's merge.sql test, adapted
for Citus by converting tables into Citus local tables. The expectation
is that any MERGE syntax that works on PostgreSQL should work on Citus
as-is, utilizing our MERGE deparser.

Diffs, which primarily seem to stem fromtwo major features in MERGE introduced by the community:

RETURNING support for MERGE
MERGE support for updatable views

Currently, Citus code does not support these features. For now, I have
implemented changes to catch these cases and raise clean exceptions.
With these adjustments, the pgmerge tests now pass without diffs.
  • Loading branch information
tejeswarm committed Dec 5, 2024
1 parent 6be0649 commit 3cef7d3
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 8 deletions.
22 changes: 22 additions & 0 deletions src/backend/distributed/commands/multi_copy.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -2828,6 +2829,25 @@ CopyStatementHasFormat(CopyStmt *copyStatement, char *formatName)
}


/*
* ErrorIfMergeInCopy Raises an exception if the MERGE is called in the COPY.
*/
static void
ErrorIfMergeInCopy(CopyStmt *copyStatement)
{
#if PG_VERSION_NUM < PG_VERSION_15
/* MERGE is supported from PG 15 onwards */
return;
#endif

if (!copyStatement->relation && (IsA(copyStatement->query, MergeStmt)))
{
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("MERGE not supported in COPY")));
}
}


/*
* ProcessCopyStmt handles Citus specific concerns for COPY like supporting
* COPYing from distributed tables and preventing unsupported actions. The
Expand All @@ -2838,6 +2858,8 @@ Node *
ProcessCopyStmt(CopyStmt *copyStatement, QueryCompletion *completionTag, const
char *queryString)
{
ErrorIfMergeInCopy(copyStatement);

/*
* Handle special COPY "resultid" FROM STDIN WITH (format result) commands
* for sending intermediate results to workers.
Expand Down
17 changes: 17 additions & 0 deletions src/backend/distributed/planner/merge_planner.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ static DistributedPlan * CreateNonPushableMergePlan(Oid targetRelationId, uint64
plannerRestrictionContext,
ParamListInfo boundParams);
static char * MergeCommandResultIdPrefix(uint64 planId);
static void ErrorIfMergeHasReturningList(Query *query);

#endif

Expand Down Expand Up @@ -949,16 +950,32 @@ ConvertSourceRTEIntoSubquery(Query *mergeQuery, RangeTblEntry *sourceRte,
}


/*
* ErrorIfMergeHasReturningList raises an exception if the MERGE
* has a RETURNING clause.
*/
static void
ErrorIfMergeHasReturningList(Query *query)
{
if (query->returningList)
{
ereport(ERROR, (errmsg("MERGE with RETURNING is not yet supported")));
}
}


/*
* 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
*/
static void
ErrorIfMergeNotSupported(Query *query, Oid targetRelationId, List *rangeTableList)
{
ErrorIfMergeHasReturningList(query);
ErrorIfMergeHasUnsupportedTables(targetRelationId, rangeTableList);
ErrorIfMergeQueryQualAndTargetListNotSupported(targetRelationId, query);
ErrorIfUnsupportedCTEs(query);
Expand Down
25 changes: 19 additions & 6 deletions src/test/regress/expected/pgmerge.out
Original file line number Diff line number Diff line change
Expand Up @@ -167,23 +167,36 @@ WITH foo AS (
MERGE INTO target USING source ON (true)
WHEN MATCHED THEN DELETE
) SELECT * FROM foo;
ERROR: MERGE not supported in WITH query
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: 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: MERGE with RETURNING is not yet supported
-- used in COPY with RETURNING
COPY (
MERGE INTO target USING source ON (true)
WHEN MATCHED THEN DELETE RETURNING target.*
) TO stdout;
ERROR: MERGE not supported in COPY
-- unsupported relation types
-- view
CREATE VIEW tv AS SELECT * FROM target;
-- non-updatable 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.
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;
-- materialized view
CREATE MATERIALIZED VIEW mv AS SELECT * FROM target;
Expand Down Expand Up @@ -1389,7 +1402,7 @@ WHEN NOT MATCHED THEN
WHEN MATCHED AND tid < 2 THEN
DELETE
RETURNING *;
ERROR: syntax error at or near "RETURNING"
ERROR: MERGE with RETURNING is not yet supported
ROLLBACK;
-- EXPLAIN
CREATE TABLE ex_mtarget (a int, b int)
Expand Down
14 changes: 12 additions & 2 deletions src/test/regress/sql/pgmerge.sql
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,20 @@ 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 * FROM target;
-- non-updatable view
CREATE VIEW tv AS SELECT count(tid) AS tid FROM target;
MERGE INTO tv t
USING source s
ON t.tid = s.sid
Expand Down

0 comments on commit 3cef7d3

Please sign in to comment.