diff --git a/src/backend/distributed/planner/insert_select_planner.c b/src/backend/distributed/planner/insert_select_planner.c index 3bf0bb327..60d774d01 100644 --- a/src/backend/distributed/planner/insert_select_planner.c +++ b/src/backend/distributed/planner/insert_select_planner.c @@ -1404,6 +1404,7 @@ InsertPartitionColumnMatchesSelect(Query *query, RangeTblEntry *insertRte, /* we can set the select relation id */ *selectPartitionColumnTableId = subqueryPartitionColumnRelationId; + break; } diff --git a/src/backend/distributed/planner/multi_logical_optimizer.c b/src/backend/distributed/planner/multi_logical_optimizer.c index c4c11f4eb..de5367916 100644 --- a/src/backend/distributed/planner/multi_logical_optimizer.c +++ b/src/backend/distributed/planner/multi_logical_optimizer.c @@ -4557,6 +4557,31 @@ FindReferencedTableColumn(Expr *columnExpression, List *parentQueryList, Query * FindReferencedTableColumn(joinColumn, parentQueryList, query, column, rteContainingReferencedColumn, skipOuterVars); } +#if PG_VERSION_NUM >= 180000 + else if (rangeTableEntry->rtekind == RTE_GROUP) + { + /* + * PG 18: synthetic GROUP RTE. Each groupexprs item corresponds to the + * columns produced by the grouping step, in the *same ordinal order* as + * the Vars that reference them. + */ + List *groupexprs = rangeTableEntry->groupexprs; + AttrNumber groupIndex = candidateColumn->varattno - 1; + + /* Safety check */ + if (groupIndex < 0 || groupIndex >= list_length(groupexprs)) + { + return; /* malformed Var */ + } + Expr *groupExpr = (Expr *) list_nth(groupexprs, groupIndex); + + /* Recurse on the underlying expression (stay in the same query) */ + FindReferencedTableColumn(groupExpr, parentQueryList, query, + column, rteContainingReferencedColumn, + skipOuterVars); + } +#endif /* PG_VERSION_NUM >= 180000 */ + else if (rangeTableEntry->rtekind == RTE_CTE) { /* diff --git a/src/backend/distributed/planner/multi_logical_planner.c b/src/backend/distributed/planner/multi_logical_planner.c index d1e2e6bca..55f1eec2a 100644 --- a/src/backend/distributed/planner/multi_logical_planner.c +++ b/src/backend/distributed/planner/multi_logical_planner.c @@ -136,6 +136,9 @@ static MultiNode * ApplyCartesianProductReferenceJoin(MultiNode *leftNode, static MultiNode * ApplyCartesianProduct(MultiNode *leftNode, MultiNode *rightNode, List *partitionColumnList, JoinType joinType, List *joinClauses); +#if PG_VERSION_NUM >= PG_VERSION_18 +static bool ExprMentionsPartitionColumn(Node *node, Query *query); +#endif /* @@ -224,9 +227,21 @@ TargetListOnPartitionColumn(Query *query, List *targetEntryList) TargetEntry *targetEntry = (TargetEntry *) lfirst(targetEntryCell); Expr *targetExpression = targetEntry->expr; - bool skipOuterVars = true; - bool isPartitionColumn = IsPartitionColumn(targetExpression, query, - skipOuterVars); + bool isPartitionColumn; + bool skipOuterVars = false; + +#if PG_VERSION_NUM >= PG_VERSION_18 + isPartitionColumn = + ExprMentionsPartitionColumn((Node *) targetExpression, query); +#else + { + skipOuterVars = true; + isPartitionColumn = + IsPartitionColumn(targetExpression, query, skipOuterVars); + } +#endif + + Var *column = NULL; RangeTblEntry *rte = NULL; @@ -292,6 +307,79 @@ TargetListOnPartitionColumn(Query *query, List *targetEntryList) } +#if PG_VERSION_NUM >= PG_VERSION_18 + +/* + * ExprMentionsPartitionColumn + * + * Return true iff `node` ultimately resolves to the partition column of + * *any* distributed table referenced by `query`. + * + * • Understands OUTER_VAR indirection (PG ≥ 17) + * • Understands synthetic RTE_GROUP entries (PG ≥ 18) + * • Falls back to original logic for PG 15/16 automatically + */ +static bool +ExprMentionsPartitionColumn(Node *node, Query *query) +{ + if (node == NULL) + { + return false; + } + + if (IsA(node, Var)) + { + Var *v = (Var *) node; + + /* Follow OUTER_VAR → target-list indirection, if present */ + if (v->varno == OUTER_VAR) + { + TargetEntry *tle = get_tle_by_resno(query->targetList, v->varattno); + return tle && ExprMentionsPartitionColumn((Node *) tle->expr, query); + } + + /* Sanity-check varno */ + if (v->varno <= 0 || v->varno > list_length(query->rtable)) + { + return false; + } + + RangeTblEntry *rte = rt_fetch(v->varno, query->rtable); + +#if PG_VERSION_NUM >= PG_VERSION_18 + + /* Synthetic GROUP RTE – examine its expressions instead */ + if (rte->rtekind == RTE_GROUP && rte->groupexprs) + { + ListCell *lc; + foreach(lc, rte->groupexprs) + if (ExprMentionsPartitionColumn((Node *) lfirst(lc), query)) + { + return true; + } + return false; + } +#endif + + /* Real table? — compare against its dist key */ + if (rte->rtekind == RTE_RELATION && HasDistributionKey(rte->relid)) + { + Var *partcol = DistPartitionKey(rte->relid); + return partcol && partcol->varattno == v->varattno; + } + return false; + } + + /* Recurse through any other node type */ + return expression_tree_walker(node, + ExprMentionsPartitionColumn, + (void *) query); +} + + +#endif + + /* * FindNodeMatchingCheckFunctionInRangeTableList finds a node for which the checker * function returns true.