mirror of
https://github.com/postgres/postgres.git
synced 2025-11-06 07:49:08 +03:00
Support window functions a la SQL:2008.
Hitoshi Harada, with some kibitzing from Heikki and Tom.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.271 2008/12/18 18:20:34 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.272 2008/12/28 18:53:57 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -72,7 +72,9 @@ typedef struct
|
||||
} substitute_actual_srf_parameters_context;
|
||||
|
||||
static bool contain_agg_clause_walker(Node *node, void *context);
|
||||
static bool pull_agg_clause_walker(Node *node, List **context);
|
||||
static bool count_agg_clauses_walker(Node *node, AggClauseCounts *counts);
|
||||
static bool find_window_functions_walker(Node *node, WindowFuncLists *lists);
|
||||
static bool expression_returns_set_rows_walker(Node *node, double *count);
|
||||
static bool contain_subplans_walker(Node *node, void *context);
|
||||
static bool contain_mutable_functions_walker(Node *node, void *context);
|
||||
@@ -388,6 +390,41 @@ contain_agg_clause_walker(Node *node, void *context)
|
||||
return expression_tree_walker(node, contain_agg_clause_walker, context);
|
||||
}
|
||||
|
||||
/*
|
||||
* pull_agg_clause
|
||||
* Recursively search for Aggref nodes within a clause.
|
||||
*
|
||||
* Returns a List of all Aggrefs found.
|
||||
*
|
||||
* This does not descend into subqueries, and so should be used only after
|
||||
* reduction of sublinks to subplans, or in contexts where it's known there
|
||||
* are no subqueries. There mustn't be outer-aggregate references either.
|
||||
*/
|
||||
List *
|
||||
pull_agg_clause(Node *clause)
|
||||
{
|
||||
List *result = NIL;
|
||||
|
||||
(void) pull_agg_clause_walker(clause, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool
|
||||
pull_agg_clause_walker(Node *node, List **context)
|
||||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
if (IsA(node, Aggref))
|
||||
{
|
||||
Assert(((Aggref *) node)->agglevelsup == 0);
|
||||
*context = lappend(*context, node);
|
||||
return false; /* no need to descend into arguments */
|
||||
}
|
||||
Assert(!IsA(node, SubLink));
|
||||
return expression_tree_walker(node, pull_agg_clause_walker,
|
||||
(void *) context);
|
||||
}
|
||||
|
||||
/*
|
||||
* count_agg_clauses
|
||||
* Recursively count the Aggref nodes in an expression tree.
|
||||
@@ -519,6 +556,79 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Window-function clause manipulation
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* contain_window_function
|
||||
* Recursively search for WindowFunc nodes within a clause.
|
||||
*
|
||||
* Since window functions don't have level fields, but are hard-wired to
|
||||
* be associated with the current query level, this is just the same as
|
||||
* rewriteManip.c's function.
|
||||
*/
|
||||
bool
|
||||
contain_window_function(Node *clause)
|
||||
{
|
||||
return checkExprHasWindowFuncs(clause);
|
||||
}
|
||||
|
||||
/*
|
||||
* find_window_functions
|
||||
* Locate all the WindowFunc nodes in an expression tree, and organize
|
||||
* them by winref ID number.
|
||||
*
|
||||
* Caller must provide an upper bound on the winref IDs expected in the tree.
|
||||
*/
|
||||
WindowFuncLists *
|
||||
find_window_functions(Node *clause, Index maxWinRef)
|
||||
{
|
||||
WindowFuncLists *lists = palloc(sizeof(WindowFuncLists));
|
||||
|
||||
lists->numWindowFuncs = 0;
|
||||
lists->maxWinRef = maxWinRef;
|
||||
lists->windowFuncs = (List **) palloc0((maxWinRef + 1) * sizeof(List *));
|
||||
(void) find_window_functions_walker(clause, lists);
|
||||
return lists;
|
||||
}
|
||||
|
||||
static bool
|
||||
find_window_functions_walker(Node *node, WindowFuncLists *lists)
|
||||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
if (IsA(node, WindowFunc))
|
||||
{
|
||||
WindowFunc *wfunc = (WindowFunc *) node;
|
||||
|
||||
/* winref is unsigned, so one-sided test is OK */
|
||||
if (wfunc->winref > lists->maxWinRef)
|
||||
elog(ERROR, "WindowFunc contains out-of-range winref %u",
|
||||
wfunc->winref);
|
||||
lists->windowFuncs[wfunc->winref] =
|
||||
lappend(lists->windowFuncs[wfunc->winref], wfunc);
|
||||
lists->numWindowFuncs++;
|
||||
|
||||
/*
|
||||
* Complain if the window function's arguments contain window functions
|
||||
*/
|
||||
if (contain_window_function((Node *) wfunc->args))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WINDOWING_ERROR),
|
||||
errmsg("window function calls cannot be nested")));
|
||||
|
||||
/*
|
||||
* Having checked that, we need not recurse into the argument.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
Assert(!IsA(node, SubLink));
|
||||
return expression_tree_walker(node, find_window_functions_walker,
|
||||
(void *) lists);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Support for expressions returning sets
|
||||
*****************************************************************************/
|
||||
@@ -567,6 +677,8 @@ expression_returns_set_rows_walker(Node *node, double *count)
|
||||
/* Avoid recursion for some cases that can't return a set */
|
||||
if (IsA(node, Aggref))
|
||||
return false;
|
||||
if (IsA(node, WindowFunc))
|
||||
return false;
|
||||
if (IsA(node, DistinctExpr))
|
||||
return false;
|
||||
if (IsA(node, ScalarArrayOpExpr))
|
||||
@@ -897,6 +1009,11 @@ contain_nonstrict_functions_walker(Node *node, void *context)
|
||||
/* an aggregate could return non-null with null input */
|
||||
return true;
|
||||
}
|
||||
if (IsA(node, WindowFunc))
|
||||
{
|
||||
/* a window function could return non-null with null input */
|
||||
return true;
|
||||
}
|
||||
if (IsA(node, ArrayRef))
|
||||
{
|
||||
/* array assignment is nonstrict, but subscripting is strict */
|
||||
@@ -1589,7 +1706,8 @@ is_strict_saop(ScalarArrayOpExpr *expr, bool falseOK)
|
||||
* not-constant expressions, namely aggregates (Aggrefs). In current usage
|
||||
* this is only applied to WHERE clauses and so a check for Aggrefs would be
|
||||
* a waste of cycles; but be sure to also check contain_agg_clause() if you
|
||||
* want to know about pseudo-constness in other contexts.
|
||||
* want to know about pseudo-constness in other contexts. The same goes
|
||||
* for window functions (WindowFuncs).
|
||||
*/
|
||||
bool
|
||||
is_pseudo_constant_clause(Node *clause)
|
||||
@@ -3472,6 +3590,7 @@ inline_function(Oid funcid, Oid result_type, List *args,
|
||||
querytree->utilityStmt ||
|
||||
querytree->intoClause ||
|
||||
querytree->hasAggs ||
|
||||
querytree->hasWindowFuncs ||
|
||||
querytree->hasSubLinks ||
|
||||
querytree->cteList ||
|
||||
querytree->rtable ||
|
||||
@@ -3479,6 +3598,7 @@ inline_function(Oid funcid, Oid result_type, List *args,
|
||||
querytree->jointree->quals ||
|
||||
querytree->groupClause ||
|
||||
querytree->havingQual ||
|
||||
querytree->windowClause ||
|
||||
querytree->distinctClause ||
|
||||
querytree->sortClause ||
|
||||
querytree->limitOffset ||
|
||||
|
||||
Reference in New Issue
Block a user