diff --git a/src/backend/distributed/master/master_modify_multiple_shards.c b/src/backend/distributed/master/master_modify_multiple_shards.c index 1a964208a..b65710968 100644 --- a/src/backend/distributed/master/master_modify_multiple_shards.c +++ b/src/backend/distributed/master/master_modify_multiple_shards.c @@ -139,7 +139,11 @@ master_modify_multiple_shards(PG_FUNCTION_ARGS) if (modifyQuery->commandType != CMD_UTILITY) { - ErrorIfModifyQueryNotSupported(modifyQuery); + DeferredErrorMessage *error = ModifyQuerySupported(modifyQuery); + if (error) + { + RaiseDeferredError(error, ERROR); + } } /* reject queries with a returning list */ diff --git a/src/backend/distributed/planner/multi_planner.c b/src/backend/distributed/planner/multi_planner.c index df702cd0e..28a77fe77 100644 --- a/src/backend/distributed/planner/multi_planner.c +++ b/src/backend/distributed/planner/multi_planner.c @@ -120,40 +120,94 @@ multi_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) } +/* + * IsModifyCommand returns true if the query performs modifications, false + * otherwise. + */ +bool +IsModifyCommand(Query *query) +{ + CmdType commandType = query->commandType; + + if (commandType == CMD_INSERT || commandType == CMD_UPDATE || + commandType == CMD_DELETE || query->hasModifyingCTE) + { + return true; + } + + return false; +} + + /* * CreateDistributedPlan encapsulates the logic needed to transform a particular - * query into a distributed plan. For modifications, queries immediately enter - * the physical planning stage, since they are essentially "routed" to remote - * target shards. SELECT queries go through the full logical plan/optimize/ - * physical plan process needed to produce distributed query plans. + * query into a distributed plan. */ static PlannedStmt * CreateDistributedPlan(PlannedStmt *localPlan, Query *originalQuery, Query *query, RelationRestrictionContext *restrictionContext) { - MultiPlan *physicalPlan = MultiRouterPlanCreate(originalQuery, query, - restrictionContext); - if (physicalPlan == NULL) + MultiPlan *distributedPlan = NULL; + + if (IsModifyCommand(query)) { - /* Create and optimize logical plan */ - MultiTreeRoot *logicalPlan = MultiLogicalPlanCreate(query); - MultiLogicalPlanOptimize(logicalPlan); - /* - * This check is here to make it likely that all node types used in - * Citus are dumpable. Explain can dump logical and physical plans - * using the extended outfuncs infrastructure, but it's infeasible to - * test most plans. MultiQueryContainerNode always serializes the - * physical plan, so there's no need to check that separately. + * Modifications are always routed through the same + * planner/executor. As there's currently no other way to plan these, + * error out if the query is unsupported. */ - CheckNodeIsDumpable((Node *) logicalPlan); + distributedPlan = CreateModifyPlan(originalQuery, query, restrictionContext); + Assert(distributedPlan); + if (distributedPlan->planningError) + { + RaiseDeferredError(distributedPlan->planningError, ERROR); + } + } + else + { + /* + * For select queries we, if router executor is enabled, first try to + * plan the query as a router query. If not supported, otherwise try + * the full blown plan/optimize/physical planing process needed to + * produce distributed query plans. + */ + if (EnableRouterExecution) + { + distributedPlan = CreateRouterPlan(originalQuery, query, restrictionContext); - /* Create the physical plan */ - physicalPlan = MultiPhysicalPlanCreate(logicalPlan); + /* for debugging it's useful to display why query was not router plannable */ + if (distributedPlan && distributedPlan->planningError) + { + RaiseDeferredError(distributedPlan->planningError, DEBUG1); + } + } + + /* router didn't yield a plan, try the full distributed planner */ + if (!distributedPlan || distributedPlan->planningError) + { + /* Create and optimize logical plan */ + MultiTreeRoot *logicalPlan = MultiLogicalPlanCreate(query); + MultiLogicalPlanOptimize(logicalPlan); + + /* + * This check is here to make it likely that all node types used in + * Citus are dumpable. Explain can dump logical and physical plans + * using the extended outfuncs infrastructure, but it's infeasible to + * test most plans. MultiQueryContainerNode always serializes the + * physical plan, so there's no need to check that separately. + */ + CheckNodeIsDumpable((Node *) logicalPlan); + + /* Create the physical plan */ + distributedPlan = MultiPhysicalPlanCreate(logicalPlan); + + /* distributed plan currently should always succeed or error out */ + Assert(distributedPlan && distributedPlan->planningError == NULL); + } } /* store required data into the planned statement */ - return MultiQueryContainerNode(localPlan, physicalPlan); + return MultiQueryContainerNode(localPlan, distributedPlan); } diff --git a/src/backend/distributed/planner/multi_router_planner.c b/src/backend/distributed/planner/multi_router_planner.c index 78094bd28..68254c739 100644 --- a/src/backend/distributed/planner/multi_router_planner.c +++ b/src/backend/distributed/planner/multi_router_planner.c @@ -25,6 +25,7 @@ #include "distributed/citus_nodefuncs.h" #include "distributed/deparse_shard_query.h" #include "distributed/distribution_column.h" +#include "distributed/errormessage.h" #include "distributed/master_metadata_utility.h" #include "distributed/master_protocol.h" #include "distributed/metadata_cache.h" @@ -74,7 +75,8 @@ typedef struct WalkerState bool EnableRouterExecution = true; /* planner functions forward declarations */ -static MultiPlan * CreateSingleTaskRouterPlan(Query *originalQuery, Query *query, +static MultiPlan * CreateSingleTaskRouterPlan(Query *originalQuery, + Query *query, RelationRestrictionContext * restrictionContext); static MultiPlan * CreateInsertSelectRouterPlan(Query *originalQuery, @@ -114,58 +116,64 @@ static bool MultiRouterPlannableQuery(Query *query, static RelationRestrictionContext * CopyRelationRestrictionContext( RelationRestrictionContext *oldContext); static Node * InstantiatePartitionQual(Node *node, void *context); -static void ErrorIfInsertSelectQueryNotSupported(Query *queryTree, - RangeTblEntry *insertRte, - RangeTblEntry *subqueryRte, - bool allReferenceTables); -static void ErrorIfMultiTaskRouterSelectQueryUnsupported(Query *query); -static void ErrorIfInsertPartitionColumnDoesNotMatchSelect(Query *query, - RangeTblEntry *insertRte, - RangeTblEntry *subqueryRte, - Oid * - selectPartitionColumnTableId); +static DeferredErrorMessage * InsertSelectQuerySupported(Query *queryTree, + RangeTblEntry *insertRte, + RangeTblEntry *subqueryRte, + bool allReferenceTables); +static DeferredErrorMessage * MultiTaskRouterSelectQuerySupported(Query *query); +static DeferredErrorMessage * InsertPartitionColumnMatchesSelect(Query *query, + RangeTblEntry *insertRte, + RangeTblEntry * + subqueryRte, + Oid * + selectPartitionColumnTableId); static void AddUninstantiatedEqualityQual(Query *query, Var *targetPartitionColumnVar); -static void ErrorIfQueryHasModifyingCTE(Query *queryTree); +static DeferredErrorMessage * ErrorIfQueryHasModifyingCTE(Query *queryTree); + /* - * MultiRouterPlanCreate creates a multi plan for the queries - * that includes the following: - * (i) modification queries that hit a single shard - * (ii) select queries hat can be executed on a single worker - * node and does not require any operations on the master node. - * (iii) INSERT INTO .... SELECT queries - * - * The function returns NULL if it cannot create the plan for SELECT - * queries and errors out if it cannot plan the modify queries. + * CreateRouterPlan attempts to create a router executor plan for the given + * SELECT statement. If planning fails either NULL is returned, or + * ->planningError is set to a description of the failure. */ MultiPlan * -MultiRouterPlanCreate(Query *originalQuery, Query *query, - RelationRestrictionContext *restrictionContext) +CreateRouterPlan(Query *originalQuery, Query *query, + RelationRestrictionContext *restrictionContext) { - MultiPlan *multiPlan = NULL; + Assert(EnableRouterExecution); - bool routerPlannable = MultiRouterPlannableQuery(query, restrictionContext); - if (!routerPlannable) + if (MultiRouterPlannableQuery(query, restrictionContext)) { - return NULL; + return CreateSingleTaskRouterPlan(originalQuery, query, + restrictionContext); } + /* + * TODO: Instead have MultiRouterPlannableQuery set an error describing + * why router cannot support the query. + */ + return NULL; +} + + +/* + * CreateModifyPlan attempts to create a plan the given modification + * statement. If planning fails ->planningError is set to a description of + * the failure. + */ +MultiPlan * +CreateModifyPlan(Query *originalQuery, Query *query, + RelationRestrictionContext *restrictionContext) +{ if (InsertSelectQuery(originalQuery)) { - multiPlan = CreateInsertSelectRouterPlan(originalQuery, restrictionContext); + return CreateInsertSelectRouterPlan(originalQuery, restrictionContext); } else { - multiPlan = CreateSingleTaskRouterPlan(originalQuery, query, restrictionContext); + return CreateSingleTaskRouterPlan(originalQuery, query, + restrictionContext); } - - /* plans created by router planner are always router executable */ - if (multiPlan != NULL) - { - multiPlan->routerExecutable = true; - } - - return multiPlan; } @@ -173,8 +181,8 @@ MultiRouterPlanCreate(Query *originalQuery, Query *query, * CreateSingleTaskRouterPlan creates a physical plan for given query. The created plan is * either a modify task that changes a single shard, or a router task that returns * query results from a single worker. Supported modify queries (insert/update/delete) - * are router plannable by default. If query is not router plannable then the function - * returns NULL. + * are router plannable by default. If query is not router plannable then either NULL is + * returned, or the returned plan has planningError set to a description of the problem. */ static MultiPlan * CreateSingleTaskRouterPlan(Query *originalQuery, Query *query, @@ -185,7 +193,7 @@ CreateSingleTaskRouterPlan(Query *originalQuery, Query *query, Job *job = NULL; Task *task = NULL; List *placementList = NIL; - MultiPlan *multiPlan = NULL; + MultiPlan *multiPlan = CitusMakeNode(MultiPlan); if (commandType == CMD_INSERT || commandType == CMD_UPDATE || commandType == CMD_DELETE) @@ -195,12 +203,23 @@ CreateSingleTaskRouterPlan(Query *originalQuery, Query *query, if (modifyTask) { - ErrorIfModifyQueryNotSupported(query); + /* FIXME: this should probably rather be inlined into CreateModifyPlan */ + multiPlan->planningError = ModifyQuerySupported(query); + if (multiPlan->planningError) + { + return multiPlan; + } task = RouterModifyTask(originalQuery, query); + Assert(task); } else { - ErrorIfQueryHasModifyingCTE(query); + /* FIXME: this should probably rather be inlined into CreateSelectPlan */ + multiPlan->planningError = ErrorIfQueryHasModifyingCTE(query); + if (multiPlan->planningError) + { + return multiPlan; + } task = RouterSelectTask(originalQuery, restrictionContext, &placementList); } @@ -213,10 +232,10 @@ CreateSingleTaskRouterPlan(Query *originalQuery, Query *query, job = RouterQueryJob(originalQuery, task, placementList); - multiPlan = CitusMakeNode(MultiPlan); multiPlan->workerJob = job; multiPlan->masterQuery = NULL; multiPlan->masterTableName = NULL; + multiPlan->routerExecutable = true; return multiPlan; } @@ -237,7 +256,7 @@ CreateInsertSelectRouterPlan(Query *originalQuery, uint32 taskIdIndex = 1; /* 0 is reserved for invalid taskId */ Job *workerJob = NULL; uint64 jobId = INVALID_JOB_ID; - MultiPlan *multiPlan = NULL; + MultiPlan *multiPlan = CitusMakeNode(MultiPlan); RangeTblEntry *insertRte = ExtractInsertRangeTableEntry(originalQuery); RangeTblEntry *subqueryRte = ExtractSelectRangeTableEntry(originalQuery); Oid targetRelationId = insertRte->relid; @@ -249,8 +268,13 @@ CreateInsertSelectRouterPlan(Query *originalQuery, * Error semantics for INSERT ... SELECT queries are different than regular * modify queries. Thus, handle separately. */ - ErrorIfInsertSelectQueryNotSupported(originalQuery, insertRte, subqueryRte, - allReferenceTables); + multiPlan->planningError = InsertSelectQuerySupported(originalQuery, insertRte, + subqueryRte, + allReferenceTables); + if (multiPlan->planningError) + { + return multiPlan; + } /* * Plan select query for each shard in the target table. Do so by replacing the @@ -291,10 +315,10 @@ CreateInsertSelectRouterPlan(Query *originalQuery, workerJob->requiresMasterEvaluation = RequiresMasterEvaluation(originalQuery); /* and finally the multi plan */ - multiPlan = CitusMakeNode(MultiPlan); multiPlan->workerJob = workerJob; multiPlan->masterTableName = NULL; multiPlan->masterQuery = NULL; + multiPlan->routerExecutable = true; return multiPlan; } @@ -628,18 +652,19 @@ ExtractInsertRangeTableEntry(Query *query) /* - * ErrorIfInsertSelectQueryNotSupported errors out for unsupported - * INSERT ... SELECT queries. + * InsertSelectQueryNotSupported returns NULL if the INSERT ... SELECT query + * is supported, or a description why not. */ -static void -ErrorIfInsertSelectQueryNotSupported(Query *queryTree, RangeTblEntry *insertRte, - RangeTblEntry *subqueryRte, bool allReferenceTables) +static DeferredErrorMessage * +InsertSelectQuerySupported(Query *queryTree, RangeTblEntry *insertRte, + RangeTblEntry *subqueryRte, bool allReferenceTables) { Query *subquery = NULL; Oid selectPartitionColumnTableId = InvalidOid; Oid targetRelationId = insertRte->relid; char targetPartitionMethod = PartitionMethod(targetRelationId); ListCell *rangeTableCell = NULL; + DeferredErrorMessage *error = NULL; /* we only do this check for INSERT ... SELECT queries */ AssertArg(InsertSelectQuery(queryTree)); @@ -653,8 +678,9 @@ ErrorIfInsertSelectQueryNotSupported(Query *queryTree, RangeTblEntry *insertRte, if (rangeTableEntry->rtekind == RTE_RELATION && rangeTableEntry->relkind == RELKIND_VIEW) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot insert into view over distributed table"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "cannot insert into view over distributed table", + NULL, NULL); } } @@ -662,15 +688,18 @@ ErrorIfInsertSelectQueryNotSupported(Query *queryTree, RangeTblEntry *insertRte, if (contain_volatile_functions((Node *) queryTree)) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot perform distributed planning for the given " - "modification"), - errdetail("Volatile functions are not allowed in " - "INSERT ... SELECT queries"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "volatile functions are not allowed in INSERT ... SELECT " + "queries", + NULL, NULL); } /* we don't support LIMIT, OFFSET and WINDOW functions */ - ErrorIfMultiTaskRouterSelectQueryUnsupported(subquery); + error = MultiTaskRouterSelectQuerySupported(subquery); + if (error) + { + return error; + } /* * If we're inserting into a reference table, all participating tables @@ -680,18 +709,23 @@ ErrorIfInsertSelectQueryNotSupported(Query *queryTree, RangeTblEntry *insertRte, { if (!allReferenceTables) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("If data inserted into a reference table, " - "all of the participating tables in the " - "INSERT INTO ... SELECT query should be " - "reference tables."))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "only reference tables may be queried when targeting " + "a reference table with INSERT ... SELECT", + NULL, NULL); } } else { + DeferredErrorMessage *error = NULL; + /* ensure that INSERT's partition column comes from SELECT's partition column */ - ErrorIfInsertPartitionColumnDoesNotMatchSelect(queryTree, insertRte, subqueryRte, - &selectPartitionColumnTableId); + error = InsertPartitionColumnMatchesSelect(queryTree, insertRte, subqueryRte, + &selectPartitionColumnTableId); + if (error) + { + return error; + } /* * We expect partition column values come from colocated tables. Note that we @@ -700,22 +734,23 @@ ErrorIfInsertSelectQueryNotSupported(Query *queryTree, RangeTblEntry *insertRte, */ if (!TablesColocated(insertRte->relid, selectPartitionColumnTableId)) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("INSERT target table and the source relation " - "of the SELECT partition column value " - "must be colocated"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "INSERT target table and the source relation of the SELECT partition " + "column value must be colocated", + NULL, NULL); } } + + return NULL; } /* - * ErrorUnsupportedMultiTaskSelectQuery errors out on queries that we support - * for single task router queries, but, cannot allow for multi task router - * queries. We do these checks recursively to prevent any wrong results. + * MultiTaskRouterSelectQuerySupported returns NULL if the query may be used + * as the source for an INSERT ... SELECT or returns a description why not. */ -static void -ErrorIfMultiTaskRouterSelectQueryUnsupported(Query *query) +static DeferredErrorMessage * +MultiTaskRouterSelectQuerySupported(Query *query) { List *queryList = NIL; ListCell *queryCell = NULL; @@ -730,21 +765,19 @@ ErrorIfMultiTaskRouterSelectQueryUnsupported(Query *query) /* pushing down limit per shard would yield wrong results */ if (subquery->limitCount != NULL) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot perform distributed planning for the given " - "modification"), - errdetail("LIMIT clauses are not allowed in " - "INSERT ... SELECT queries"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "LIMIT clauses are not allowed in INSERT ... SELECT " + "queries", + NULL, NULL); } /* pushing down limit offest per shard would yield wrong results */ if (subquery->limitOffset != NULL) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot perform distributed planning for the given " - "modification"), - errdetail("OFFSET clauses are not allowed in " - "INSERT ... SELECT queries"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "OFFSET clauses are not allowed in INSERT ... SELECT " + "queries", + NULL, NULL); } /* @@ -754,21 +787,19 @@ ErrorIfMultiTaskRouterSelectQueryUnsupported(Query *query) */ if (subquery->windowClause != NULL) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot perform distributed planning for the given " - "modification"), - errdetail("Window functions are not allowed in " - "INSERT ... SELECT queries"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "window functions are not allowed in INSERT ... SELECT " + "queries", + NULL, NULL); } /* see comment on AddUninstantiatedPartitionRestriction() */ if (subquery->setOperations != NULL) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot perform distributed planning for the given " - "modification"), - errdetail("Set operations are not allowed in " - "INSERT ... SELECT queries"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "set operations are not allowed in INSERT ... SELECT " + "queries", + NULL, NULL); } /* @@ -779,11 +810,10 @@ ErrorIfMultiTaskRouterSelectQueryUnsupported(Query *query) */ if (subquery->groupingSets != NULL) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot perform distributed planning for the given " - "modification"), - errdetail("Grouping sets are not allowed in " - "INSERT ... SELECT queries"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "grouping sets are not allowed in INSERT ... SELECT " + "queries", + NULL, NULL); } /* @@ -792,27 +822,29 @@ ErrorIfMultiTaskRouterSelectQueryUnsupported(Query *query) */ if (subquery->hasDistinctOn) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot perform distributed planning for the given " - "modification"), - errdetail("DISTINCT ON clauses are not allowed in " - "INSERT ... SELECT queries"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "DISTINCT ON clauses are not allowed in " + "INSERT ... SELECT queries", + NULL, NULL); } } + + return NULL; } /* - * ErrorIfInsertPartitionColumnDoesNotMatchSelect checks whether the INSERTed table's - * partition column value matches with the any of the SELECTed table's partition column. + * InsertPartitionColumnMatchesSelect returns NULL the partition column in the + * table targeted by INSERTed matches with the any of the SELECTed table's + * partition column. Returns the error description if there's no match. * - * On return without error (i.e., if partition columns match), the function also sets - * selectPartitionColumnTableId. + * On return without error (i.e., if partition columns match), the function + * also sets selectPartitionColumnTableId. */ -static void -ErrorIfInsertPartitionColumnDoesNotMatchSelect(Query *query, RangeTblEntry *insertRte, - RangeTblEntry *subqueryRte, - Oid *selectPartitionColumnTableId) +static DeferredErrorMessage * +InsertPartitionColumnMatchesSelect(Query *query, RangeTblEntry *insertRte, + RangeTblEntry *subqueryRte, + Oid *selectPartitionColumnTableId) { ListCell *targetEntryCell = NULL; uint32 rangeTableId = 1; @@ -972,14 +1004,13 @@ ErrorIfInsertPartitionColumnDoesNotMatchSelect(Query *query, RangeTblEntry *inse } } - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot plan INSERT INTO ... SELECT " - "because partition columns in the target table " - "and the subquery do not match"), - errdetail(errorDetailTemplate, exprDescription), - errhint("Ensure the target table's partition column has a " - "corresponding simple column reference to a distributed " - "table's partition column in the subquery."))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "INSERT INTO ... SELECT partition columns in the source " + "table and subquery do not match", + psprintf(errorDetailTemplate, exprDescription), + "Ensure the target table's partition column has a " + "corresponding simple column reference to a distributed " + "table's partition column in the subquery."); } /* @@ -988,27 +1019,24 @@ ErrorIfInsertPartitionColumnDoesNotMatchSelect(Query *query, RangeTblEntry *inse */ if (!IsA(targetEntry->expr, Var)) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot plan INSERT INTO ... SELECT " - "because partition columns in the target table " - "and the subquery do not match"), - errdetail( - "The data type of the target table's partition column " - "should exactly match the data type of the " - "corresponding simple column reference in the subquery."))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "INSERT INTO ... SELECT partition columns in the source " + "table and subquery do not match", + "The data type of the target table's partition column " + "should exactly match the data type of the " + "corresponding simple column reference in the subquery.", + NULL); } /* finally, check that the select target column is a partition column */ if (!IsPartitionColumn(selectTargetExpr, subquery)) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot plan INSERT INTO ... SELECT " - "because partition columns in the target table " - "and the subquery do not match"), - errdetail( - "The target table's partition column " - "should correspond to a partition column " - "in the subquery."))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "INSERT INTO ... SELECT partition columns in the source " + "table and subquery do not match", + "The target table's partition column should correspond " + "to a partition column in the subquery.", + NULL); } /* we can set the select relation id */ @@ -1019,11 +1047,15 @@ ErrorIfInsertPartitionColumnDoesNotMatchSelect(Query *query, RangeTblEntry *inse if (!targetTableHasPartitionColumn) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot plan INSERT INTO ... SELECT " - "because the query doesn't include the target table's " - "partition column"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "INSERT INTO ... SELECT partition columns in the source " + "table and subquery do not match", + "the query doesn't include the target table's " + "partition column", + NULL); } + + return NULL; } @@ -1155,11 +1187,11 @@ AddUninstantiatedEqualityQual(Query *query, Var *partitionColumn) /* - * ErrorIfModifyQueryNotSupported checks if the query contains unsupported features, - * and errors out if it does. + * ModifyQuerySupported returns NULL if the query only contains supported + * features, otherwise it returns an error description. */ -void -ErrorIfModifyQueryNotSupported(Query *queryTree) +DeferredErrorMessage * +ModifyQuerySupported(Query *queryTree) { Oid distributedTableId = ExtractFirstDistributedTableId(queryTree); uint32 rangeTableId = 1; @@ -1185,21 +1217,18 @@ ErrorIfModifyQueryNotSupported(Query *queryTree) */ if (queryTree->hasSubLinks == true) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot perform distributed planning for the given" - " modification"), - errdetail("Subqueries are not supported in distributed" - " modifications."))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "subqueries are not supported in distributed modifications", + NULL, NULL); } /* reject queries which include CommonTableExpr */ if (queryTree->cteList != NIL) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot perform distributed planning for the given" - " modification"), - errdetail("Common table expressions are not supported in" - " distributed modifications."))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "common table expressions are not supported in distributed " + "modifications", + NULL, NULL); } /* extract range table entries */ @@ -1227,11 +1256,12 @@ ErrorIfModifyQueryNotSupported(Query *queryTree) if (referenceTable && !schemaNode) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot perform distributed planning for the given" - " modification"), - errdetail("Modifications to reference tables are " - "supported only from the schema node."))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "cannot perform distributed planning for the given" + " modification", + "Modifications to reference tables are " + "supported only from the schema node.", + NULL); } queryTableCount++; @@ -1239,8 +1269,9 @@ ErrorIfModifyQueryNotSupported(Query *queryTree) /* we do not expect to see a view in modify query */ if (rangeTableEntry->relkind == RELKIND_VIEW) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot modify views over distributed tables"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "cannot modify views over distributed tables", + NULL, NULL); } } else if (rangeTableEntry->rtekind == RTE_VALUES) @@ -1277,10 +1308,11 @@ ErrorIfModifyQueryNotSupported(Query *queryTree) rangeTableEntryErrorDetail = "Unrecognized range table entry."; } - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot perform distributed planning for the given" - " modifications"), - errdetail("%s", rangeTableEntryErrorDetail))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "cannot perform distributed planning for the given " + "modifications", + rangeTableEntryErrorDetail, + NULL); } } @@ -1291,11 +1323,12 @@ ErrorIfModifyQueryNotSupported(Query *queryTree) */ if (commandType != CMD_INSERT && queryTableCount != 1) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot perform distributed planning for the given" - " modification"), - errdetail("Joins are not supported in distributed " - "modifications."))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "cannot perform distributed planning for the given" + " modification", + "Joins are not supported in distributed " + "modifications.", + NULL); } /* reject queries which involve multi-row inserts */ @@ -1308,11 +1341,12 @@ ErrorIfModifyQueryNotSupported(Query *queryTree) * with a constant, and if you're inserting multiple rows at once the function * should return a different value for each row. */ - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot perform distributed planning for the given" - " modification"), - errdetail("Multi-row INSERTs to distributed tables are not " - "supported."))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "cannot perform distributed planning for the given" + " modification", + "Multi-row INSERTs to distributed tables are not " + "supported.", + NULL); } if (commandType == CMD_INSERT || commandType == CMD_UPDATE || @@ -1347,9 +1381,10 @@ ErrorIfModifyQueryNotSupported(Query *queryTree) if (commandType == CMD_UPDATE && contain_volatile_functions((Node *) targetEntry->expr)) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("functions used in UPDATE queries on distributed " - "tables must not be VOLATILE"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "functions used in UPDATE queries on distributed " + "tables must not be VOLATILE", + NULL, NULL); } if (commandType == CMD_UPDATE && targetEntryPartitionColumn && @@ -1362,9 +1397,10 @@ ErrorIfModifyQueryNotSupported(Query *queryTree) if (commandType == CMD_INSERT && targetEntryPartitionColumn && !IsA(targetEntry->expr, Const)) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("values given for the partition column must be" - " constants or constant expressions"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "values given for the partition column must be" + " constants or constant expressions", + NULL, NULL); } if (commandType == CMD_UPDATE && @@ -1379,10 +1415,10 @@ ErrorIfModifyQueryNotSupported(Query *queryTree) { if (contain_volatile_functions(joinTree->quals)) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("functions used in the WHERE clause of " - "modification queries on distributed tables " - "must not be VOLATILE"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "functions used in the WHERE clause of modification " + "queries on distributed tables must not be VOLATILE", + NULL, NULL); } else if (MasterIrreducibleExpression(joinTree->quals, &hasVarArgument, &hasBadCoalesce)) @@ -1393,23 +1429,26 @@ ErrorIfModifyQueryNotSupported(Query *queryTree) if (hasVarArgument) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("STABLE functions used in UPDATE queries" - " cannot be called with column references"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "STABLE functions used in UPDATE queries " + "cannot be called with column references", + NULL, NULL); } if (hasBadCoalesce) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("non-IMMUTABLE functions are not allowed in CASE or" - " COALESCE statements"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "non-IMMUTABLE functions are not allowed in CASE or " + "COALESCE statements", + NULL, NULL); } if (contain_mutable_functions((Node *) queryTree->returningList)) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("non-IMMUTABLE functions are not allowed in the" - " RETURNING clause"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "non-IMMUTABLE functions are not allowed in the " + "RETURNING clause", + NULL, NULL); } } @@ -1470,10 +1509,11 @@ ErrorIfModifyQueryNotSupported(Query *queryTree) } else if (contain_mutable_functions((Node *) setTargetEntry->expr)) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("functions used in the DO UPDATE SET clause of " - "INSERTs on distributed tables must be marked " - "IMMUTABLE"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "functions used in the DO UPDATE SET clause of " + "INSERTs on distributed tables must be marked " + "IMMUTABLE", + NULL, NULL); } } } @@ -1482,17 +1522,22 @@ ErrorIfModifyQueryNotSupported(Query *queryTree) if (contain_mutable_functions((Node *) arbiterWhere) || contain_mutable_functions((Node *) onConflictWhere)) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("functions used in the WHERE clause of the ON CONFLICT " - "clause of INSERTs on distributed tables must be marked " - "IMMUTABLE"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "functions used in the WHERE clause of the " + "ON CONFLICT clause of INSERTs on distributed " + "tables must be marked IMMUTABLE", + NULL, NULL); } if (specifiesPartitionValue) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("modifying the partition value of rows is not allowed"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "modifying the partition value of rows is not " + "allowed", + NULL, NULL); } + + return NULL; } @@ -2579,7 +2624,7 @@ RouterQueryJob(Query *query, Task *task, List *placementList) * the same node. Router plannable checks for select queries can be turned off * by setting citus.enable_router_execution flag to false. */ -bool +static bool MultiRouterPlannableQuery(Query *query, RelationRestrictionContext *restrictionContext) { CmdType commandType = query->commandType; @@ -3021,19 +3066,13 @@ InstantiatePartitionQual(Node *node, void *context) * ErrorIfQueryHasModifyingCTE checks if the query contains modifying common table * expressions and errors out if it does. */ -static void +static DeferredErrorMessage * ErrorIfQueryHasModifyingCTE(Query *queryTree) { ListCell *cteCell = NULL; Assert(queryTree->commandType == CMD_SELECT); - /* we do not need to do anything if there are no CTEs */ - if (queryTree->cteList == NIL) - { - return; - } - foreach(cteCell, queryTree->cteList) { CommonTableExpr *cte = (CommonTableExpr *) lfirst(cteCell); @@ -3047,11 +3086,13 @@ ErrorIfQueryHasModifyingCTE(Query *queryTree) */ if (cteQuery->commandType != CMD_SELECT) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot perform distributed planning for the given " - "modification"), - errdetail("Data-modifying statements are not supported in " - "the WITH clauses of distributed queries."))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "data-modifying statements are not supported in " + "the WITH clauses of distributed queries", + NULL, NULL); } } + + /* everything OK */ + return NULL; } diff --git a/src/backend/distributed/utils/citus_outfuncs.c b/src/backend/distributed/utils/citus_outfuncs.c index 28b943848..0e9ed1aa6 100644 --- a/src/backend/distributed/utils/citus_outfuncs.c +++ b/src/backend/distributed/utils/citus_outfuncs.c @@ -280,6 +280,7 @@ OutMultiPlan(OUTFUNC_ARGS) WRITE_NODE_FIELD(masterQuery); WRITE_STRING_FIELD(masterTableName); WRITE_BOOL_FIELD(routerExecutable); + WRITE_NODE_FIELD(planningError); } diff --git a/src/backend/distributed/utils/citus_readfuncs.c b/src/backend/distributed/utils/citus_readfuncs.c index 149ae0492..4d51ae86d 100644 --- a/src/backend/distributed/utils/citus_readfuncs.c +++ b/src/backend/distributed/utils/citus_readfuncs.c @@ -187,6 +187,7 @@ ReadMultiPlan(READFUNC_ARGS) READ_NODE_FIELD(masterQuery); READ_STRING_FIELD(masterTableName); READ_BOOL_FIELD(routerExecutable); + READ_NODE_FIELD(planningError); READ_DONE(); } diff --git a/src/include/distributed/multi_physical_planner.h b/src/include/distributed/multi_physical_planner.h index 0905a3b15..61f5baf73 100644 --- a/src/include/distributed/multi_physical_planner.h +++ b/src/include/distributed/multi_physical_planner.h @@ -19,6 +19,7 @@ #include "datatype/timestamp.h" #include "distributed/citus_nodes.h" +#include "distributed/errormessage.h" #include "distributed/master_metadata_utility.h" #include "distributed/multi_logical_planner.h" #include "lib/stringinfo.h" @@ -215,6 +216,13 @@ typedef struct MultiPlan Query *masterQuery; char *masterTableName; bool routerExecutable; + + /* + * NULL if this a valid plan, an error description otherwise. This will + * e.g. be set if SQL features are present that a planner doesn't support, + * or if prepared statement parameters prevented successful planning. + */ + DeferredErrorMessage *planningError; } MultiPlan; diff --git a/src/include/distributed/multi_planner.h b/src/include/distributed/multi_planner.h index a425232fa..baee8a064 100644 --- a/src/include/distributed/multi_planner.h +++ b/src/include/distributed/multi_planner.h @@ -56,5 +56,6 @@ struct MultiPlan; extern struct MultiPlan * GetMultiPlan(PlannedStmt *planStatement); extern void multi_relation_restriction_hook(PlannerInfo *root, RelOptInfo *relOptInfo, Index index, RangeTblEntry *rte); +extern bool IsModifyCommand(Query *query); #endif /* MULTI_PLANNER_H */ diff --git a/src/include/distributed/multi_router_planner.h b/src/include/distributed/multi_router_planner.h index 9fac89427..86d69a258 100644 --- a/src/include/distributed/multi_router_planner.h +++ b/src/include/distributed/multi_router_planner.h @@ -14,6 +14,7 @@ #include "c.h" +#include "distributed/errormessage.h" #include "distributed/multi_logical_planner.h" #include "distributed/multi_physical_planner.h" #include "distributed/multi_planner.h" @@ -28,10 +29,13 @@ extern bool EnableRouterExecution; -extern MultiPlan * MultiRouterPlanCreate(Query *originalQuery, Query *query, - RelationRestrictionContext *restrictionContext); +extern MultiPlan * CreateRouterPlan(Query *originalQuery, Query *query, + RelationRestrictionContext *restrictionContext); +extern MultiPlan * CreateModifyPlan(Query *originalQuery, Query *query, + RelationRestrictionContext *restrictionContext); + extern void AddUninstantiatedPartitionRestriction(Query *originalQuery); -extern void ErrorIfModifyQueryNotSupported(Query *queryTree); +extern DeferredErrorMessage * ModifyQuerySupported(Query *queryTree); extern Query * ReorderInsertSelectTargetLists(Query *originalQuery, RangeTblEntry *insertRte, RangeTblEntry *subqueryRte); diff --git a/src/test/regress/expected/multi_insert_select.out b/src/test/regress/expected/multi_insert_select.out index 491406d55..f28f6f472 100644 --- a/src/test/regress/expected/multi_insert_select.out +++ b/src/test/regress/expected/multi_insert_select.out @@ -177,16 +177,14 @@ SELECT user_id, (random()*10)::int FROM raw_events_first; -ERROR: cannot perform distributed planning for the given modification -DETAIL: Volatile functions are not allowed in INSERT ... SELECT queries +ERROR: volatile functions are not allowed in INSERT ... SELECT queries INSERT INTO raw_events_second (user_id, value_1) WITH sub_cte AS (SELECT (random()*10)::int) SELECT user_id, (SELECT * FROM sub_cte) FROM raw_events_first; -ERROR: cannot perform distributed planning for the given modification -DETAIL: Volatile functions are not allowed in INSERT ... SELECT queries +ERROR: volatile functions are not allowed in INSERT ... SELECT queries -- add one more row INSERT INTO raw_events_first (user_id, time) VALUES (7, now()); @@ -1098,8 +1096,7 @@ INSERT INTO agg_events (value_1_agg, user_id) DEBUG: StartTransactionCommand DEBUG: StartTransaction DEBUG: name: unnamed; blockState: DEFAULT; state: INPROGR, xid/subid/cid: 0/1/0, nestlvl: 1, children: -ERROR: cannot perform distributed planning for the given modification -DETAIL: DISTINCT ON clauses are not allowed in INSERT ... SELECT queries +ERROR: DISTINCT ON clauses are not allowed in INSERT ... SELECT queries -- We do not support some CTEs WITH fist_table_agg AS (SELECT sum(value_1) as v1_agg, user_id FROM raw_events_first GROUP BY user_id) @@ -1112,7 +1109,7 @@ INSERT INTO agg_events DEBUG: StartTransactionCommand DEBUG: StartTransaction DEBUG: name: unnamed; blockState: DEFAULT; state: INPROGR, xid/subid/cid: 0/1/0, nestlvl: 1, children: -ERROR: cannot plan INSERT INTO ... SELECT because partition columns in the target table and the subquery do not match +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match DETAIL: The target table's partition column should correspond to a partition column in the subquery. -- We do support some CTEs INSERT INTO agg_events @@ -1156,8 +1153,7 @@ FROM DEBUG: StartTransactionCommand DEBUG: StartTransaction DEBUG: name: unnamed; blockState: DEFAULT; state: INPROGR, xid/subid/cid: 0/1/0, nestlvl: 1, children: -ERROR: cannot perform distributed planning for the given modification -DETAIL: Set operations are not allowed in INSERT ... SELECT queries +ERROR: set operations are not allowed in INSERT ... SELECT queries -- We do not support any set operations INSERT INTO raw_events_first(user_id) @@ -1166,8 +1162,7 @@ INSERT INTO DEBUG: StartTransactionCommand DEBUG: StartTransaction DEBUG: name: unnamed; blockState: DEFAULT; state: INPROGR, xid/subid/cid: 0/1/0, nestlvl: 1, children: -ERROR: cannot perform distributed planning for the given modification -DETAIL: Set operations are not allowed in INSERT ... SELECT queries +ERROR: set operations are not allowed in INSERT ... SELECT queries -- We do not support any set operations INSERT INTO raw_events_first(user_id) @@ -1179,8 +1174,7 @@ FROM DEBUG: StartTransactionCommand DEBUG: StartTransaction DEBUG: name: unnamed; blockState: DEFAULT; state: INPROGR, xid/subid/cid: 0/1/0, nestlvl: 1, children: -ERROR: cannot perform distributed planning for the given modification -DETAIL: Set operations are not allowed in INSERT ... SELECT queries +ERROR: set operations are not allowed in INSERT ... SELECT queries -- unsupported JOIN INSERT INTO agg_events (value_4_agg, @@ -1219,7 +1213,7 @@ FROM (SELECT SUM(raw_events_second.value_4) AS v4, DEBUG: StartTransactionCommand DEBUG: StartTransaction DEBUG: name: unnamed; blockState: DEFAULT; state: INPROGR, xid/subid/cid: 0/1/0, nestlvl: 1, children: -ERROR: cannot plan INSERT INTO ... SELECT because partition columns in the target table and the subquery do not match +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match DETAIL: The data type of the target table's partition column should exactly match the data type of the corresponding simple column reference in the subquery. -- error cases -- no part column at all @@ -1230,7 +1224,8 @@ FROM raw_events_first; DEBUG: StartTransactionCommand DEBUG: StartTransaction DEBUG: name: unnamed; blockState: DEFAULT; state: INPROGR, xid/subid/cid: 0/1/0, nestlvl: 1, children: -ERROR: cannot plan INSERT INTO ... SELECT because the query doesn't include the target table's partition column +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match +DETAIL: the query doesn't include the target table's partition column INSERT INTO raw_events_second (value_1) SELECT user_id @@ -1238,7 +1233,8 @@ FROM raw_events_first; DEBUG: StartTransactionCommand DEBUG: StartTransaction DEBUG: name: unnamed; blockState: DEFAULT; state: INPROGR, xid/subid/cid: 0/1/0, nestlvl: 1, children: -ERROR: cannot plan INSERT INTO ... SELECT because the query doesn't include the target table's partition column +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match +DETAIL: the query doesn't include the target table's partition column INSERT INTO raw_events_second (user_id) SELECT value_1 @@ -1246,7 +1242,7 @@ FROM raw_events_first; DEBUG: StartTransactionCommand DEBUG: StartTransaction DEBUG: name: unnamed; blockState: DEFAULT; state: INPROGR, xid/subid/cid: 0/1/0, nestlvl: 1, children: -ERROR: cannot plan INSERT INTO ... SELECT because partition columns in the target table and the subquery do not match +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match DETAIL: The target table's partition column should correspond to a partition column in the subquery. INSERT INTO raw_events_second (user_id) @@ -1255,7 +1251,7 @@ FROM raw_events_first; DEBUG: StartTransactionCommand DEBUG: StartTransaction DEBUG: name: unnamed; blockState: DEFAULT; state: INPROGR, xid/subid/cid: 0/1/0, nestlvl: 1, children: -ERROR: cannot plan INSERT INTO ... SELECT because partition columns in the target table and the subquery do not match +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match DETAIL: Subquery contains an operator in the same position as the target table's partition column. HINT: Ensure the target table's partition column has a corresponding simple column reference to a distributed table's partition column in the subquery. INSERT INTO raw_events_second @@ -1265,7 +1261,7 @@ FROM raw_events_first; DEBUG: StartTransactionCommand DEBUG: StartTransaction DEBUG: name: unnamed; blockState: DEFAULT; state: INPROGR, xid/subid/cid: 0/1/0, nestlvl: 1, children: -ERROR: cannot plan INSERT INTO ... SELECT because partition columns in the target table and the subquery do not match +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match DETAIL: Subquery contains an explicit cast in the same position as the target table's partition column. HINT: Ensure the target table's partition column has a corresponding simple column reference to a distributed table's partition column in the subquery. INSERT INTO agg_events @@ -1284,7 +1280,7 @@ GROUP BY user_id; DEBUG: StartTransactionCommand DEBUG: StartTransaction DEBUG: name: unnamed; blockState: DEFAULT; state: INPROGR, xid/subid/cid: 0/1/0, nestlvl: 1, children: -ERROR: cannot plan INSERT INTO ... SELECT because partition columns in the target table and the subquery do not match +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match DETAIL: Subquery contains an aggregation in the same position as the target table's partition column. HINT: Ensure the target table's partition column has a corresponding simple column reference to a distributed table's partition column in the subquery. INSERT INTO agg_events @@ -1304,7 +1300,7 @@ GROUP BY user_id, DEBUG: StartTransactionCommand DEBUG: StartTransaction DEBUG: name: unnamed; blockState: DEFAULT; state: INPROGR, xid/subid/cid: 0/1/0, nestlvl: 1, children: -ERROR: cannot plan INSERT INTO ... SELECT because partition columns in the target table and the subquery do not match +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match DETAIL: The target table's partition column should correspond to a partition column in the subquery. -- tables should be co-located INSERT INTO agg_events (user_id) @@ -1315,7 +1311,7 @@ FROM DEBUG: StartTransactionCommand DEBUG: StartTransaction DEBUG: name: unnamed; blockState: DEFAULT; state: INPROGR, xid/subid/cid: 0/1/0, nestlvl: 1, children: -ERROR: cannot plan INSERT INTO ... SELECT because partition columns in the target table and the subquery do not match +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match DETAIL: The target table's partition column should correspond to a partition column in the subquery. -- unsupported joins between subqueries -- we do not return bare partition column on the inner query @@ -1344,7 +1340,7 @@ ON (f.id = f2.id); DEBUG: StartTransactionCommand DEBUG: StartTransaction DEBUG: name: unnamed; blockState: DEFAULT; state: INPROGR, xid/subid/cid: 0/1/0, nestlvl: 1, children: -ERROR: cannot plan INSERT INTO ... SELECT because partition columns in the target table and the subquery do not match +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match DETAIL: Subquery contains an expression that is not a simple column reference in the same position as the target table's partition column. HINT: Ensure the target table's partition column has a corresponding simple column reference to a distributed table's partition column in the subquery. -- the second part of the query is not routable since @@ -1428,8 +1424,7 @@ GROUP BY grouping sets ( ( user_id ), ( value_1 ), ( user_id, value_1 ), ( ) ); DEBUG: StartTransactionCommand DEBUG: StartTransaction DEBUG: name: unnamed; blockState: DEFAULT; state: INPROGR, xid/subid/cid: 0/1/0, nestlvl: 1, children: -ERROR: cannot perform distributed planning for the given modification -DETAIL: Grouping sets are not allowed in INSERT ... SELECT queries +ERROR: grouping sets are not allowed in INSERT ... SELECT queries -- set back to INFO SET client_min_messages TO INFO; DEBUG: StartTransactionCommand @@ -1999,8 +1994,7 @@ FROM table_with_defaults GROUP BY store_id; -ERROR: cannot perform distributed planning for the given modification -DETAIL: Volatile functions are not allowed in INSERT ... SELECT queries +ERROR: volatile functions are not allowed in INSERT ... SELECT queries -- do some more error/error message checks SET citus.shard_count TO 4; SET citus.shard_replication_factor TO 1; @@ -2030,42 +2024,42 @@ INSERT INTO text_table (part_col) CASE WHEN part_col = 'onder' THEN 'marco' END FROM text_table ; -ERROR: cannot plan INSERT INTO ... SELECT because partition columns in the target table and the subquery do not match +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match DETAIL: Subquery contains a case expression in the same position as the target table's partition column. HINT: Ensure the target table's partition column has a corresponding simple column reference to a distributed table's partition column in the subquery. INSERT INTO text_table (part_col) SELECT COALESCE(part_col, 'onder') FROM text_table; -ERROR: cannot plan INSERT INTO ... SELECT because partition columns in the target table and the subquery do not match +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match DETAIL: Subquery contains a coalesce expression in the same position as the target table's partition column. HINT: Ensure the target table's partition column has a corresponding simple column reference to a distributed table's partition column in the subquery. INSERT INTO text_table (part_col) SELECT GREATEST(part_col, 'jason') FROM text_table; -ERROR: cannot plan INSERT INTO ... SELECT because partition columns in the target table and the subquery do not match +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match DETAIL: Subquery contains a min/max expression in the same position as the target table's partition column. HINT: Ensure the target table's partition column has a corresponding simple column reference to a distributed table's partition column in the subquery. INSERT INTO text_table (part_col) SELECT LEAST(part_col, 'andres') FROM text_table; -ERROR: cannot plan INSERT INTO ... SELECT because partition columns in the target table and the subquery do not match +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match DETAIL: Subquery contains a min/max expression in the same position as the target table's partition column. HINT: Ensure the target table's partition column has a corresponding simple column reference to a distributed table's partition column in the subquery. INSERT INTO text_table (part_col) SELECT NULLIF(part_col, 'metin') FROM text_table; -ERROR: cannot plan INSERT INTO ... SELECT because partition columns in the target table and the subquery do not match +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match DETAIL: Subquery contains an expression that is not a simple column reference in the same position as the target table's partition column. HINT: Ensure the target table's partition column has a corresponding simple column reference to a distributed table's partition column in the subquery. INSERT INTO text_table (part_col) SELECT part_col isnull FROM text_table; -ERROR: cannot plan INSERT INTO ... SELECT because partition columns in the target table and the subquery do not match +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match DETAIL: Subquery contains an expression that is not a simple column reference in the same position as the target table's partition column. HINT: Ensure the target table's partition column has a corresponding simple column reference to a distributed table's partition column in the subquery. INSERT INTO text_table (part_col) SELECT part_col::text from char_table; -ERROR: cannot plan INSERT INTO ... SELECT because partition columns in the target table and the subquery do not match +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match DETAIL: Subquery contains an explicit coercion in the same position as the target table's partition column. HINT: Ensure the target table's partition column has a corresponding simple column reference to a distributed table's partition column in the subquery. INSERT INTO text_table (part_col) SELECT (part_col = 'burak') is true FROM text_table; -ERROR: cannot plan INSERT INTO ... SELECT because partition columns in the target table and the subquery do not match +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match DETAIL: Subquery contains an expression that is not a simple column reference in the same position as the target table's partition column. HINT: Ensure the target table's partition column has a corresponding simple column reference to a distributed table's partition column in the subquery. INSERT INTO text_table (part_col) SELECT val FROM text_table; -ERROR: cannot plan INSERT INTO ... SELECT because partition columns in the target table and the subquery do not match +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match DETAIL: The data type of the target table's partition column should exactly match the data type of the corresponding simple column reference in the subquery. INSERT INTO text_table (part_col) SELECT val::text FROM text_table; -ERROR: cannot plan INSERT INTO ... SELECT because partition columns in the target table and the subquery do not match +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match DETAIL: Subquery contains an explicit coercion in the same position as the target table's partition column. HINT: Ensure the target table's partition column has a corresponding simple column reference to a distributed table's partition column in the subquery. insert into table_with_starts_with_defaults (b,c) select b,c FROM table_with_starts_with_defaults; diff --git a/src/test/regress/expected/multi_modifications.out b/src/test/regress/expected/multi_modifications.out index 411aedef9..0f45ecf3b 100644 --- a/src/test/regress/expected/multi_modifications.out +++ b/src/test/regress/expected/multi_modifications.out @@ -216,8 +216,7 @@ DETAIL: Multi-row INSERTs to distributed tables are not supported. -- commands containing a CTE are unsupported WITH deleted_orders AS (DELETE FROM limit_orders RETURNING *) INSERT INTO limit_orders DEFAULT VALUES; -ERROR: cannot perform distributed planning for the given modification -DETAIL: Common table expressions are not supported in distributed modifications. +ERROR: common table expressions are not supported in distributed modifications -- test simple DELETE INSERT INTO limit_orders VALUES (246, 'TSLA', 162, '2007-07-02 16:32:15', 'sell', 20.69); SELECT COUNT(*) FROM limit_orders WHERE id = 246; @@ -275,8 +274,7 @@ ERROR: cannot plan queries that include both regular and partitioned relations -- commands containing a CTE are unsupported WITH deleted_orders AS (INSERT INTO limit_orders DEFAULT VALUES RETURNING *) DELETE FROM limit_orders; -ERROR: cannot perform distributed planning for the given modification -DETAIL: Common table expressions are not supported in distributed modifications. +ERROR: common table expressions are not supported in distributed modifications -- cursors are not supported DELETE FROM limit_orders WHERE CURRENT OF cursor_name; ERROR: distributed modifications must target exactly one shard @@ -421,8 +419,7 @@ ERROR: cannot plan queries that include both regular and partitioned relations -- commands containing a CTE are unsupported WITH deleted_orders AS (INSERT INTO limit_orders DEFAULT VALUES RETURNING *) UPDATE limit_orders SET symbol = 'GM'; -ERROR: cannot perform distributed planning for the given modification -DETAIL: Common table expressions are not supported in distributed modifications. +ERROR: common table expressions are not supported in distributed modifications SELECT symbol, bidder_id FROM limit_orders WHERE id = 246; symbol | bidder_id --------+----------- diff --git a/src/test/regress/expected/multi_reference_table.out b/src/test/regress/expected/multi_reference_table.out index 3ee311bc7..7ff726ce1 100644 --- a/src/test/regress/expected/multi_reference_table.out +++ b/src/test/regress/expected/multi_reference_table.out @@ -1091,7 +1091,7 @@ FROM colocated_table_test, colocated_table_test_2 WHERE colocated_table_test.value_1 = colocated_table_test.value_1; -ERROR: If data inserted into a reference table, all of the participating tables in the INSERT INTO ... SELECT query should be reference tables. +ERROR: only reference tables may be queried when targeting a reference table with INSERT ... SELECT -- should error out, same as the above INSERT INTO reference_table_test (value_1) @@ -1101,7 +1101,7 @@ FROM colocated_table_test, reference_table_test WHERE colocated_table_test.value_1 = reference_table_test.value_1; -ERROR: If data inserted into a reference table, all of the participating tables in the INSERT INTO ... SELECT query should be reference tables. +ERROR: only reference tables may be queried when targeting a reference table with INSERT ... SELECT -- now, insert into the hash partitioned table and use reference -- tables in the SELECT queries INSERT INTO @@ -1146,7 +1146,7 @@ FROM WHERE colocated_table_test_2.value_4 = reference_table_test.value_4 RETURNING value_1, value_2; -ERROR: cannot plan INSERT INTO ... SELECT because partition columns in the target table and the subquery do not match +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match DETAIL: The data type of the target table's partition column should exactly match the data type of the corresponding simple column reference in the subquery. -- partition column value comes from reference table which should error out INSERT INTO @@ -1158,7 +1158,7 @@ FROM WHERE colocated_table_test_2.value_4 = reference_table_test.value_4 RETURNING value_1, value_2; -ERROR: cannot plan INSERT INTO ... SELECT because partition columns in the target table and the subquery do not match +ERROR: INSERT INTO ... SELECT partition columns in the source table and subquery do not match DETAIL: The target table's partition column should correspond to a partition column in the subquery. -- some tests for mark_tables_colocated -- should error out diff --git a/src/test/regress/expected/multi_router_planner.out b/src/test/regress/expected/multi_router_planner.out index 46f9e34a3..339bcb764 100644 --- a/src/test/regress/expected/multi_router_planner.out +++ b/src/test/regress/expected/multi_router_planner.out @@ -444,8 +444,7 @@ WITH new_article AS ( INSERT INTO articles_hash VALUES (1, 1, 'arsenous', 9572) RETURNING * ) SELECT * FROM new_article; -ERROR: cannot perform distributed planning for the given modification -DETAIL: Data-modifying statements are not supported in the WITH clauses of distributed queries. +ERROR: data-modifying statements are not supported in the WITH clauses of distributed queries -- Modifying statement in nested CTE case is covered by PostgreSQL itself WITH new_article AS ( WITH nested_cte AS ( diff --git a/src/test/regress/expected/multi_shard_modify.out b/src/test/regress/expected/multi_shard_modify.out index 98077f51a..c9b0f3cd2 100644 --- a/src/test/regress/expected/multi_shard_modify.out +++ b/src/test/regress/expected/multi_shard_modify.out @@ -92,8 +92,7 @@ SELECT master_modify_multiple_shards('DELETE FROM multi_shard_modify_test WHERE ERROR: master_modify_multiple_shards() does not support RETURNING -- commands containing a CTE are unsupported SELECT master_modify_multiple_shards('WITH deleted_stuff AS (INSERT INTO multi_shard_modify_test DEFAULT VALUES RETURNING *) DELETE FROM multi_shard_modify_test'); -ERROR: cannot perform distributed planning for the given modification -DETAIL: Common table expressions are not supported in distributed modifications. +ERROR: common table expressions are not supported in distributed modifications -- Check that we can successfully delete from multiple shards with 1PC SET citus.multi_shard_commit_protocol TO '1pc'; SELECT count(*) FROM multi_shard_modify_test; @@ -234,8 +233,7 @@ SELECT master_modify_multiple_shards('UPDATE multi_shard_modify_test SET t_name= ERROR: master_modify_multiple_shards() does not support RETURNING -- commands containing a CTE are unsupported SELECT master_modify_multiple_shards('WITH t AS (INSERT INTO multi_shard_modify_test DEFAULT VALUES RETURNING *) UPDATE multi_shard_modify_test SET t_name = ''FAIL'' '); -ERROR: cannot perform distributed planning for the given modification -DETAIL: Common table expressions are not supported in distributed modifications. +ERROR: common table expressions are not supported in distributed modifications -- updates referencing just a var are supported SELECT master_modify_multiple_shards('UPDATE multi_shard_modify_test SET t_value=t_key WHERE t_key = 10'); master_modify_multiple_shards diff --git a/src/test/regress/expected/multi_upsert.out b/src/test/regress/expected/multi_upsert.out index 10106d088..8038b7087 100644 --- a/src/test/regress/expected/multi_upsert.out +++ b/src/test/regress/expected/multi_upsert.out @@ -237,8 +237,7 @@ INSERT INTO dropcol_distributed AS dropcol (key, keep1) VALUES (1, '5') ON CONFL -- subquery in the SET clause INSERT INTO upsert_test (part_key, other_col) VALUES (1, 1) ON CONFLICT (part_key) DO UPDATE SET other_col = (SELECT count(*) from upsert_test); -ERROR: cannot perform distributed planning for the given modification -DETAIL: Subqueries are not supported in distributed modifications. +ERROR: subqueries are not supported in distributed modifications -- non mutable function call in the SET INSERT INTO upsert_test (part_key, other_col) VALUES (1, 1) ON CONFLICT (part_key) DO UPDATE SET other_col = random()::int;