Disallow SRFs when considering sorts below Gather Merge
authorTomas Vondra <[email protected]>
Mon, 21 Dec 2020 17:58:32 +0000 (18:58 +0100)
committerTomas Vondra <[email protected]>
Mon, 21 Dec 2020 18:36:22 +0000 (19:36 +0100)
While we do allow SRFs in ORDER BY, scan/join processing should not
consider such cases - such sorts should only happen via final Sort atop
a ProjectSet. So make sure we don't try adding such sorts below Gather
Merge, just like we do for expressions that are volatile and/or not
parallel safe.

Back to PostgreSQL 13, where this code was introduced as part of
the Incremental Sort .

Author: James Coleman
Reviewed-by: Tomas Vondra
Back-through: 13
Discussion: https://postgr.es/m/CAAaqYe8cK3g5CfLC4w7bs=hC0mSksZC=H5M8LSchj5e5OxpTAg@mail.gmail.com
Discussion: https://postgr.es/m/295524.1606246314%40sss.pgh.pa.us

src/backend/optimizer/path/equivclass.c
src/backend/optimizer/util/tlist.c
src/include/optimizer/optimizer.h
src/test/regress/expected/incremental_sort.out
src/test/regress/sql/incremental_sort.sql

index b49ee52cbad8254fcc9a6f91b246f9b52db8aa0e..0b2e212b2bc1853ba81c99415f233411c2666cda 100644 (file)
@@ -840,6 +840,13 @@ find_em_expr_usable_for_sorting_rel(PlannerInfo *root, EquivalenceClass *ec,
        if (require_parallel_safe && !is_parallel_safe(root, (Node *) em_expr))
            continue;
 
+       /*
+        * Disallow SRFs so that all of them can be evaluated at the correct
+        * time as determined by make_sort_input_target.
+        */
+       if (IS_SRF_CALL((Node *) em_expr))
+           continue;
+
        /*
         * As long as the expression isn't volatile then
         * prepare_sort_from_pathkeys is able to generate a new target entry,
index 02a3c6b16561910348ead2712924b3ee56dd378e..01cea102eab1a3b61a6b1ea91abf0cb5b26bb226 100644 (file)
 #include "optimizer/tlist.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))
-
 /*
  * 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 dea0e7338d59996c22fc5ce8578d16b785d699fb..446071c554d93f1d9adaf486e8615dfd723075bf 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.
index d316a7c4b909f3b75512758023ae942c6163acea..a8cbfd9f5f995176025bdb4210922878fc5b3941 100644 (file)
@@ -1620,3 +1620,15 @@ order by 1, 2;
                ->  Function Scan on generate_series
 (7 rows)
 
+-- Disallow pushing down sort when pathkey is an SRF.
+explain (costs off) select unique1 from tenk1 order by unnest('{1,2}'::int[]);
+                               QUERY PLAN                                
+-------------------------------------------------------------------------
+ Sort
+   Sort Key: (unnest('{1,2}'::integer[]))
+   ->  Gather
+         Workers Planned: 2
+         ->  ProjectSet
+               ->  Parallel Index Only Scan using tenk1_unique1 on tenk1
+(6 rows)
+
index ff3af0fd165d87708cc72ed135c00add76b3fd94..62a037b0cfb6732b8209c485403cb1149ec7e4b2 100644 (file)
@@ -267,3 +267,5 @@ from tenk1, lateral (select tenk1.unique1 from generate_series(1, 1000)) as sub;
 explain (costs off) select sub.unique1, stringu1 || random()::text
 from tenk1, lateral (select tenk1.unique1 from generate_series(1, 1000)) as sub
 order by 1, 2;
+-- Disallow pushing down sort when pathkey is an SRF.
+explain (costs off) select unique1 from tenk1 order by unnest('{1,2}'::int[]);