Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Fix incorrect tests for SRFs in relation_can_be_sorted_early().
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 3 Aug 2022 21:33:42 +0000 (17:33 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 3 Aug 2022 21:33:42 +0000 (17:33 -0400)
Commit fac1b470a thought we could check for set-returning functions
by testing only the top-level node in an expression tree.  This is
wrong in itself, and to make matters worse it encouraged others
to make the same mistake, by exporting tlist.c's special-purpose
IS_SRF_CALL() as a widely-visible macro.  I can't find any evidence
that anyone's taken the bait, but it was only a matter of time.

Use expression_returns_set() instead, and stuff the IS_SRF_CALL()
genie back in its bottle, this time with a warning label.  I also
added a couple of cross-reference comments.

After a fair amount of fooling around, I've despaired of making
a robust test case that exposes the bug reliably, so no test case
here.  (Note that the test case added by fac1b470a is itself
broken, in that it doesn't notice if you remove the code change.
The repro given by the bug submitter currently doesn't fail either
in v15 or HEAD, though I suspect that may indicate an unrelated bug.)

Per bug #17564 from Martijn van Oosterhout.  Back-patch to v13,
as the faulty patch was.

Discussion: https://postgr.es/m/17564-c7472c2f90ef2da3@postgresql.org

src/backend/nodes/nodeFuncs.c
src/backend/optimizer/path/equivclass.c
src/backend/optimizer/util/tlist.c
src/include/optimizer/optimizer.h

index 4cb1744da6ccb67e23353e101ff8d5896c0ce60c..c334daae3928576d5c40d0f4190b09b66e973a3a 100644 (file)
@@ -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;
index 60c0e3f1089bae7e23a540705345313f4f7e18b8..79912955485b0d5e877c4526c556219a90b22b3f 100644 (file)
@@ -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;
index fe9a9d7d8966e448691f3619fd245a10bf8a937d..784a1af82dfa30cc1f39d5f7ea50f89c9542ddfd 100644 (file)
 #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
index 7be1e5906b1f56ac0d23a3b6a32864c046f59df2..409005bae9590a11812f466d3063485917ab2e67 100644 (file)
 
 #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.