diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index 4cb1744da6..c334daae39 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -760,6 +760,12 @@ expression_returns_set_walker(Node *node, void *context) /* else fall through to check args */ } + /* + * If you add any more cases that return sets, also fix + * expression_returns_set_rows() in clauses.c and IS_SRF_CALL() in + * tlist.c. + */ + /* Avoid recursion for some cases that parser checks not to return a set */ if (IsA(node, Aggref)) return false; diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c index 60c0e3f108..7991295548 100644 --- a/src/backend/optimizer/path/equivclass.c +++ b/src/backend/optimizer/path/equivclass.c @@ -986,7 +986,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel, * one are effectively checking properties of targetexpr, so there's * no point in asking whether some other EC member would be better.) */ - if (IS_SRF_CALL((Node *) em->em_expr)) + if (expression_returns_set((Node *) em->em_expr)) continue; /* @@ -1014,7 +1014,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel, * member in this case; since SRFs can't appear in WHERE, they cannot * belong to multi-member ECs.) */ - if (IS_SRF_CALL((Node *) em->em_expr)) + if (expression_returns_set((Node *) em->em_expr)) return false; return true; diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c index fe9a9d7d89..784a1af82d 100644 --- a/src/backend/optimizer/util/tlist.c +++ b/src/backend/optimizer/util/tlist.c @@ -21,6 +21,17 @@ #include "optimizer/tlist.h" +/* + * Test if an expression node represents a SRF call. Beware multiple eval! + * + * Please note that this is only meant for use in split_pathtarget_at_srfs(); + * if you use it anywhere else, your code is almost certainly wrong for SRFs + * nested within expressions. Use expression_returns_set() instead. + */ +#define IS_SRF_CALL(node) \ + ((IsA(node, FuncExpr) && ((FuncExpr *) (node))->funcretset) || \ + (IsA(node, OpExpr) && ((OpExpr *) (node))->opretset)) + /* * Data structures for split_pathtarget_at_srfs(). To preserve the identity * of sortgroupref items even if they are textually equal(), what we track is diff --git a/src/include/optimizer/optimizer.h b/src/include/optimizer/optimizer.h index 7be1e5906b..409005bae9 100644 --- a/src/include/optimizer/optimizer.h +++ b/src/include/optimizer/optimizer.h @@ -24,11 +24,6 @@ #include "nodes/parsenodes.h" -/* Test if an expression node represents a SRF call. Beware multiple eval! */ -#define IS_SRF_CALL(node) \ - ((IsA(node, FuncExpr) && ((FuncExpr *) (node))->funcretset) || \ - (IsA(node, OpExpr) && ((OpExpr *) (node))->opretset)) - /* * We don't want to include nodes/pathnodes.h here, because non-planner * code should generally treat PlannerInfo as an opaque typedef.