summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/planner.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan/planner.c')
-rw-r--r--src/backend/optimizer/plan/planner.c156
1 files changed, 156 insertions, 0 deletions
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index e21e72eb870..8a41e1e6d36 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -41,6 +41,7 @@
#ifdef OPTIMIZER_DEBUG
#include "nodes/print.h"
#endif
+#include "nodes/supportnodes.h"
#include "optimizer/appendinfo.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
@@ -208,6 +209,8 @@ static PathTarget *make_partial_grouping_target(PlannerInfo *root,
PathTarget *grouping_target,
Node *havingQual);
static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
+static void optimize_window_clauses(PlannerInfo *root,
+ WindowFuncLists *wflists);
static List *select_active_windows(PlannerInfo *root, WindowFuncLists *wflists);
static PathTarget *make_window_input_target(PlannerInfo *root,
PathTarget *final_target,
@@ -1430,7 +1433,16 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
wflists = find_window_functions((Node *) root->processed_tlist,
list_length(parse->windowClause));
if (wflists->numWindowFuncs > 0)
+ {
+ /*
+ * See if any modifications can be made to each WindowClause
+ * to allow the executor to execute the WindowFuncs more
+ * quickly.
+ */
+ optimize_window_clauses(root, wflists);
+
activeWindows = select_active_windows(root, wflists);
+ }
else
parse->hasWindowFuncs = false;
}
@@ -5433,6 +5445,150 @@ postprocess_setop_tlist(List *new_tlist, List *orig_tlist)
}
/*
+ * optimize_window_clauses
+ * Call each WindowFunc's prosupport function to see if we're able to
+ * make any adjustments to any of the WindowClause's so that the executor
+ * can execute the window functions in a more optimal way.
+ *
+ * Currently we only allow adjustments to the WindowClause's frameOptions. We
+ * may allow more things to be done here in the future.
+ */
+static void
+optimize_window_clauses(PlannerInfo *root, WindowFuncLists *wflists)
+{
+ List *windowClause = root->parse->windowClause;
+ ListCell *lc;
+
+ foreach(lc, windowClause)
+ {
+ WindowClause *wc = lfirst_node(WindowClause, lc);
+ ListCell *lc2;
+ int optimizedFrameOptions = 0;
+
+ Assert(wc->winref <= wflists->maxWinRef);
+
+ /* skip any WindowClauses that have no WindowFuncs */
+ if (wflists->windowFuncs[wc->winref] == NIL)
+ continue;
+
+ foreach(lc2, wflists->windowFuncs[wc->winref])
+ {
+ SupportRequestOptimizeWindowClause req;
+ SupportRequestOptimizeWindowClause *res;
+ WindowFunc *wfunc = lfirst_node(WindowFunc, lc2);
+ Oid prosupport;
+
+ prosupport = get_func_support(wfunc->winfnoid);
+
+ /* Check if there's a support function for 'wfunc' */
+ if (!OidIsValid(prosupport))
+ break; /* can't optimize this WindowClause */
+
+ req.type = T_SupportRequestOptimizeWindowClause;
+ req.window_clause = wc;
+ req.window_func = wfunc;
+ req.frameOptions = wc->frameOptions;
+
+ /* call the support function */
+ res = (SupportRequestOptimizeWindowClause *)
+ DatumGetPointer(OidFunctionCall1(prosupport,
+ PointerGetDatum(&req)));
+
+ /*
+ * Skip to next WindowClause if the support function does not
+ * support this request type.
+ */
+ if (res == NULL)
+ break;
+
+ /*
+ * Save these frameOptions for the first WindowFunc for this
+ * WindowClause.
+ */
+ if (foreach_current_index(lc2) == 0)
+ optimizedFrameOptions = res->frameOptions;
+
+ /*
+ * On subsequent WindowFuncs, if the frameOptions are not the same
+ * then we're unable to optimize the frameOptions for this
+ * WindowClause.
+ */
+ else if (optimizedFrameOptions != res->frameOptions)
+ break; /* skip to the next WindowClause, if any */
+ }
+
+ /* adjust the frameOptions if all WindowFunc's agree that it's ok */
+ if (lc2 == NULL && wc->frameOptions != optimizedFrameOptions)
+ {
+ ListCell *lc3;
+
+ /* apply the new frame options */
+ wc->frameOptions = optimizedFrameOptions;
+
+ /*
+ * We now check to see if changing the frameOptions has caused
+ * this WindowClause to be a duplicate of some other WindowClause.
+ * This can only happen if we have multiple WindowClauses, so
+ * don't bother if there's only 1.
+ */
+ if (list_length(windowClause) == 1)
+ continue;
+
+ /*
+ * Do the duplicate check and reuse the existing WindowClause if
+ * we find a duplicate.
+ */
+ foreach(lc3, windowClause)
+ {
+ WindowClause *existing_wc = lfirst_node(WindowClause, lc3);
+
+ /* skip over the WindowClause we're currently editing */
+ if (existing_wc == wc)
+ continue;
+
+ /*
+ * Perform the same duplicate check that is done in
+ * transformWindowFuncCall.
+ */
+ if (equal(wc->partitionClause, existing_wc->partitionClause) &&
+ equal(wc->orderClause, existing_wc->orderClause) &&
+ wc->frameOptions == existing_wc->frameOptions &&
+ equal(wc->startOffset, existing_wc->startOffset) &&
+ equal(wc->endOffset, existing_wc->endOffset))
+ {
+ ListCell *lc4;
+
+ /*
+ * Now move each WindowFunc in 'wc' into 'existing_wc'.
+ * This required adjusting each WindowFunc's winref and
+ * moving the WindowFuncs in 'wc' to the list of
+ * WindowFuncs in 'existing_wc'.
+ */
+ foreach(lc4, wflists->windowFuncs[wc->winref])
+ {
+ WindowFunc *wfunc = lfirst_node(WindowFunc, lc4);
+
+ wfunc->winref = existing_wc->winref;
+ }
+
+ /* move list items */
+ wflists->windowFuncs[existing_wc->winref] = list_concat(wflists->windowFuncs[existing_wc->winref],
+ wflists->windowFuncs[wc->winref]);
+ wflists->windowFuncs[wc->winref] = NIL;
+
+ /*
+ * transformWindowFuncCall() should have made sure there
+ * are no other duplicates, so we needn't bother looking
+ * any further.
+ */
+ break;
+ }
+ }
+ }
+ }
+}
+
+/*
* select_active_windows
* Create a list of the "active" window clauses (ie, those referenced
* by non-deleted WindowFuncs) in the order they are to be executed.