diff --git a/src/backend/distributed/planner/multi_router_planner.c b/src/backend/distributed/planner/multi_router_planner.c index e34985834..9bf8766fd 100644 --- a/src/backend/distributed/planner/multi_router_planner.c +++ b/src/backend/distributed/planner/multi_router_planner.c @@ -158,6 +158,10 @@ static List * ExtractInsertValuesList(Query *query, Var *partitionColumn); static DeferredErrorMessage * DeferErrorIfUnsupportedRouterPlannableSelectQuery( Query *query); static DeferredErrorMessage * ErrorIfQueryHasUnroutableModifyingCTE(Query *queryTree); +#if PG_VERSION_NUM >= PG_VERSION_14 +static DeferredErrorMessage * ErrorIfQueryHasCTEWithSearchClause(Query *queryTree); +static bool ContainsSearchClauseWalker(Node *node); +#endif static bool SelectsFromDistributedTable(List *rangeTableList, Query *query); static ShardPlacement * CreateDummyPlacement(bool hasLocalRelation); static ShardPlacement * CreateLocalDummyPlacement(); @@ -1070,6 +1074,15 @@ ModifyQuerySupported(Query *queryTree, Query *originalQuery, bool multiShardQuer } } +#if PG_VERSION_NUM >= PG_VERSION_14 + DeferredErrorMessage *CTEWithSearchClauseError = + ErrorIfQueryHasCTEWithSearchClause(originalQuery); + if (CTEWithSearchClauseError != NULL) + { + return CTEWithSearchClauseError; + } +#endif + return NULL; } @@ -3623,6 +3636,15 @@ DeferErrorIfUnsupportedRouterPlannableSelectQuery(Query *query) NULL, NULL); } +#if PG_VERSION_NUM >= PG_VERSION_14 + DeferredErrorMessage *CTEWithSearchClauseError = + ErrorIfQueryHasCTEWithSearchClause(query); + if (CTEWithSearchClauseError != NULL) + { + return CTEWithSearchClauseError; + } +#endif + return ErrorIfQueryHasUnroutableModifyingCTE(query); } @@ -3756,6 +3778,57 @@ ErrorIfQueryHasUnroutableModifyingCTE(Query *queryTree) } +#if PG_VERSION_NUM >= PG_VERSION_14 + +/* + * ErrorIfQueryHasCTEWithSearchClause checks if the query contains any common table + * expressions with search clause and errors out if it does. + */ +static DeferredErrorMessage * +ErrorIfQueryHasCTEWithSearchClause(Query *queryTree) +{ + if (ContainsSearchClauseWalker((Node *) queryTree)) + { + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "CTEs with search clauses are not supported", + NULL, NULL); + } + return NULL; +} + + +/* + * ContainsSearchClauseWalker walks over the node and finds if there are any + * CommonTableExprs with search clause + */ +static bool +ContainsSearchClauseWalker(Node *node) +{ + if (node == NULL) + { + return false; + } + + if (IsA(node, CommonTableExpr)) + { + if (((CommonTableExpr *) node)->search_clause != NULL) + { + return true; + } + } + + if (IsA(node, Query)) + { + return query_tree_walker((Query *) node, ContainsSearchClauseWalker, NULL, 0); + } + + return expression_tree_walker(node, ContainsSearchClauseWalker, NULL); +} + + +#endif + + /* * get_all_actual_clauses * diff --git a/src/test/regress/expected/pg14.out b/src/test/regress/expected/pg14.out index 7291550b7..911e78bdf 100644 --- a/src/test/regress/expected/pg14.out +++ b/src/test/regress/expected/pg14.out @@ -644,5 +644,74 @@ REINDEX TABLE dist_part_table; ERROR: REINDEX TABLE queries on distributed partitioned tables are not supported -- but we support REINDEXing partitions REINDEX TABLE dist_part_table_1; +-- test if we error with CTEs with search clauses +CREATE TABLE graph0(f INT, t INT, label TEXT); +SELECT create_distributed_table('graph0', 'f'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO graph0 VALUES (1, 2, 'arc 1 -> 2'), + (1, 3, 'arc 1 -> 3'), (2, 3, 'arc 2 -> 3'), + (1, 4, 'arc 1 -> 4'), (4, 5, 'arc 4 -> 5'); +WITH RECURSIVE search_graph(f, t, label) AS ( + SELECT * FROM graph0 g WHERE f = 1 + UNION ALL + SELECT g.* + FROM graph0 g, search_graph sg + WHERE g.f = sg.t and g.f = 1 +) SEARCH DEPTH FIRST BY f, t SET seq +SELECT * FROM search_graph ORDER BY seq; +ERROR: recursive CTEs are not supported in distributed queries +WITH RECURSIVE search_graph(f, t, label) AS ( + SELECT * FROM graph0 g WHERE f = 1 + UNION ALL + SELECT g.* + FROM graph0 g, search_graph sg + WHERE g.f = sg.t and g.f = 1 +) SEARCH DEPTH FIRST BY f, t SET seq +DELETE FROM graph0 WHERE t IN (SELECT t FROM search_graph ORDER BY seq); +ERROR: recursive CTEs are not supported in distributed queries +CREATE TABLE graph1(f INT, t INT, label TEXT); +SELECT create_reference_table('graph1'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO graph1 VALUES (1, 2, 'arc 1 -> 2'), + (1, 3, 'arc 1 -> 3'), (2, 3, 'arc 2 -> 3'), + (1, 4, 'arc 1 -> 4'), (4, 5, 'arc 4 -> 5'); +WITH RECURSIVE search_graph(f, t, label) AS ( + SELECT * FROM graph1 g WHERE f = 1 + UNION ALL + SELECT g.* + FROM graph1 g, search_graph sg + WHERE g.f = sg.t and g.f = 1 +) SEARCH DEPTH FIRST BY f, t SET seq +SELECT * FROM search_graph ORDER BY seq; +ERROR: recursive CTEs are not supported in distributed queries +WITH RECURSIVE search_graph(f, t, label) AS ( + SELECT * FROM graph1 g WHERE f = 1 + UNION ALL + SELECT g.* + FROM graph1 g, search_graph sg + WHERE g.f = sg.t and g.f = 1 +) SEARCH DEPTH FIRST BY f, t SET seq +DELETE FROM graph1 WHERE t IN (SELECT t FROM search_graph ORDER BY seq); +ERROR: recursive CTEs are not supported in distributed queries +SELECT * FROM ( + WITH RECURSIVE search_graph(f, t, label) AS ( + SELECT * + FROM graph0 g + WHERE f = 1 + UNION ALL SELECT g.* + FROM graph0 g, search_graph sg + WHERE g.f = sg.t AND g.f = 1 + ) SEARCH DEPTH FIRST BY f, t SET seq + SELECT * FROM search_graph ORDER BY seq +) as foo; +ERROR: recursive CTEs are not supported in distributed queries set client_min_messages to error; drop schema pg14 cascade; diff --git a/src/test/regress/sql/pg14.sql b/src/test/regress/sql/pg14.sql index d60b5416f..e7f9a3ca8 100644 --- a/src/test/regress/sql/pg14.sql +++ b/src/test/regress/sql/pg14.sql @@ -272,5 +272,70 @@ REINDEX TABLE dist_part_table; -- but we support REINDEXing partitions REINDEX TABLE dist_part_table_1; + +-- test if we error with CTEs with search clauses +CREATE TABLE graph0(f INT, t INT, label TEXT); +SELECT create_distributed_table('graph0', 'f'); + +INSERT INTO graph0 VALUES (1, 2, 'arc 1 -> 2'), + (1, 3, 'arc 1 -> 3'), (2, 3, 'arc 2 -> 3'), + (1, 4, 'arc 1 -> 4'), (4, 5, 'arc 4 -> 5'); + +WITH RECURSIVE search_graph(f, t, label) AS ( + SELECT * FROM graph0 g WHERE f = 1 + UNION ALL + SELECT g.* + FROM graph0 g, search_graph sg + WHERE g.f = sg.t and g.f = 1 +) SEARCH DEPTH FIRST BY f, t SET seq +SELECT * FROM search_graph ORDER BY seq; + +WITH RECURSIVE search_graph(f, t, label) AS ( + SELECT * FROM graph0 g WHERE f = 1 + UNION ALL + SELECT g.* + FROM graph0 g, search_graph sg + WHERE g.f = sg.t and g.f = 1 +) SEARCH DEPTH FIRST BY f, t SET seq +DELETE FROM graph0 WHERE t IN (SELECT t FROM search_graph ORDER BY seq); + +CREATE TABLE graph1(f INT, t INT, label TEXT); +SELECT create_reference_table('graph1'); + +INSERT INTO graph1 VALUES (1, 2, 'arc 1 -> 2'), + (1, 3, 'arc 1 -> 3'), (2, 3, 'arc 2 -> 3'), + (1, 4, 'arc 1 -> 4'), (4, 5, 'arc 4 -> 5'); + +WITH RECURSIVE search_graph(f, t, label) AS ( + SELECT * FROM graph1 g WHERE f = 1 + UNION ALL + SELECT g.* + FROM graph1 g, search_graph sg + WHERE g.f = sg.t and g.f = 1 +) SEARCH DEPTH FIRST BY f, t SET seq +SELECT * FROM search_graph ORDER BY seq; + +WITH RECURSIVE search_graph(f, t, label) AS ( + SELECT * FROM graph1 g WHERE f = 1 + UNION ALL + SELECT g.* + FROM graph1 g, search_graph sg + WHERE g.f = sg.t and g.f = 1 +) SEARCH DEPTH FIRST BY f, t SET seq +DELETE FROM graph1 WHERE t IN (SELECT t FROM search_graph ORDER BY seq); + + +SELECT * FROM ( + WITH RECURSIVE search_graph(f, t, label) AS ( + SELECT * + FROM graph0 g + WHERE f = 1 + UNION ALL SELECT g.* + FROM graph0 g, search_graph sg + WHERE g.f = sg.t AND g.f = 1 + ) SEARCH DEPTH FIRST BY f, t SET seq + SELECT * FROM search_graph ORDER BY seq +) as foo; + set client_min_messages to error; drop schema pg14 cascade;