@@ -4615,11 +4615,16 @@ make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc,
4615
4615
*
4616
4616
* Our current policy is to postpone volatile expressions till after the sort
4617
4617
* unconditionally (assuming that that's possible, ie they are in plain tlist
4618
- * columns and not ORDER BY/GROUP BY/DISTINCT columns). We also postpone
4619
- * set-returning expressions unconditionally (if possible), because running
4620
- * them beforehand would bloat the sort dataset, and because it might cause
4621
- * unexpected output order if the sort isn't stable. Expensive expressions
4622
- * are postponed if there is a LIMIT, or if root->tuple_fraction shows that
4618
+ * columns and not ORDER BY/GROUP BY/DISTINCT columns). We also prefer to
4619
+ * postpone set-returning expressions, because running them beforehand would
4620
+ * bloat the sort dataset, and because it might cause unexpected output order
4621
+ * if the sort isn't stable. However there's a constraint on that: all SRFs
4622
+ * in the tlist should be evaluated at the same plan step, so that they can
4623
+ * run in sync in ExecTargetList. So if any SRFs are in sort columns, we
4624
+ * mustn't postpone any SRFs. (Note that in principle that policy should
4625
+ * probably get applied to the group/window input targetlists too, but we
4626
+ * have not done that historically.) Lastly, expensive expressions are
4627
+ * postponed if there is a LIMIT, or if root->tuple_fraction shows that
4623
4628
* partial evaluation of the query is possible (if neither is true, we expect
4624
4629
* to have to evaluate the expressions for every row anyway), or if there are
4625
4630
* any volatile or set-returning expressions (since once we've put in a
@@ -4664,10 +4669,13 @@ make_sort_input_target(PlannerInfo *root,
4664
4669
Query * parse = root -> parse ;
4665
4670
PathTarget * input_target ;
4666
4671
int ncols ;
4672
+ bool * col_is_srf ;
4667
4673
bool * postpone_col ;
4668
4674
bool have_srf ;
4669
4675
bool have_volatile ;
4670
4676
bool have_expensive ;
4677
+ bool have_srf_sortcols ;
4678
+ bool postpone_srfs ;
4671
4679
List * postponable_cols ;
4672
4680
List * postponable_vars ;
4673
4681
int i ;
@@ -4680,8 +4688,9 @@ make_sort_input_target(PlannerInfo *root,
4680
4688
4681
4689
/* Inspect tlist and collect per-column information */
4682
4690
ncols = list_length (final_target -> exprs );
4691
+ col_is_srf = (bool * ) palloc0 (ncols * sizeof (bool ));
4683
4692
postpone_col = (bool * ) palloc0 (ncols * sizeof (bool ));
4684
- have_srf = have_volatile = have_expensive = false;
4693
+ have_srf = have_volatile = have_expensive = have_srf_sortcols = false;
4685
4694
4686
4695
i = 0 ;
4687
4696
foreach (lc , final_target -> exprs )
@@ -4699,17 +4708,18 @@ make_sort_input_target(PlannerInfo *root,
4699
4708
if (final_target -> sortgrouprefs [i ] == 0 )
4700
4709
{
4701
4710
/*
4702
- * If it returns a set or is volatile, that's an unconditional
4703
- * reason to postpone. Check the SRF case first because we must
4704
- * know whether we have any postponed SRFs.
4711
+ * Check for SRF or volatile functions. Check the SRF case first
4712
+ * because we must know whether we have any postponed SRFs.
4705
4713
*/
4706
4714
if (expression_returns_set ((Node * ) expr ))
4707
4715
{
4708
- postpone_col [i ] = true;
4716
+ /* We'll decide below whether these are postponable */
4717
+ col_is_srf [i ] = true;
4709
4718
have_srf = true;
4710
4719
}
4711
4720
else if (contain_volatile_functions ((Node * ) expr ))
4712
4721
{
4722
+ /* Unconditionally postpone */
4713
4723
postpone_col [i ] = true;
4714
4724
have_volatile = true;
4715
4725
}
@@ -4736,14 +4746,26 @@ make_sort_input_target(PlannerInfo *root,
4736
4746
}
4737
4747
}
4738
4748
}
4749
+ else
4750
+ {
4751
+ /* For sortgroupref cols, just check if any contain SRFs */
4752
+ if (!have_srf_sortcols &&
4753
+ expression_returns_set ((Node * ) expr ))
4754
+ have_srf_sortcols = true;
4755
+ }
4739
4756
4740
4757
i ++ ;
4741
4758
}
4742
4759
4760
+ /*
4761
+ * We can postpone SRFs if we have some but none are in sortgroupref cols.
4762
+ */
4763
+ postpone_srfs = (have_srf && !have_srf_sortcols );
4764
+
4743
4765
/*
4744
4766
* If we don't need a post-sort projection, just return final_target.
4745
4767
*/
4746
- if (!(have_srf || have_volatile ||
4768
+ if (!(postpone_srfs || have_volatile ||
4747
4769
(have_expensive &&
4748
4770
(parse -> limitCount || root -> tuple_fraction > 0 ))))
4749
4771
return final_target ;
@@ -4754,7 +4776,7 @@ make_sort_input_target(PlannerInfo *root,
4754
4776
* rely on the query's LIMIT (if any) to bound the number of rows it needs
4755
4777
* to return.
4756
4778
*/
4757
- * have_postponed_srfs = have_srf ;
4779
+ * have_postponed_srfs = postpone_srfs ;
4758
4780
4759
4781
/*
4760
4782
* Construct the sort-input target, taking all non-postponable columns and
@@ -4769,7 +4791,7 @@ make_sort_input_target(PlannerInfo *root,
4769
4791
{
4770
4792
Expr * expr = (Expr * ) lfirst (lc );
4771
4793
4772
- if (postpone_col [i ])
4794
+ if (postpone_col [i ] || ( postpone_srfs && col_is_srf [ i ]) )
4773
4795
postponable_cols = lappend (postponable_cols , expr );
4774
4796
else
4775
4797
add_column_to_pathtarget (input_target , expr ,
0 commit comments