Fix window function plan generation to cope with volatile sort expressions.
authorTom Lane
Mon, 30 Mar 2009 17:30:44 +0000 (17:30 +0000)
committerTom Lane
Mon, 30 Mar 2009 17:30:44 +0000 (17:30 +0000)
(Not clear how useful these really are, but failing is no good...)
Per report from David Fetter and Robert Treat.

src/backend/optimizer/plan/planner.c

index 40ed1ac38cd0ff07f164184b5be27268d9ff5e2a..d7a793ba769687ed44d230ab362dfc5599259ab3 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.252 2009/03/24 21:12:56 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.253 2009/03/30 17:30:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -83,6 +83,8 @@ static void locate_grouping_columns(PlannerInfo *root,
                        AttrNumber *groupColIdx);
 static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
 static List *select_active_windows(PlannerInfo *root, WindowFuncLists *wflists);
+static List *add_volatile_sort_exprs(List *window_tlist, List *tlist,
+                                    List *activeWindows);
 static List *make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc,
                                      List *tlist, bool canonicalize);
 static void get_column_info_for_window(PlannerInfo *root, WindowClause *wc,
@@ -1305,7 +1307,10 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
             * (In some cases we wouldn't need to propagate all of these
             * all the way to the top, since they might only be needed as
             * inputs to WindowFuncs.  It's probably not worth trying to
-            * optimize that though.)  As we climb up the stack, we add
+            * optimize that though.)  We also need any volatile sort
+            * expressions, because make_sort_from_pathkeys won't add those
+            * on its own, and anyway we want them evaluated only once at
+            * the bottom of the stack.  As we climb up the stack, we add
             * outputs for the WindowFuncs computed at each level.  Also,
             * each input tlist has to present all the columns needed to
             * sort the data for the next WindowAgg step.  That's handled
@@ -1317,6 +1322,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
            if (parse->hasAggs)
                window_tlist = add_to_flat_tlist(window_tlist,
                                            pull_agg_clause((Node *) tlist));
+           window_tlist = add_volatile_sort_exprs(window_tlist, tlist,
+                                                  activeWindows);
            result_plan->targetlist = (List *) copyObject(window_tlist);
 
            foreach(l, activeWindows)
@@ -2429,6 +2436,68 @@ select_active_windows(PlannerInfo *root, WindowFuncLists *wflists)
    return result;
 }
 
+/*
+ * add_volatile_sort_exprs
+ *     Identify any volatile sort/group expressions used by the active
+ *     windows, and add them to window_tlist if not already present.
+ *     Return the modified window_tlist.
+ */
+static List *
+add_volatile_sort_exprs(List *window_tlist, List *tlist, List *activeWindows)
+{
+   Bitmapset  *sgrefs = NULL;
+   ListCell   *lc;
+
+   /* First, collect the sortgrouprefs of the windows into a bitmapset */
+   foreach(lc, activeWindows)
+   {
+       WindowClause *wc = (WindowClause *) lfirst(lc);
+       ListCell   *lc2;
+
+       foreach(lc2, wc->partitionClause)
+       {
+           SortGroupClause *sortcl = (SortGroupClause *) lfirst(lc2);
+
+           sgrefs = bms_add_member(sgrefs, sortcl->tleSortGroupRef);
+       }
+       foreach(lc2, wc->orderClause)
+       {
+           SortGroupClause *sortcl = (SortGroupClause *) lfirst(lc2);
+
+           sgrefs = bms_add_member(sgrefs, sortcl->tleSortGroupRef);
+       }
+   }
+
+   /*
+    * Now scan the original tlist to find the referenced expressions.
+    * Any that are volatile must be added to window_tlist.
+    *
+    * Note: we know that the input window_tlist contains no items marked
+    * with ressortgrouprefs, so we don't have to worry about collisions
+    * of the reference numbers.
+    */
+   foreach(lc, tlist)
+   {
+       TargetEntry *tle = (TargetEntry *) lfirst(lc);
+
+       if (tle->ressortgroupref != 0 &&
+           bms_is_member(tle->ressortgroupref, sgrefs) &&
+           contain_volatile_functions((Node *) tle->expr))
+       {
+           TargetEntry *newtle;
+
+           newtle = makeTargetEntry(tle->expr,
+                                    list_length(window_tlist) + 1,
+                                    NULL,
+                                    false);
+           newtle->ressortgroupref = tle->ressortgroupref;
+           window_tlist = lappend(window_tlist, newtle);
+       }
+   }
+
+   return window_tlist;
+}
+
 /*
  * make_pathkeys_for_window
  *     Create a pathkeys list describing the required input ordering