diff options
Diffstat (limited to 'src/backend/utils')
-rw-r--r-- | src/backend/utils/adt/Makefile | 4 | ||||
-rw-r--r-- | src/backend/utils/adt/array_userfuncs.c | 35 | ||||
-rw-r--r-- | src/backend/utils/adt/arrayfuncs.c | 11 | ||||
-rw-r--r-- | src/backend/utils/adt/float.c | 14 | ||||
-rw-r--r-- | src/backend/utils/adt/int8.c | 6 | ||||
-rw-r--r-- | src/backend/utils/adt/numeric.c | 18 | ||||
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 298 | ||||
-rw-r--r-- | src/backend/utils/adt/windowfuncs.c | 475 | ||||
-rw-r--r-- | src/backend/utils/fmgr/fmgr.c | 75 | ||||
-rw-r--r-- | src/backend/utils/sort/tuplestore.c | 15 |
10 files changed, 873 insertions, 78 deletions
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile index d7d28c9f15..69dfbbba3b 100644 --- a/src/backend/utils/adt/Makefile +++ b/src/backend/utils/adt/Makefile @@ -1,7 +1,7 @@ # # Makefile for utils/adt # -# $PostgreSQL: pgsql/src/backend/utils/adt/Makefile,v 1.70 2008/11/03 20:17:20 adunstan Exp $ +# $PostgreSQL: pgsql/src/backend/utils/adt/Makefile,v 1.71 2008/12/28 18:53:59 tgl Exp $ # subdir = src/backend/utils/adt @@ -29,7 +29,7 @@ OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \ tsginidx.o tsgistidx.o tsquery.o tsquery_cleanup.o tsquery_gist.o \ tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \ tsvector.o tsvector_op.o tsvector_parser.o \ - txid.o uuid.o xml.o + txid.o uuid.o windowfuncs.o xml.o like.o: like.c like_match.c diff --git a/src/backend/utils/adt/array_userfuncs.c b/src/backend/utils/adt/array_userfuncs.c index 4c147f0021..1401b29359 100644 --- a/src/backend/utils/adt/array_userfuncs.c +++ b/src/backend/utils/adt/array_userfuncs.c @@ -6,7 +6,7 @@ * Copyright (c) 2003-2008, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.26 2008/11/14 02:09:51 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.27 2008/12/28 18:53:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -475,6 +475,7 @@ Datum array_agg_transfn(PG_FUNCTION_ARGS) { Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1); + MemoryContext aggcontext; ArrayBuildState *state; Datum elem; @@ -483,8 +484,16 @@ array_agg_transfn(PG_FUNCTION_ARGS) (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); - /* cannot be called directly because of internal-type argument */ - Assert(fcinfo->context && IsA(fcinfo->context, AggState)); + if (fcinfo->context && IsA(fcinfo->context, AggState)) + aggcontext = ((AggState *) fcinfo->context)->aggcontext; + else if (fcinfo->context && IsA(fcinfo->context, WindowAggState)) + aggcontext = ((WindowAggState *) fcinfo->context)->wincontext; + else + { + /* cannot be called directly because of internal-type argument */ + elog(ERROR, "array_agg_transfn called in non-aggregate context"); + aggcontext = NULL; /* keep compiler quiet */ + } state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0); elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1); @@ -492,7 +501,7 @@ array_agg_transfn(PG_FUNCTION_ARGS) elem, PG_ARGISNULL(1), arg1_typeid, - ((AggState *) fcinfo->context)->aggcontext); + aggcontext); /* * The transition type for array_agg() is declared to be "internal", @@ -506,14 +515,28 @@ array_agg_transfn(PG_FUNCTION_ARGS) Datum array_agg_finalfn(PG_FUNCTION_ARGS) { + Datum result; ArrayBuildState *state; + int dims[1]; + int lbs[1]; /* cannot be called directly because of internal-type argument */ - Assert(fcinfo->context && IsA(fcinfo->context, AggState)); + Assert(fcinfo->context && + (IsA(fcinfo->context, AggState) || + IsA(fcinfo->context, WindowAggState))); if (PG_ARGISNULL(0)) PG_RETURN_NULL(); /* returns null iff no input values */ state = (ArrayBuildState *) PG_GETARG_POINTER(0); - PG_RETURN_ARRAYTYPE_P(makeArrayResult(state, CurrentMemoryContext)); + + dims[0] = state->nelems; + lbs[0] = 1; + + /* Release working state if regular aggregate, but not if window agg */ + result = makeMdArrayResult(state, 1, dims, lbs, + CurrentMemoryContext, + IsA(fcinfo->context, AggState)); + + PG_RETURN_DATUM(result); } diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 4580040d69..a0501b8fa8 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.150 2008/11/14 00:51:46 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.151 2008/12/28 18:53:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -4208,7 +4208,7 @@ makeArrayResult(ArrayBuildState *astate, dims[0] = astate->nelems; lbs[0] = 1; - return makeMdArrayResult(astate, 1, dims, lbs, rcontext); + return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true); } /* @@ -4219,13 +4219,15 @@ makeArrayResult(ArrayBuildState *astate, * * astate is working state (not NULL) * rcontext is where to construct result + * release is true if okay to release working state */ Datum makeMdArrayResult(ArrayBuildState *astate, int ndims, int *dims, int *lbs, - MemoryContext rcontext) + MemoryContext rcontext, + bool release) { ArrayType *result; MemoryContext oldcontext; @@ -4246,7 +4248,8 @@ makeMdArrayResult(ArrayBuildState *astate, MemoryContextSwitchTo(oldcontext); /* Clean up all the junk */ - MemoryContextDelete(astate->mcontext); + if (release) + MemoryContextDelete(astate->mcontext); return PointerGetDatum(result); } diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c index d19f9edb82..1f7ef9af86 100644 --- a/src/backend/utils/adt/float.c +++ b/src/backend/utils/adt/float.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/float.c,v 1.157 2008/05/09 21:31:23 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/float.c,v 1.158 2008/12/28 18:53:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1765,7 +1765,9 @@ float8_accum(PG_FUNCTION_ARGS) * parameter in-place to reduce palloc overhead. Otherwise we construct a * new array with the updated transition data and return it. */ - if (fcinfo->context && IsA(fcinfo->context, AggState)) + if (fcinfo->context && + (IsA(fcinfo->context, AggState) || + IsA(fcinfo->context, WindowAggState))) { transvalues[0] = N; transvalues[1] = sumX; @@ -1818,7 +1820,9 @@ float4_accum(PG_FUNCTION_ARGS) * parameter in-place to reduce palloc overhead. Otherwise we construct a * new array with the updated transition data and return it. */ - if (fcinfo->context && IsA(fcinfo->context, AggState)) + if (fcinfo->context && + (IsA(fcinfo->context, AggState) || + IsA(fcinfo->context, WindowAggState))) { transvalues[0] = N; transvalues[1] = sumX; @@ -2035,7 +2039,9 @@ float8_regr_accum(PG_FUNCTION_ARGS) * parameter in-place to reduce palloc overhead. Otherwise we construct a * new array with the updated transition data and return it. */ - if (fcinfo->context && IsA(fcinfo->context, AggState)) + if (fcinfo->context && + (IsA(fcinfo->context, AggState) || + IsA(fcinfo->context, WindowAggState))) { transvalues[0] = N; transvalues[1] = sumX; diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c index 550c06f5d0..15bfe81aa0 100644 --- a/src/backend/utils/adt/int8.c +++ b/src/backend/utils/adt/int8.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/int8.c,v 1.71 2008/10/05 23:18:37 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/int8.c,v 1.72 2008/12/28 18:53:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -666,7 +666,9 @@ int8inc(PG_FUNCTION_ARGS) * as incorrect, so just ifdef it out.) */ #ifndef USE_FLOAT8_BYVAL /* controls int8 too */ - if (fcinfo->context && IsA(fcinfo->context, AggState)) + if (fcinfo->context && + (IsA(fcinfo->context, AggState) || + IsA(fcinfo->context, WindowAggState))) { int64 *arg = (int64 *) PG_GETARG_POINTER(0); int64 result; diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index c88469fdec..cd42e92ae6 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -14,7 +14,7 @@ * Copyright (c) 1998-2008, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.114 2008/05/09 21:31:23 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.115 2008/12/28 18:53:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2611,7 +2611,9 @@ int2_sum(PG_FUNCTION_ARGS) * as incorrect, so just ifdef it out.) */ #ifndef USE_FLOAT8_BYVAL /* controls int8 too */ - if (fcinfo->context && IsA(fcinfo->context, AggState)) + if (fcinfo->context && + (IsA(fcinfo->context, AggState) || + IsA(fcinfo->context, WindowAggState))) { int64 *oldsum = (int64 *) PG_GETARG_POINTER(0); @@ -2660,7 +2662,9 @@ int4_sum(PG_FUNCTION_ARGS) * as incorrect, so just ifdef it out.) */ #ifndef USE_FLOAT8_BYVAL /* controls int8 too */ - if (fcinfo->context && IsA(fcinfo->context, AggState)) + if (fcinfo->context && + (IsA(fcinfo->context, AggState) || + IsA(fcinfo->context, WindowAggState))) { int64 *oldsum = (int64 *) PG_GETARG_POINTER(0); @@ -2753,7 +2757,9 @@ int2_avg_accum(PG_FUNCTION_ARGS) * parameter in-place to reduce palloc overhead. Otherwise we need to make * a copy of it before scribbling on it. */ - if (fcinfo->context && IsA(fcinfo->context, AggState)) + if (fcinfo->context && + (IsA(fcinfo->context, AggState) || + IsA(fcinfo->context, WindowAggState))) transarray = PG_GETARG_ARRAYTYPE_P(0); else transarray = PG_GETARG_ARRAYTYPE_P_COPY(0); @@ -2781,7 +2787,9 @@ int4_avg_accum(PG_FUNCTION_ARGS) * parameter in-place to reduce palloc overhead. Otherwise we need to make * a copy of it before scribbling on it. */ - if (fcinfo->context && IsA(fcinfo->context, AggState)) + if (fcinfo->context && + (IsA(fcinfo->context, AggState) || + IsA(fcinfo->context, WindowAggState))) transarray = PG_GETARG_ARRAYTYPE_P(0); else transarray = PG_GETARG_ARRAYTYPE_P_COPY(0); diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 444ee7a200..969977cea5 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.290 2008/12/19 05:04:35 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.291 2008/12/28 18:53:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -81,6 +81,8 @@ typedef struct { StringInfo buf; /* output buffer to append to */ List *namespaces; /* List of deparse_namespace nodes */ + List *windowClause; /* Current query level's WINDOW clause */ + List *windowTList; /* targetlist for resolving WINDOW clause */ int prettyFlags; /* enabling of pretty-print functions */ int indentLevel; /* current indent level for prettyprint */ bool varprefix; /* TRUE to print prefixes on Vars */ @@ -167,6 +169,11 @@ static void get_setop_query(Node *setOp, Query *query, static Node *get_rule_sortgroupclause(SortGroupClause *srt, List *tlist, bool force_colno, deparse_context *context); +static void get_rule_orderby(List *orderList, List *targetList, + bool force_colno, deparse_context *context); +static void get_rule_windowclause(Query *query, deparse_context *context); +static void get_rule_windowspec(WindowClause *wc, List *targetList, + deparse_context *context); static void push_plan(deparse_namespace *dpns, Plan *subplan); static char *get_variable(Var *var, int levelsup, bool showstar, deparse_context *context); @@ -183,6 +190,7 @@ static void get_oper_expr(OpExpr *expr, deparse_context *context); static void get_func_expr(FuncExpr *expr, deparse_context *context, bool showimplicit); static void get_agg_expr(Aggref *aggref, deparse_context *context); +static void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context); static void get_coercion_expr(Node *arg, deparse_context *context, Oid resulttype, int32 resulttypmod, Node *parentNode); @@ -1854,6 +1862,8 @@ deparse_expression_pretty(Node *expr, List *dpcontext, initStringInfo(&buf); context.buf = &buf; context.namespaces = dpcontext; + context.windowClause = NIL; + context.windowTList = NIL; context.varprefix = forceprefix; context.prettyFlags = prettyFlags; context.indentLevel = startIndent; @@ -2085,6 +2095,8 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, context.buf = buf; context.namespaces = list_make1(&dpns); + context.windowClause = NIL; + context.windowTList = NIL; context.varprefix = (list_length(query->rtable) != 1); context.prettyFlags = prettyFlags; context.indentLevel = PRETTYINDENT_STD; @@ -2228,6 +2240,8 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace, context.buf = buf; context.namespaces = lcons(&dpns, list_copy(parentnamespace)); + context.windowClause = NIL; + context.windowTList = NIL; context.varprefix = (parentnamespace != NIL || list_length(query->rtable) != 1); context.prettyFlags = prettyFlags; @@ -2392,13 +2406,20 @@ get_select_query_def(Query *query, deparse_context *context, TupleDesc resultDesc) { StringInfo buf = context->buf; + List *save_windowclause; + List *save_windowtlist; bool force_colno; - const char *sep; ListCell *l; /* Insert the WITH clause if given */ get_with_clause(query, context); + /* Set up context for possible window functions */ + save_windowclause = context->windowClause; + context->windowClause = query->windowClause; + save_windowtlist = context->windowTList; + context->windowTList = query->targetList; + /* * If the Query node has a setOperations tree, then it's the top level of * a UNION/INTERSECT/EXCEPT query; only the WITH, ORDER BY and LIMIT @@ -2421,48 +2442,8 @@ get_select_query_def(Query *query, deparse_context *context, { appendContextKeyword(context, " ORDER BY ", -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - sep = ""; - foreach(l, query->sortClause) - { - SortGroupClause *srt = (SortGroupClause *) lfirst(l); - Node *sortexpr; - Oid sortcoltype; - TypeCacheEntry *typentry; - - appendStringInfoString(buf, sep); - sortexpr = get_rule_sortgroupclause(srt, query->targetList, - force_colno, context); - sortcoltype = exprType(sortexpr); - /* See whether operator is default < or > for datatype */ - typentry = lookup_type_cache(sortcoltype, - TYPECACHE_LT_OPR | TYPECACHE_GT_OPR); - if (srt->sortop == typentry->lt_opr) - { - /* ASC is default, so emit nothing for it */ - if (srt->nulls_first) - appendStringInfo(buf, " NULLS FIRST"); - } - else if (srt->sortop == typentry->gt_opr) - { - appendStringInfo(buf, " DESC"); - /* DESC defaults to NULLS FIRST */ - if (!srt->nulls_first) - appendStringInfo(buf, " NULLS LAST"); - } - else - { - appendStringInfo(buf, " USING %s", - generate_operator_name(srt->sortop, - sortcoltype, - sortcoltype)); - /* be specific to eliminate ambiguity */ - if (srt->nulls_first) - appendStringInfo(buf, " NULLS FIRST"); - else - appendStringInfo(buf, " NULLS LAST"); - } - sep = ", "; - } + get_rule_orderby(query->sortClause, query->targetList, + force_colno, context); } /* Add the LIMIT clause if given */ @@ -2500,6 +2481,9 @@ get_select_query_def(Query *query, deparse_context *context, if (rc->noWait) appendStringInfo(buf, " NOWAIT"); } + + context->windowClause = save_windowclause; + context->windowTList = save_windowtlist; } static void @@ -2603,6 +2587,10 @@ get_basic_select_query(Query *query, deparse_context *context, -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); get_rule_expr(query->havingQual, context, false); } + + /* Add the WINDOW clause if needed */ + if (query->windowClause != NIL) + get_rule_windowclause(query, context); } /* ---------- @@ -2807,6 +2795,143 @@ get_rule_sortgroupclause(SortGroupClause *srt, List *tlist, bool force_colno, return expr; } +/* + * Display an ORDER BY list. + */ +static void +get_rule_orderby(List *orderList, List *targetList, + bool force_colno, deparse_context *context) +{ + StringInfo buf = context->buf; + const char *sep; + ListCell *l; + + sep = ""; + foreach(l, orderList) + { + SortGroupClause *srt = (SortGroupClause *) lfirst(l); + Node *sortexpr; + Oid sortcoltype; + TypeCacheEntry *typentry; + + appendStringInfoString(buf, sep); + sortexpr = get_rule_sortgroupclause(srt, targetList, + force_colno, context); + sortcoltype = exprType(sortexpr); + /* See whether operator is default < or > for datatype */ + typentry = lookup_type_cache(sortcoltype, + TYPECACHE_LT_OPR | TYPECACHE_GT_OPR); + if (srt->sortop == typentry->lt_opr) + { + /* ASC is default, so emit nothing for it */ + if (srt->nulls_first) + appendStringInfo(buf, " NULLS FIRST"); + } + else if (srt->sortop == typentry->gt_opr) + { + appendStringInfo(buf, " DESC"); + /* DESC defaults to NULLS FIRST */ + if (!srt->nulls_first) + appendStringInfo(buf, " NULLS LAST"); + } + else + { + appendStringInfo(buf, " USING %s", + generate_operator_name(srt->sortop, + sortcoltype, + sortcoltype)); + /* be specific to eliminate ambiguity */ + if (srt->nulls_first) + appendStringInfo(buf, " NULLS FIRST"); + else + appendStringInfo(buf, " NULLS LAST"); + } + sep = ", "; + } +} + +/* + * Display a WINDOW clause. + * + * Note that the windowClause list might contain only anonymous window + * specifications, in which case we should print nothing here. + */ +static void +get_rule_windowclause(Query *query, deparse_context *context) +{ + StringInfo buf = context->buf; + const char *sep; + ListCell *l; + + sep = NULL; + foreach(l, query->windowClause) + { + WindowClause *wc = (WindowClause *) lfirst(l); + + if (wc->name == NULL) + continue; /* ignore anonymous windows */ + + if (sep == NULL) + appendContextKeyword(context, " WINDOW ", + -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); + else + appendStringInfoString(buf, sep); + + appendStringInfo(buf, "%s AS ", quote_identifier(wc->name)); + + get_rule_windowspec(wc, query->targetList, context); + + sep = ", "; + } +} + +/* + * Display a window definition + */ +static void +get_rule_windowspec(WindowClause *wc, List *targetList, + deparse_context *context) +{ + StringInfo buf = context->buf; + bool needspace = false; + const char *sep; + ListCell *l; + + appendStringInfoChar(buf, '('); + if (wc->refname) + { + appendStringInfoString(buf, quote_identifier(wc->refname)); + needspace = true; + } + /* partitions are always inherited, so only print if no refname */ + if (wc->partitionClause && !wc->refname) + { + if (needspace) + appendStringInfoChar(buf, ' '); + appendStringInfoString(buf, "PARTITION BY "); + sep = ""; + foreach(l, wc->partitionClause) + { + SortGroupClause *grp = (SortGroupClause *) lfirst(l); + + appendStringInfoString(buf, sep); + get_rule_sortgroupclause(grp, targetList, + false, context); + sep = ", "; + } + needspace = true; + } + if (wc->orderClause && !wc->copiedOrder) + { + if (needspace) + appendStringInfoChar(buf, ' '); + appendStringInfoString(buf, "ORDER BY "); + get_rule_orderby(wc->orderClause, targetList, false, context); + needspace = true; + } + appendStringInfoChar(buf, ')'); +} + /* ---------- * get_insert_query_def - Parse back an INSERT parsetree * ---------- @@ -3801,6 +3926,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) case T_XmlExpr: case T_NullIfExpr: case T_Aggref: + case T_WindowFunc: case T_FuncExpr: /* function-like: name(..) or name[..] */ return true; @@ -3916,6 +4042,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) case T_XmlExpr: /* own parentheses */ case T_NullIfExpr: /* other separators */ case T_Aggref: /* own parentheses */ + case T_WindowFunc: /* own parentheses */ case T_CaseExpr: /* other separators */ return true; default: @@ -3965,6 +4092,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) case T_XmlExpr: /* own parentheses */ case T_NullIfExpr: /* other separators */ case T_Aggref: /* own parentheses */ + case T_WindowFunc: /* own parentheses */ case T_CaseExpr: /* other separators */ return true; default: @@ -4093,6 +4221,10 @@ get_rule_expr(Node *node, deparse_context *context, get_agg_expr((Aggref *) node, context); break; + case T_WindowFunc: + get_windowfunc_expr((WindowFunc *) node, context); + break; + case T_ArrayRef: { ArrayRef *aref = (ArrayRef *) node; @@ -4999,13 +5131,13 @@ get_func_expr(FuncExpr *expr, deparse_context *context, * Normal function: display as proname(args). First we need to extract * the argument datatypes. */ + if (list_length(expr->args) > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg("too many arguments"))); nargs = 0; foreach(l, expr->args) { - if (nargs >= FUNC_MAX_ARGS) - ereport(ERROR, - (errcode(ERRCODE_TOO_MANY_ARGUMENTS), - errmsg("too many arguments"))); argtypes[nargs] = exprType((Node *) lfirst(l)); nargs++; } @@ -5036,13 +5168,13 @@ get_agg_expr(Aggref *aggref, deparse_context *context) int nargs; ListCell *l; + if (list_length(aggref->args) > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg("too many arguments"))); nargs = 0; foreach(l, aggref->args) { - if (nargs >= FUNC_MAX_ARGS) - ereport(ERROR, - (errcode(ERRCODE_TOO_MANY_ARGUMENTS), - errmsg("too many arguments"))); argtypes[nargs] = exprType((Node *) lfirst(l)); nargs++; } @@ -5059,6 +5191,64 @@ get_agg_expr(Aggref *aggref, deparse_context *context) appendStringInfoChar(buf, ')'); } +/* + * get_windowfunc_expr - Parse back a WindowFunc node + */ +static void +get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context) +{ + StringInfo buf = context->buf; + Oid argtypes[FUNC_MAX_ARGS]; + int nargs; + ListCell *l; + + if (list_length(wfunc->args) > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg("too many arguments"))); + nargs = 0; + foreach(l, wfunc->args) + { + argtypes[nargs] = exprType((Node *) lfirst(l)); + nargs++; + } + + appendStringInfo(buf, "%s(%s", + generate_function_name(wfunc->winfnoid, + nargs, argtypes, NULL), ""); + /* winstar can be set only in zero-argument aggregates */ + if (wfunc->winstar) + appendStringInfoChar(buf, '*'); + else + get_rule_expr((Node *) wfunc->args, context, true); + appendStringInfoString(buf, ") OVER "); + + foreach(l, context->windowClause) + { + WindowClause *wc = (WindowClause *) lfirst(l); + + if (wc->winref == wfunc->winref) + { + if (wc->name) + appendStringInfoString(buf, quote_identifier(wc->name)); + else + get_rule_windowspec(wc, context->windowTList, context); + break; + } + } + if (l == NULL) + { + if (context->windowClause) + elog(ERROR, "could not find window clause for winref %u", + wfunc->winref); + /* + * In EXPLAIN, we don't have window context information available, + * so we have to settle for this: + */ + appendStringInfoString(buf, "(?)"); + } +} + /* ---------- * get_coercion_expr * @@ -6089,7 +6279,9 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes, NIL, nargs, argtypes, false, true, &p_funcid, &p_rettype, &p_retset, &p_nvargs, &p_true_typeids, NULL); - if ((p_result == FUNCDETAIL_NORMAL || p_result == FUNCDETAIL_AGGREGATE) && + if ((p_result == FUNCDETAIL_NORMAL || + p_result == FUNCDETAIL_AGGREGATE || + p_result == FUNCDETAIL_WINDOWFUNC) && p_funcid == funcid) nspname = NULL; else diff --git a/src/backend/utils/adt/windowfuncs.c b/src/backend/utils/adt/windowfuncs.c new file mode 100644 index 0000000000..a32ea25c06 --- /dev/null +++ b/src/backend/utils/adt/windowfuncs.c @@ -0,0 +1,475 @@ +/*------------------------------------------------------------------------- + * + * windowfuncs.c + * Standard window functions defined in SQL spec. + * + * Portions Copyright (c) 2000-2008, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * $PostgreSQL: pgsql/src/backend/utils/adt/windowfuncs.c,v 1.1 2008/12/28 18:53:59 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "utils/builtins.h" +#include "windowapi.h" + +/* + * ranking process information + */ +typedef struct rank_context +{ + int64 rank; /* current rank */ +} rank_context; + +/* + * ntile process information + */ +typedef struct +{ + int32 ntile; /* current result */ + int64 rows_per_bucket; /* row number of current bucket */ + int64 boundary; /* how many rows should be in the bucket */ + int64 remainder; /* (total rows) % (bucket num) */ +} ntile_context; + +static bool rank_up(WindowObject winobj); +static Datum leadlag_common(FunctionCallInfo fcinfo, + bool forward, bool withoffset, bool withdefault); + + +/* + * utility routine for *_rank functions. + */ +static bool +rank_up(WindowObject winobj) +{ + bool up = false; /* should rank increase? */ + int64 curpos = WinGetCurrentPosition(winobj); + rank_context *context; + + context = (rank_context *) + WinGetPartitionLocalMemory(winobj, sizeof(rank_context)); + + if (context->rank == 0) + { + /* first call: rank of first row is always 1 */ + Assert(curpos == 0); + context->rank = 1; + } + else + { + Assert(curpos > 0); + /* do current and prior tuples match by ORDER BY clause? */ + if (!WinRowsArePeers(winobj, curpos - 1, curpos)) + up = true; + } + + /* We can advance the mark, but only *after* acccess to prior row */ + WinSetMarkPosition(winobj, curpos); + + return up; +} + + +/* + * row_number + * just increment up from 1 until current partition finishes. + */ +Datum +window_row_number(PG_FUNCTION_ARGS) +{ + WindowObject winobj = PG_WINDOW_OBJECT(); + int64 curpos = WinGetCurrentPosition(winobj); + + WinSetMarkPosition(winobj, curpos); + PG_RETURN_INT64(curpos + 1); +} + + +/* + * rank + * Rank changes when key columns change. + * The new rank number is the current row number. + */ +Datum +window_rank(PG_FUNCTION_ARGS) +{ + WindowObject winobj = PG_WINDOW_OBJECT(); + rank_context *context; + bool up; + + up = rank_up(winobj); + context = (rank_context *) + WinGetPartitionLocalMemory(winobj, sizeof(rank_context)); + if (up) + context->rank = WinGetCurrentPosition(winobj) + 1; + + PG_RETURN_INT64(context->rank); +} + +/* + * dense_rank + * Rank increases by 1 when key columns change. + */ +Datum +window_dense_rank(PG_FUNCTION_ARGS) +{ + WindowObject winobj = PG_WINDOW_OBJECT(); + rank_context *context; + bool up; + + up = rank_up(winobj); + context = (rank_context *) + WinGetPartitionLocalMemory(winobj, sizeof(rank_context)); + if (up) + context->rank++; + + PG_RETURN_INT64(context->rank); +} + +/* + * percent_rank + * return fraction between 0 and 1 inclusive, + * which is described as (RK - 1) / (NR - 1), where RK is the current row's + * rank and NR is the total number of rows, per spec. + */ +Datum +window_percent_rank(PG_FUNCTION_ARGS) +{ + WindowObject winobj = PG_WINDOW_OBJECT(); + rank_context *context; + bool up; + int64 totalrows = WinGetPartitionRowCount(winobj); + + Assert(totalrows > 0); + + up = rank_up(winobj); + context = (rank_context *) + WinGetPartitionLocalMemory(winobj, sizeof(rank_context)); + if (up) + context->rank = WinGetCurrentPosition(winobj) + 1; + + /* return zero if there's only one row, per spec */ + if (totalrows <= 1) + PG_RETURN_FLOAT8(0.0); + + PG_RETURN_FLOAT8((float8) (context->rank - 1) / (float8) (totalrows - 1)); +} + +/* + * cume_dist + * return fraction betweeen 0 and 1 inclusive, + * which is described as NP / NR, where NP is the number of rows preceding or + * peers to the current row, and NR is the total number of rows, per spec. + */ +Datum +window_cume_dist(PG_FUNCTION_ARGS) +{ + WindowObject winobj = PG_WINDOW_OBJECT(); + rank_context *context; + bool up; + int64 totalrows = WinGetPartitionRowCount(winobj); + + Assert(totalrows > 0); + + up = rank_up(winobj); + context = (rank_context *) + WinGetPartitionLocalMemory(winobj, sizeof(rank_context)); + if (up || context->rank == 1) + { + /* + * The current row is not peer to prior row or is just the first, + * so count up the number of rows that are peer to the current. + */ + int64 row; + + context->rank = WinGetCurrentPosition(winobj) + 1; + + /* + * start from current + 1 + */ + for (row = context->rank; row < totalrows; row++) + { + if (!WinRowsArePeers(winobj, row - 1, row)) + break; + context->rank++; + } + } + + PG_RETURN_FLOAT8((float8) context->rank / (float8) totalrows); +} + +/* + * ntile + * compute an exact numeric value with scale 0 (zero), + * ranging from 1 (one) to n, per spec. + */ +Datum +window_ntile(PG_FUNCTION_ARGS) +{ + WindowObject winobj = PG_WINDOW_OBJECT(); + ntile_context *context; + + context = (ntile_context *) + WinGetPartitionLocalMemory(winobj, sizeof(ntile_context)); + + if (context->ntile == 0) + { + /* first call */ + int64 total; + int32 nbuckets; + bool isnull; + + total = WinGetPartitionRowCount(winobj); + nbuckets = DatumGetInt32(WinGetFuncArgCurrent(winobj, 0, &isnull)); + + /* + * per spec: + * If NT is the null value, then the result is the null value. + */ + if (isnull) + PG_RETURN_NULL(); + + /* + * per spec: + * If NT is less than or equal to 0 (zero), then an exception + * condition is raised. + */ + if (nbuckets <= 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_ARGUMENT_FOR_NTILE), + errmsg("argument of ntile must be greater than zero"))); + + context->ntile = 1; + context->rows_per_bucket = 0; + context->boundary = total / nbuckets; + if (context->boundary <= 0) + context->boundary = 1; + else + { + /* + * If the total number is not divisible, add 1 row to + * leading buckets. + */ + context->remainder = total % nbuckets; + if (context->remainder != 0) + context->boundary++; + } + } + + context->rows_per_bucket++; + if (context->boundary < context->rows_per_bucket) + { + /* ntile up */ + if (context->remainder != 0 && context->ntile == context->remainder) + { + context->remainder = 0; + context->boundary -= 1; + } + context->ntile += 1; + context->rows_per_bucket = 1; + } + + PG_RETURN_INT32(context->ntile); +} + +/* + * leadlag_common + * common operation of lead() and lag() + * For lead() forward is true, whereas for lag() it is false. + * withoffset indicates we have an offset second argument. + * withdefault indicates we have a default third argument. + */ +static Datum +leadlag_common(FunctionCallInfo fcinfo, + bool forward, bool withoffset, bool withdefault) +{ + WindowObject winobj = PG_WINDOW_OBJECT(); + int32 offset; + bool const_offset; + Datum result; + bool isnull; + bool isout; + + if (withoffset) + { + offset = DatumGetInt32(WinGetFuncArgCurrent(winobj, 1, &isnull)); + if (isnull) + PG_RETURN_NULL(); + const_offset = get_fn_expr_arg_stable(fcinfo->flinfo, 1); + } + else + { + offset = 1; + const_offset = true; + } + + result = WinGetFuncArgInPartition(winobj, 0, + (forward ? offset : -offset), + WINDOW_SEEK_CURRENT, + const_offset, + &isnull, &isout); + + if (isout) + { + /* + * target row is out of the partition; supply default value if + * provided. otherwise it'll stay NULL + */ + if (withdefault) + result = WinGetFuncArgCurrent(winobj, 2, &isnull); + } + + if (isnull) + PG_RETURN_NULL(); + + PG_RETURN_DATUM(result); +} + +/* + * lag + * returns the value of VE evaluated on a row that is 1 + * row before the current row within a partition, + * per spec. + */ +Datum +window_lag(PG_FUNCTION_ARGS) +{ + return leadlag_common(fcinfo, false, false, false); +} + +/* + * lag_with_offset + * returns the value of VE evelulated on a row that is OFFSET + * rows before the current row within a partition, + * per spec. + */ +Datum +window_lag_with_offset(PG_FUNCTION_ARGS) +{ + return leadlag_common(fcinfo, false, true, false); +} + +/* + * lag_with_offset_and_default + * same as lag_with_offset but accepts default value + * as its third argument. + */ +Datum +window_lag_with_offset_and_default(PG_FUNCTION_ARGS) +{ + return leadlag_common(fcinfo, false, true, true); +} + +/* + * lead + * returns the value of VE evaluated on a row that is 1 + * row after the current row within a partition, + * per spec. + */ +Datum +window_lead(PG_FUNCTION_ARGS) +{ + return leadlag_common(fcinfo, true, false, false); +} + +/* + * lead_with_offset + * returns the value of VE evaluated on a row that is OFFSET + * number of rows after the current row within a partition, + * per spec. + */ +Datum +window_lead_with_offset(PG_FUNCTION_ARGS) +{ + return leadlag_common(fcinfo, true, true, false); +} + +/* + * lead_with_offset_and_default + * same as lead_with_offset but accepts default value + * as its third argument. + */ +Datum +window_lead_with_offset_and_default(PG_FUNCTION_ARGS) +{ + return leadlag_common(fcinfo, true, true, true); +} + +/* + * first_value + * return the value of VE evaluated on the first row of the + * window frame, per spec. + */ +Datum +window_first_value(PG_FUNCTION_ARGS) +{ + WindowObject winobj = PG_WINDOW_OBJECT(); + Datum result; + bool isnull; + + result = WinGetFuncArgInFrame(winobj, 0, + 0, WINDOW_SEEK_HEAD, true, + &isnull, NULL); + if (isnull) + PG_RETURN_NULL(); + + PG_RETURN_DATUM(result); +} + +/* + * last_value + * return the value of VE evaluated on the last row of the + * window frame, per spec. + */ +Datum +window_last_value(PG_FUNCTION_ARGS) +{ + WindowObject winobj = PG_WINDOW_OBJECT(); + Datum result; + bool isnull; + + result = WinGetFuncArgInFrame(winobj, 0, + 0, WINDOW_SEEK_TAIL, true, + &isnull, NULL); + if (isnull) + PG_RETURN_NULL(); + + PG_RETURN_DATUM(result); +} + +/* + * nth_value + * return the value of VE evaluated on the n-th row from the first + * row of the window frame, per spec. + */ +Datum +window_nth_value(PG_FUNCTION_ARGS) +{ + WindowObject winobj = PG_WINDOW_OBJECT(); + bool const_offset; + Datum result; + bool isnull; + int32 nth; + + nth = DatumGetInt32(WinGetFuncArgCurrent(winobj, 1, &isnull)); + if (isnull) + PG_RETURN_NULL(); + const_offset = get_fn_expr_arg_stable(fcinfo->flinfo, 1); + + if (nth <= 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_ARGUMENT_FOR_NTH_VALUE), + errmsg("argument of nth_value must be greater than zero"))); + + result = WinGetFuncArgInFrame(winobj, 0, + nth - 1, WINDOW_SEEK_HEAD, const_offset, + &isnull, NULL); + if (isnull) + PG_RETURN_NULL(); + + PG_RETURN_DATUM(result); +} diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index 82dbb8e970..de09419542 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.122 2008/08/25 22:42:34 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.123 2008/12/28 18:53:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2218,6 +2218,7 @@ pg_detoast_datum_packed(struct varlena * datum) * * These are needed by polymorphic functions, which accept multiple possible * input types and need help from the parser to know what they've got. + * Also, some functions might be interested in whether a parameter is constant. *------------------------------------------------------------------------- */ @@ -2288,6 +2289,8 @@ get_call_expr_argtype(Node *expr, int argnum) args = list_make1(((ArrayCoerceExpr *) expr)->arg); else if (IsA(expr, NullIfExpr)) args = ((NullIfExpr *) expr)->args; + else if (IsA(expr, WindowFunc)) + args = ((WindowFunc *) expr)->args; else return InvalidOid; @@ -2310,3 +2313,73 @@ get_call_expr_argtype(Node *expr, int argnum) return argtype; } + +/* + * Find out whether a specific function argument is constant for the + * duration of a query + * + * Returns false if information is not available + */ +bool +get_fn_expr_arg_stable(FmgrInfo *flinfo, int argnum) +{ + /* + * can't return anything useful if we have no FmgrInfo or if its fn_expr + * node has not been initialized + */ + if (!flinfo || !flinfo->fn_expr) + return false; + + return get_call_expr_arg_stable(flinfo->fn_expr, argnum); +} + +/* + * Find out whether a specific function argument is constant for the + * duration of a query, but working from the calling expression tree + * + * Returns false if information is not available + */ +bool +get_call_expr_arg_stable(Node *expr, int argnum) +{ + List *args; + Node *arg; + + if (expr == NULL) + return false; + + if (IsA(expr, FuncExpr)) + args = ((FuncExpr *) expr)->args; + else if (IsA(expr, OpExpr)) + args = ((OpExpr *) expr)->args; + else if (IsA(expr, DistinctExpr)) + args = ((DistinctExpr *) expr)->args; + else if (IsA(expr, ScalarArrayOpExpr)) + args = ((ScalarArrayOpExpr *) expr)->args; + else if (IsA(expr, ArrayCoerceExpr)) + args = list_make1(((ArrayCoerceExpr *) expr)->arg); + else if (IsA(expr, NullIfExpr)) + args = ((NullIfExpr *) expr)->args; + else if (IsA(expr, WindowFunc)) + args = ((WindowFunc *) expr)->args; + else + return false; + + if (argnum < 0 || argnum >= list_length(args)) + return false; + + arg = (Node *) list_nth(args, argnum); + + /* + * Either a true Const or an external Param will have a value that + * doesn't change during the execution of the query. In future we + * might want to consider other cases too, e.g. now(). + */ + if (IsA(arg, Const)) + return true; + if (IsA(arg, Param) && + ((Param *) arg)->paramkind == PARAM_EXTERN) + return true; + + return false; +} diff --git a/src/backend/utils/sort/tuplestore.c b/src/backend/utils/sort/tuplestore.c index 00bed7e139..543114cb17 100644 --- a/src/backend/utils/sort/tuplestore.c +++ b/src/backend/utils/sort/tuplestore.c @@ -47,7 +47,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/sort/tuplestore.c,v 1.44 2008/12/27 17:39:00 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/sort/tuplestore.c,v 1.45 2008/12/28 18:53:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1148,6 +1148,19 @@ tuplestore_trim(Tuplestorestate *state) state->truncated = true; } +/* + * tuplestore_in_memory + * + * Returns true if the tuplestore has not spilled to disk. + * + * XXX exposing this is a violation of modularity ... should get rid of it. + */ +bool +tuplestore_in_memory(Tuplestorestate *state) +{ + return (state->status == TSS_INMEM); +} + /* * Tape interface routines |