? src/backend/executor/nodeWindow.c ? src/include/executor/nodeWindow.h Index: src/backend/commands/explain.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/commands/explain.c,v retrieving revision 1.175 diff -c -r1.175 explain.c *** src/backend/commands/explain.c 14 May 2008 19:10:29 -0000 1.175 --- src/backend/commands/explain.c 28 Jul 2008 08:14:09 -0000 *************** *** 580,585 **** --- 580,588 ---- case T_Limit: pname = "Limit"; break; + case T_Window: + pname = "Window"; + break; case T_Hash: pname = "Hash"; break; *************** *** 831,836 **** --- 834,841 ---- show_sort_info((SortState *) planstate, str, indent, es); break; + case T_Window: + break; case T_Result: show_upper_qual((List *) ((Result *) plan)->resconstantqual, "One-Time Filter", plan, Index: src/backend/executor/Makefile =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/executor/Makefile,v retrieving revision 1.27 diff -c -r1.27 Makefile *** src/backend/executor/Makefile 19 Feb 2008 10:30:07 -0000 1.27 --- src/backend/executor/Makefile 28 Jul 2008 08:14:09 -0000 *************** *** 19,25 **** nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeHash.o \ nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \ nodeNestloop.o nodeFunctionscan.o nodeResult.o nodeSeqscan.o \ ! nodeSetOp.o nodeSort.o nodeUnique.o \ nodeValuesscan.o nodeLimit.o nodeGroup.o \ nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o tstoreReceiver.o spi.o --- 19,25 ---- nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeHash.o \ nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \ nodeNestloop.o nodeFunctionscan.o nodeResult.o nodeSeqscan.o \ ! nodeSetOp.o nodeSort.o nodeUnique.o nodeWindow.o\ nodeValuesscan.o nodeLimit.o nodeGroup.o \ nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o tstoreReceiver.o spi.o Index: src/backend/executor/execAmi.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/executor/execAmi.c,v retrieving revision 1.96 diff -c -r1.96 execAmi.c *** src/backend/executor/execAmi.c 26 Jul 2008 19:15:35 -0000 1.96 --- src/backend/executor/execAmi.c 28 Jul 2008 08:14:09 -0000 *************** *** 39,44 **** --- 39,45 ---- #include "executor/nodeTidscan.h" #include "executor/nodeUnique.h" #include "executor/nodeValuesscan.h" + #include "executor/nodeWindow.h" /* *************** *** 205,210 **** --- 206,215 ---- ExecReScanLimit((LimitState *) node, exprCtxt); break; + case T_WindowState: + ExecReScanWindow((WindowState *) node, exprCtxt); + break; + default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); break; Index: src/backend/executor/execProcnode.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/executor/execProcnode.c,v retrieving revision 1.62 diff -c -r1.62 execProcnode.c *** src/backend/executor/execProcnode.c 1 Jan 2008 19:45:49 -0000 1.62 --- src/backend/executor/execProcnode.c 28 Jul 2008 08:14:09 -0000 *************** *** 103,108 **** --- 103,109 ---- #include "executor/nodeTidscan.h" #include "executor/nodeUnique.h" #include "executor/nodeValuesscan.h" + #include "executor/nodeWindow.h" #include "miscadmin.h" /* ------------------------------------------------------------------------ *************** *** 261,266 **** --- 262,272 ---- estate, eflags); break; + case T_Window: + result = (PlanState *) ExecInitWindow((Window *) node, + estate, eflags); + break; + default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); result = NULL; /* keep compiler quiet */ *************** *** 410,415 **** --- 416,425 ---- result = ExecLimit((LimitState *) node); break; + case T_WindowState: + result = ExecWindow((WindowState *) node); + break; + default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); result = NULL; *************** *** 573,578 **** --- 583,592 ---- case T_Limit: return ExecCountSlotsLimit((Limit *) node); + case T_Window: + return ExecCountSlotsWindow((Window *) node); + break; + default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); break; *************** *** 713,718 **** --- 727,736 ---- ExecEndLimit((LimitState *) node); break; + case T_WindowState: + ExecEndWindow((WindowState *) node); + break; + default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); break; Index: src/backend/executor/execQual.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/executor/execQual.c,v retrieving revision 1.231 diff -c -r1.231 execQual.c *** src/backend/executor/execQual.c 15 May 2008 00:17:39 -0000 1.231 --- src/backend/executor/execQual.c 28 Jul 2008 08:14:09 -0000 *************** *** 62,67 **** --- 62,70 ---- static Datum ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); + static Datum ExecEvalWinAggref(WinAggrefExprState *winagg, + ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalVar(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext, *************** *** 433,438 **** --- 436,465 ---- } /* ---------------------------------------------------------------- + * ExecEvalWinAggref + * + * Returns a Datum whose value is the value of the precomputed + * aggregate found in the given expression context. + * + * Note: exct_winaggvalues may be replaced by exct_aggvalues + * because Window node never handles exct_aggvalues currently. + * ---------------------------------------------------------------- + */ + static Datum + ExecEvalWinAggref(WinAggrefExprState *winagg, ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone) + { + if (isDone) + *isDone = ExprSingleResult; + + if (econtext->ecxt_winaggvalues == NULL) /* safety check */ + elog(ERROR, "no aggregates in this expression context"); + + *isNull = econtext->ecxt_winaggnulls[winagg->aggno]; + return econtext->ecxt_winaggvalues[winagg->aggno]; + } + + /* ---------------------------------------------------------------- * ExecEvalVar * * Returns a Datum whose value is the value of a range *************** *** 3844,3849 **** --- 3871,3901 ---- state = (ExprState *) astate; } break; + case T_WinAggref: + { + WinAggref *winagg = (WinAggref *) node; + WinAggrefExprState *wstate = makeNode(WinAggrefExprState); + + wstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalWinAggref; + if (parent && IsA(parent, WindowState)) + { + WindowState *winstate = (WindowState *) parent; + int naggs; + + winstate->aggs = lcons(wstate, winstate->aggs); + naggs = ++winstate->numaggs; + + wstate->args = (List *) ExecInitExpr((Expr *) winagg->args, + parent); + } + else + { + /* planner messed up */ + elog(ERROR, "winaggref found in non-Window plan node"); + } + state = (ExprState *) wstate; + } + break; case T_ArrayRef: { ArrayRef *aref = (ArrayRef *) node; Index: src/backend/nodes/copyfuncs.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v retrieving revision 1.395 diff -c -r1.395 copyfuncs.c *** src/backend/nodes/copyfuncs.c 16 Jul 2008 01:30:22 -0000 1.395 --- src/backend/nodes/copyfuncs.c 28 Jul 2008 08:14:09 -0000 *************** *** 682,687 **** --- 682,710 ---- return newnode; } + /* + * _copyWindow + */ + static Window * + _copyWindow(Window *from) + { + Window *newnode = makeNode(Window); + + CopyPlanFields((Plan *) from, (Plan *) newnode); + + COPY_SCALAR_FIELD(prtNumCols); + COPY_POINTER_FIELD(prtColIdx, from->prtNumCols * sizeof(AttrNumber)); + COPY_POINTER_FIELD(prtOperators, from->prtNumCols * sizeof(Oid)); + COPY_SCALAR_FIELD(ordNumCols); + COPY_POINTER_FIELD(ordColIdx, from->ordNumCols * sizeof(AttrNumber)); + COPY_NODE_FIELD(preceding); + COPY_NODE_FIELD(following); + COPY_SCALAR_FIELD(winref); + + return newnode; + } + + /* **************************************************************** * primnodes.h copy functions * **************************************************************** *************** *** 833,838 **** --- 856,880 ---- } /* + * _copyWinAggref + */ + static WinAggref * + _copyWinAggref(WinAggref *from) + { + WinAggref *newnode = makeNode(WinAggref); + + COPY_SCALAR_FIELD(aggfnoid); + COPY_SCALAR_FIELD(aggtype); + COPY_NODE_FIELD(args); + COPY_SCALAR_FIELD(agglevelsup); + COPY_SCALAR_FIELD(aggstar); + COPY_SCALAR_FIELD(aggdistinct); + COPY_SCALAR_FIELD(winref); + + return newnode; + } + + /* * _copyArrayRef */ static ArrayRef * *************** *** 1566,1571 **** --- 1608,1661 ---- return newnode; } + static OrderClause * + _copyOrderClause(OrderClause *from) + { + OrderClause *newnode = makeNode(OrderClause); + + COPY_SCALAR_FIELD(tleSortGroupRef); + COPY_SCALAR_FIELD(sortop); + COPY_SCALAR_FIELD(nulls_first); + + return newnode; + } + + static PartitionClause * + _copyPartitionClause(PartitionClause *from) + { + PartitionClause *newnode = makeNode(PartitionClause); + + COPY_SCALAR_FIELD(tleSortGroupRef); + COPY_SCALAR_FIELD(sortop); + COPY_SCALAR_FIELD(nulls_first); + + return newnode; + } + + static WinDef * + _copyWinDef(WinDef *from) + { + WinDef *newnode = makeNode(WinDef); + + COPY_NODE_FIELD(partitionClause); + COPY_NODE_FIELD(orderClause); + COPY_NODE_FIELD(expr_list); + + return newnode; + } + + static WindowClause * + _copyWindowClause(WindowClause *from) + { + WindowClause *newnode = makeNode(WindowClause); + + COPY_NODE_FIELD(partitionClause); + COPY_NODE_FIELD(orderClause); + COPY_SCALAR_FIELD(winref); + + return newnode; + } + static RowMarkClause * _copyRowMarkClause(RowMarkClause *from) { *************** *** 1652,1657 **** --- 1742,1748 ---- COPY_SCALAR_FIELD(agg_star); COPY_SCALAR_FIELD(agg_distinct); COPY_SCALAR_FIELD(func_variadic); + COPY_NODE_FIELD(win_definition); COPY_SCALAR_FIELD(location); return newnode; *************** *** 1861,1866 **** --- 1952,1958 ---- COPY_NODE_FIELD(intoClause); COPY_SCALAR_FIELD(hasAggs); COPY_SCALAR_FIELD(hasSubLinks); + COPY_SCALAR_FIELD(hasWindow); COPY_NODE_FIELD(rtable); COPY_NODE_FIELD(jointree); COPY_NODE_FIELD(targetList); *************** *** 1869,1874 **** --- 1961,1967 ---- COPY_NODE_FIELD(havingQual); COPY_NODE_FIELD(distinctClause); COPY_NODE_FIELD(sortClause); + COPY_NODE_FIELD(windowList); COPY_NODE_FIELD(limitOffset); COPY_NODE_FIELD(limitCount); COPY_NODE_FIELD(rowMarks); *************** *** 3102,3107 **** --- 3195,3203 ---- case T_Limit: retval = _copyLimit(from); break; + case T_Window: + retval = _copyWindow(from); + break; /* * PRIMITIVE NODES *************** *** 3127,3132 **** --- 3223,3231 ---- case T_Aggref: retval = _copyAggref(from); break; + case T_WinAggref: + retval = _copyWinAggref(from); + break; case T_ArrayRef: retval = _copyArrayRef(from); break; *************** *** 3592,3597 **** --- 3691,3708 ---- case T_GroupClause: retval = _copyGroupClause(from); break; + case T_OrderClause: + retval = _copyOrderClause(from); + break; + case T_PartitionClause: + retval = _copyPartitionClause(from); + break; + case T_WinDef: + retval = _copyWinDef(from); + break; + case T_WindowClause: + retval = _copyWindowClause(from); + break; case T_RowMarkClause: retval = _copyRowMarkClause(from); break; Index: src/backend/nodes/equalfuncs.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v retrieving revision 1.324 diff -c -r1.324 equalfuncs.c *** src/backend/nodes/equalfuncs.c 16 Jul 2008 01:30:22 -0000 1.324 --- src/backend/nodes/equalfuncs.c 28 Jul 2008 08:14:09 -0000 *************** *** 179,184 **** --- 179,198 ---- } static bool + _equalWinAggref(WinAggref *a, WinAggref *b) + { + COMPARE_SCALAR_FIELD(aggfnoid); + COMPARE_SCALAR_FIELD(aggtype); + COMPARE_NODE_FIELD(args); + COMPARE_SCALAR_FIELD(agglevelsup); + COMPARE_SCALAR_FIELD(aggstar); + COMPARE_SCALAR_FIELD(aggdistinct); + COMPARE_SCALAR_FIELD(winref); + + return true; + } + + static bool _equalArrayRef(ArrayRef *a, ArrayRef *b) { COMPARE_SCALAR_FIELD(refarraytype); *************** *** 1706,1711 **** --- 1720,1726 ---- COMPARE_SCALAR_FIELD(agg_star); COMPARE_SCALAR_FIELD(agg_distinct); COMPARE_SCALAR_FIELD(func_variadic); + COMPARE_NODE_FIELD(win_definition); COMPARE_SCALAR_FIELD(location); return true; *************** *** 1895,1900 **** --- 1910,1935 ---- } static bool + _equalWinDef(WinDef *a, WinDef *b) + { + COMPARE_NODE_FIELD(partitionClause); + COMPARE_NODE_FIELD(orderClause); + COMPARE_NODE_FIELD(expr_list); + COMPARE_SCALAR_FIELD(location); + + return true; + } + + static bool + _equalWindowClause(WindowClause *a, WindowClause *b) + { + COMPARE_NODE_FIELD(partitionClause); + COMPARE_NODE_FIELD(orderClause); + + return true; + } + + static bool _equalRowMarkClause(RowMarkClause *a, RowMarkClause *b) { COMPARE_SCALAR_FIELD(rti); *************** *** 2069,2074 **** --- 2104,2111 ---- break; case T_Aggref: retval = _equalAggref(a, b); + case T_WinAggref: + retval = _equalWinAggref(a, b); break; case T_ArrayRef: retval = _equalArrayRef(a, b); *************** *** 2172,2177 **** --- 2209,2217 ---- case T_JoinExpr: retval = _equalJoinExpr(a, b); break; + case T_WindowClause: + retval = _equalWindowClause(a, b); + break; /* * RELATION NODES *************** *** 2519,2527 **** --- 2559,2572 ---- retval = _equalSortClause(a, b); break; case T_GroupClause: + case T_OrderClause: + case T_PartitionClause: /* GroupClause is equivalent to SortClause */ retval = _equalSortClause(a, b); break; + case T_WinDef: + retval = _equalWinDef(a, b); + break; case T_RowMarkClause: retval = _equalRowMarkClause(a, b); break; Index: src/backend/nodes/outfuncs.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/nodes/outfuncs.c,v retrieving revision 1.328 diff -c -r1.328 outfuncs.c *** src/backend/nodes/outfuncs.c 17 Jul 2008 16:02:12 -0000 1.328 --- src/backend/nodes/outfuncs.c 28 Jul 2008 08:14:10 -0000 *************** *** 624,629 **** --- 624,651 ---- } static void + _outWindow(StringInfo str, Window *node) + { + int i; + + WRITE_NODE_TYPE("WINDOW"); + + _outPlanInfo(str, (Plan *) node); + + appendStringInfo(str, " :prtColIdx"); + for (i = 0; i < node->prtNumCols; i++) + appendStringInfo(str, " %d", node->prtColIdx[i]); + + appendStringInfo(str, " :prtOperations"); + for (i = 0; i < node->prtNumCols; i++) + appendStringInfo(str, " %d", node->prtOperators[i]); + + appendStringInfo(str, " :ordColIdx"); + for (i = 0; i< node->ordNumCols; i++) + appendStringInfo(str, " %d", node->ordColIdx[i]); + } + + static void _outHash(StringInfo str, Hash *node) { WRITE_NODE_TYPE("HASH"); *************** *** 731,736 **** --- 753,772 ---- } static void + _outWinAggref(StringInfo str, WinAggref *node) + { + WRITE_NODE_TYPE("WINAGGREF"); + + WRITE_OID_FIELD(aggfnoid); + WRITE_OID_FIELD(aggtype); + WRITE_NODE_FIELD(args); + WRITE_UINT_FIELD(agglevelsup); + WRITE_BOOL_FIELD(aggstar); + WRITE_BOOL_FIELD(aggdistinct); + WRITE_UINT_FIELD(winref); + } + + static void _outArrayRef(StringInfo str, ArrayRef *node) { WRITE_NODE_TYPE("ARRAYREF"); *************** *** 1611,1616 **** --- 1647,1653 ---- WRITE_BOOL_FIELD(agg_star); WRITE_BOOL_FIELD(agg_distinct); WRITE_BOOL_FIELD(func_variadic); + WRITE_NODE_FIELD(win_definition); WRITE_INT_FIELD(location); } *************** *** 1732,1737 **** --- 1769,1775 ---- WRITE_NODE_FIELD(intoClause); WRITE_BOOL_FIELD(hasAggs); WRITE_BOOL_FIELD(hasSubLinks); + WRITE_BOOL_FIELD(hasWindow); WRITE_NODE_FIELD(rtable); WRITE_NODE_FIELD(jointree); WRITE_NODE_FIELD(targetList); *************** *** 1740,1745 **** --- 1778,1784 ---- WRITE_NODE_FIELD(havingQual); WRITE_NODE_FIELD(distinctClause); WRITE_NODE_FIELD(sortClause); + WRITE_NODE_FIELD(windowList); WRITE_NODE_FIELD(limitOffset); WRITE_NODE_FIELD(limitCount); WRITE_NODE_FIELD(rowMarks); *************** *** 1767,1772 **** --- 1806,1853 ---- } static void + _outOrderClause(StringInfo str, OrderClause *node) + { + WRITE_NODE_TYPE("ORDERCLAUSE"); + + WRITE_UINT_FIELD(tleSortGroupRef); + WRITE_OID_FIELD(sortop); + WRITE_BOOL_FIELD(nulls_first); + } + + static void + _outPartitionClause(StringInfo str, PartitionClause *node) + { + WRITE_NODE_TYPE("PARTITIONCLAUSE"); + + WRITE_UINT_FIELD(tleSortGroupRef); + WRITE_OID_FIELD(sortop); + WRITE_BOOL_FIELD(nulls_first); + } + + static void + _outWinDef(StringInfo str, WinDef *node) + { + WRITE_NODE_TYPE("WINDEF"); + + WRITE_NODE_FIELD(partitionClause); + WRITE_NODE_FIELD(orderClause); + WRITE_NODE_FIELD(expr_list); + WRITE_UINT_FIELD(location); + } + + static void + _outWindowClause(StringInfo str, WindowClause *node) + { + WRITE_NODE_TYPE("WINDOWCLAUSE"); + + WRITE_NODE_FIELD(partitionClause); + WRITE_NODE_FIELD(orderClause); + WRITE_UINT_FIELD(winref); + } + + + static void _outRowMarkClause(StringInfo str, RowMarkClause *node) { WRITE_NODE_TYPE("ROWMARKCLAUSE"); *************** *** 2162,2167 **** --- 2243,2251 ---- case T_Limit: _outLimit(str, obj); break; + case T_Window: + _outWindow(str, obj); + break; case T_Hash: _outHash(str, obj); break; *************** *** 2186,2191 **** --- 2270,2278 ---- case T_Aggref: _outAggref(str, obj); break; + case T_WinAggref: + _outWinAggref(str, obj); + break; case T_ArrayRef: _outArrayRef(str, obj); break; *************** *** 2404,2409 **** --- 2491,2508 ---- case T_GroupClause: _outGroupClause(str, obj); break; + case T_OrderClause: + _outOrderClause(str, obj); + break; + case T_PartitionClause: + _outPartitionClause(str, obj); + break; + case T_WinDef: + _outWinDef(str, obj); + break; + case T_WindowClause: + _outWindowClause(str, obj); + break; case T_RowMarkClause: _outRowMarkClause(str, obj); break; Index: src/backend/nodes/readfuncs.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/nodes/readfuncs.c,v retrieving revision 1.210 diff -c -r1.210 readfuncs.c *** src/backend/nodes/readfuncs.c 1 Jan 2008 19:45:50 -0000 1.210 --- src/backend/nodes/readfuncs.c 28 Jul 2008 08:14:10 -0000 *************** *** 142,147 **** --- 142,148 ---- READ_NODE_FIELD(intoClause); READ_BOOL_FIELD(hasAggs); READ_BOOL_FIELD(hasSubLinks); + READ_BOOL_FIELD(hasWindow); READ_NODE_FIELD(rtable); READ_NODE_FIELD(jointree); READ_NODE_FIELD(targetList); *************** *** 150,155 **** --- 151,157 ---- READ_NODE_FIELD(havingQual); READ_NODE_FIELD(distinctClause); READ_NODE_FIELD(sortClause); + READ_NODE_FIELD(windowList); READ_NODE_FIELD(limitOffset); READ_NODE_FIELD(limitCount); READ_NODE_FIELD(rowMarks); *************** *** 217,222 **** --- 219,284 ---- } /* + * _readOrderClause + */ + static OrderClause * + _readOrderClause(void) + { + READ_LOCALS(OrderClause); + + READ_UINT_FIELD(tleSortGroupRef); + READ_OID_FIELD(sortop); + READ_BOOL_FIELD(nulls_first); + + READ_DONE(); + } + + /* + * _readPartitionClause + */ + static PartitionClause * + _readPartitionClause(void) + { + READ_LOCALS(PartitionClause); + + READ_UINT_FIELD(tleSortGroupRef); + READ_OID_FIELD(sortop); + READ_BOOL_FIELD(nulls_first); + + READ_DONE(); + } + + /* + * _readWinDef + */ + static WinDef * + _readWinDef(void) + { + READ_LOCALS(WinDef); + + READ_NODE_FIELD(partitionClause); + READ_NODE_FIELD(orderClause); + READ_NODE_FIELD(expr_list); + + READ_DONE(); + } + + /* + * _readWindowClause + */ + static WindowClause * + _readWindowClause(void) + { + READ_LOCALS(WindowClause); + + READ_NODE_FIELD(partitionClause); + READ_NODE_FIELD(orderClause); + READ_UINT_FIELD(winref); + + READ_DONE(); + } + + /* * _readRowMarkClause */ static RowMarkClause * *************** *** 1034,1039 **** --- 1096,1109 ---- return_value = _readSortClause(); else if (MATCH("GROUPCLAUSE", 11)) return_value = _readGroupClause(); + else if (MATCH("ORDERCLAUSE", 11)) + return_value = _readOrderClause(); + else if (MATCH("PARTITIONCLAUSE", 15)) + return_value = _readPartitionClause(); + else if (MATCH("WINDEF", 6)) + return_value = _readWinDef(); + else if (MATCH("WINDOWCLAUSE", 12)) + return_value = _readWindowClause(); else if (MATCH("ROWMARKCLAUSE", 13)) return_value = _readRowMarkClause(); else if (MATCH("SETOPERATIONSTMT", 16)) Index: src/backend/optimizer/path/allpaths.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v retrieving revision 1.171 diff -c -r1.171 allpaths.c *** src/backend/optimizer/path/allpaths.c 27 Jun 2008 03:56:55 -0000 1.171 --- src/backend/optimizer/path/allpaths.c 28 Jul 2008 08:14:11 -0000 *************** *** 825,831 **** * * Conditions checked here: * ! * 1. If the subquery has a LIMIT clause, we must not push down any quals, * since that could change the set of rows returned. * * 2. If the subquery contains EXCEPT or EXCEPT ALL set ops we cannot push --- 825,831 ---- * * Conditions checked here: * ! * 1. If the subquery has a LIMIT or Window clause, we must not push down any quals, * since that could change the set of rows returned. * * 2. If the subquery contains EXCEPT or EXCEPT ALL set ops we cannot push *************** *** 846,852 **** SetOperationStmt *topop; /* Check point 1 */ ! if (subquery->limitOffset != NULL || subquery->limitCount != NULL) return false; /* Are we at top level, or looking at a setop component? */ --- 846,853 ---- SetOperationStmt *topop; /* Check point 1 */ ! if (subquery->limitOffset != NULL || subquery->limitCount != NULL ! || subquery->hasWindow) return false; /* Are we at top level, or looking at a setop component? */ Index: src/backend/optimizer/plan/createplan.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v retrieving revision 1.241 diff -c -r1.241 createplan.c *** src/backend/optimizer/plan/createplan.c 27 Jun 2008 03:56:55 -0000 1.241 --- src/backend/optimizer/plan/createplan.c 28 Jul 2008 08:14:12 -0000 *************** *** 3242,3247 **** --- 3242,3344 ---- return node; } + /* + * make_window + * Almost same as make_agg in the meaning of partition columns + */ + Window * + make_window(PlannerInfo *root, + List *tlist, + WindowClause *parse, + Oid *prtOperators, + Oid *ordOperators, + Plan *lefttree) + { + Window *node = makeNode(Window); + Plan *plan = &node->plan; + List *sub_tlist = lefttree->targetlist; + int numCols; + + copy_plan_costsize(plan, lefttree); + + plan->lefttree = lefttree; + plan->targetlist = tlist; + plan->qual = NIL; + + numCols = list_length(parse->partitionClause); + node->prtNumCols = numCols; + if (parse->partitionClause) + { + int keyno = 0; + AttrNumber *prtColIdx = NULL; + ListCell *pl; + + prtColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols); + + foreach(pl, parse->partitionClause) + { + PartitionClause *prtcl = (PartitionClause *) lfirst(pl); + Node *prtexpr = get_sortgroupclause_expr(prtcl, sub_tlist); + TargetEntry *te = NULL; + ListCell *l; + + foreach(l, sub_tlist) + { + te = (TargetEntry *) lfirst(l); + if (equal(prtexpr, te->expr)) + break; + } + + Assert(te); + prtColIdx[keyno++] = te->resno; + } + node->prtColIdx = prtColIdx; + node->prtOperators = prtOperators; + } + + numCols = list_length(parse->orderClause); + node->ordNumCols = numCols; + if (parse->orderClause) + { + int keyno = 0; + AttrNumber *ordColIdx = NULL; + ListCell *ol; + + ordColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols); + foreach(ol, parse->orderClause) + { + OrderClause *ordcl = (OrderClause *) lfirst(ol); + Node *ordexpr = get_sortgroupclause_expr(ordcl, sub_tlist); + TargetEntry *te = NULL; + ListCell *l; + + foreach(l, sub_tlist) + { + te = (TargetEntry *) lfirst(l); + if (equal(ordexpr, te->expr)) + break; + } + + Assert(te); + ordColIdx[keyno++] = te->resno; + } + /* orderClause columns will be used as "key columns". */ + node->ordColIdx = ordColIdx; + node->ordOperators = ordOperators; + } + + /* + * Currently, the parser doesn't accept frame clause. + * This is for future implementation. + */ + node->frameType = WINDOW_FRAME_NONE; + node->preceding = NULL; + node->following = NULL; + + node->winref = parse->winref; + + return node; + } /* * make_result Index: src/backend/optimizer/plan/planagg.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/optimizer/plan/planagg.c,v retrieving revision 1.41 diff -c -r1.41 planagg.c *** src/backend/optimizer/plan/planagg.c 10 Jul 2008 02:14:03 -0000 1.41 --- src/backend/optimizer/plan/planagg.c 28 Jul 2008 08:14:12 -0000 *************** *** 52,57 **** --- 52,58 ---- static void make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info); static Node *replace_aggs_with_params_mutator(Node *node, List **context); static Oid fetch_agg_sort_op(Oid aggfnoid); + static bool find_aggref_walker(Node *node, Aggref **context); /* *************** *** 620,622 **** --- 621,647 ---- return aggsortop; } + + Aggref * + find_aggref(Node *node) + { + Aggref *context = NULL; + + find_aggref_walker(node, &context); + return context; + } + + static bool + find_aggref_walker(Node *node, Aggref **context) + { + if (node == NULL) + return false; + + if (IsA(node, Aggref)) + { + *context = node; + return true; + } + + return expression_tree_walker(node, find_aggref_walker, (void *) context); + } Index: src/backend/optimizer/plan/planner.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v retrieving revision 1.234 diff -c -r1.234 planner.c *** src/backend/optimizer/plan/planner.c 10 Jul 2008 02:14:03 -0000 1.234 --- src/backend/optimizer/plan/planner.c 28 Jul 2008 08:14:12 -0000 *************** *** 80,85 **** --- 80,87 ---- List *sub_tlist, AttrNumber *groupColIdx); static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist); + static List *preprocess_window(List *tlist, Plan *subplan); + static List *window_tlist(List *tlist, Index winref); /***************************************************************************** *************** *** 1169,1174 **** --- 1171,1244 ---- } /* end of if (setOperations) */ /* + * Window nodes are stacked one by one for each window because Window + * functions are evaluated in the appropriate window. Hence, in a window + * level, upper window expressions are replaced by nulls so as to be + * evaluated in the upper Window node. For lower expressions, setrefs + * will replace them to Var nodes. + */ + if (parse->windowList) + { + ListCell *l; + List *window_pathkeys = NIL; + + result_plan->targetlist = preprocess_window(tlist, result_plan); + foreach(l, parse->windowList) + { + List *current_tlist; + List *partition_pathkeys = NIL; + List *order_pathkeys = NIL; + WindowClause *wc = (WindowClause *) lfirst(l); + + window_pathkeys = NIL; + current_tlist = window_tlist(tlist, wc->winref); + + /* + * Currently, Window Partitioning strategy is only by Sort. + * So just join partitionClause and orderClause + * to match Grouping. Hashing algorithm will be considered later. + */ + if (wc->partitionClause) + { + partition_pathkeys = make_pathkeys_for_sortclauses(root, + wc->partitionClause, + result_plan->targetlist, + false); + } + + if (wc->orderClause) + { + order_pathkeys = make_pathkeys_for_sortclauses(root, + wc->orderClause, + result_plan->targetlist, + false); + } + + Assert(partition_pathkeys != NIL || order_pathkeys != NIL); + /* + * create Sort node under Window, so PARTITION BY works + */ + window_pathkeys = list_concat(partition_pathkeys, order_pathkeys); + if (!pathkeys_contained_in(window_pathkeys, current_pathkeys)) + { + result_plan = (Plan *) make_sort_from_pathkeys(root, + result_plan, + window_pathkeys, + -1); + current_pathkeys = window_pathkeys; + } + + result_plan = (Plan *) make_window(root, + current_tlist, + wc, + extract_grouping_ops(wc->partitionClause), + extract_grouping_ops(wc->orderClause), + result_plan); + } + current_pathkeys = NIL; + } + + /* * If we were not able to make the plan come out in the right order, add * an explicit sort step. */ *************** *** 1691,1697 **** * If we're not grouping or aggregating, there's nothing to do here; * query_planner should receive the unmodified target list. */ ! if (!parse->hasAggs && !parse->groupClause && !root->hasHavingQual) { *need_tlist_eval = true; return tlist; --- 1761,1767 ---- * If we're not grouping or aggregating, there's nothing to do here; * query_planner should receive the unmodified target list. */ ! if (!parse->hasAggs && !parse->groupClause && !root->hasHavingQual && !parse->hasWindow) { *need_tlist_eval = true; return tlist; *************** *** 1839,1841 **** --- 1909,2111 ---- elog(ERROR, "resjunk output columns are not implemented"); return new_tlist; } + + /* + * preprocess_window - + * given parser tlist, returns recomposed tlist for current top plan. + * + * Before create Window nodes, window expressions are removed from current + * tlist because current plan must not be a Window node. These expressions + * are evaluated in appropriate window later. + * + * There are two main cases to be considered. + * 1. Agg/Group + * Vars from scan node required by any of expression should have been pulled + * up to now. So there's no need to consider it. A Aggref node which is contained + * in window expression is pulled and appended at the tail for upper Window node use. + * + * 2. Other Scan + * The situation resembles to the one in Agg/Group. Var expressions are pulled + * (tlist is flattened), and other evaluation expressions but window expression + * are as well, since in Window nodes we take care of only window expression. + * + */ + static List * + preprocess_window(List *tlist, Plan *subplan) + { + List *output_targetlist = NIL; + ListCell *l; + AttrNumber resno; + + /* + * Agg/Group nodes have pushed vars down to lower scan node. + * So we don't take care of them. Those nodes may be found in + * window expression nodes which will be evaluated later than aggs. + * We need pull pure Aggref node and append current tlist, so + * that Agg node doesn't care about window expressions. + */ + if (IsA(subplan, Agg) || IsA(subplan, Group)) + { + List *pulled_aggrefs = NIL; + Aggref *aggref; + AttrNumber tlist_resno; + + foreach(l, subplan->targetlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(l); + + if (find_winexpr(tle)) + { + /* pull pure aggref in window expressions */ + aggref = find_aggref((Node *) tle->expr); + if (aggref) + { + tle = flatCopyTargetEntry(tle); + tle->expr = (Expr *) makeNullConst(exprType((Node *) tle->expr), + exprTypmod((Node *) tle->expr)); + + pulled_aggrefs = lappend(pulled_aggrefs, aggref); + } + } + /* otherwise, it is safe to be evaluated in Agg node */ + output_targetlist = lappend(output_targetlist, tle); + } + + /* + * Here pulled pure Aggref nodes are appended to tlist. + * Through these process, window expressions are removed. + */ + resno = list_length(output_targetlist); + tlist_resno = list_length(tlist); + foreach(l, pulled_aggrefs) + { + TargetEntry *tle; + aggref = (Aggref *) lfirst(l); + + resno++; + tle = makeTargetEntry((Expr *) aggref, + resno, + NULL, + true); + output_targetlist = lappend(output_targetlist, tle); + + /* + * So as keep Aggref result until the topmost window, + * add the pure aggref entry to tlist. + * This node will be transfered to Var in setrefs. + */ + tlist_resno++; + tle = makeTargetEntry((Expr *) aggref, + tlist_resno, + NULL, + true); + tlist = lappend(tlist, tle); + } + } + else + { + /* + * copyObject() is required, as in tlist = lappend(tlist, tle); + * Without this, it may fall into a infinte loop. + */ + output_targetlist = copyObject(flatten_tlist(tlist)); + resno = list_length(tlist); + + foreach(l, output_targetlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(l); + Var *var = (Var *) tle->expr; + if (!tlist_member((Node *)var, tlist)) + { + resno++; + tle = makeTargetEntry(copyObject(var), + resno, + NULL, + true); + tlist = lappend(tlist, tle); + } + } + + resno = list_length(output_targetlist); + foreach(l, tlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(l); + Node *winexpr; + ListCell *lc; + + /* + * TargetEntry that contains window expression is ignored here, + * added later in window_tlist(). + */ + winexpr = find_winexpr(tle); + if (winexpr) + continue; + + foreach(lc, output_targetlist) + { + TargetEntry *subtle = (TargetEntry *) lfirst(lc); + if (equal(tle->expr, subtle->expr)) + { + /* + * It is necessary since flatten_tlist() doesn't pull + * their resosortgroupref. + */ + subtle->ressortgroupref = tle->ressortgroupref; + break; + } + } + + /* + * If an entry isn't in flatten tlist nor window expression, + * it must be evaluated here before any of Window nodes. + */ + if (!lc) + { + TargetEntry *newtle = flatCopyTargetEntry(tle); + + resno++; + newtle->resno = resno; + output_targetlist = lappend(output_targetlist, newtle); + } + } + } + + return output_targetlist; + } + + /* + * window_tlist - + * + * creates tlist suitable for current window, indicated by winref. + * For the upper window expressions than current, they are relaced + * by NullConst, so that setrefs can understand where the references + * may stop. + */ + static List * + window_tlist(List *tlist, Index winref) + { + List *output_targetlist = NIL; + ListCell *l; + + foreach(l, tlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(l); + WinAggref *winexpr; + + tle = flatCopyTargetEntry(tle); + winexpr = (WinAggref *) find_winexpr(tle); + /* + * window evaluation upper than current window is set null. + * for the lower ones, setrefs will fix them to Vars pointing to OUTER. + */ + if (winexpr && winref < winexpr->winref) + { + tle->expr = (Expr *) makeNullConst(exprType((Node *) tle->expr), + exprTypmod((Node *) tle->expr)); + } + + output_targetlist = lappend(output_targetlist, tle); + } + + return output_targetlist; + } Index: src/backend/optimizer/plan/setrefs.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v retrieving revision 1.142 diff -c -r1.142 setrefs.c *** src/backend/optimizer/plan/setrefs.c 17 Jun 2008 14:51:32 -0000 1.142 --- src/backend/optimizer/plan/setrefs.c 28 Jul 2008 08:14:12 -0000 *************** *** 385,390 **** --- 385,391 ---- break; case T_Agg: case T_Group: + case T_Window: set_upper_references(glob, plan, rtoffset); break; case T_Result: Index: src/backend/optimizer/plan/subselect.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v retrieving revision 1.132 diff -c -r1.132 subselect.c *** src/backend/optimizer/plan/subselect.c 10 Jul 2008 02:14:03 -0000 1.132 --- src/backend/optimizer/plan/subselect.c 28 Jul 2008 08:14:12 -0000 *************** *** 1303,1308 **** --- 1303,1309 ---- case T_Unique: case T_SetOp: case T_Group: + case T_Window: break; default: Index: src/backend/optimizer/prep/prepjointree.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/optimizer/prep/prepjointree.c,v retrieving revision 1.50 diff -c -r1.50 prepjointree.c *** src/backend/optimizer/prep/prepjointree.c 18 Mar 2008 22:04:14 -0000 1.50 --- src/backend/optimizer/prep/prepjointree.c 28 Jul 2008 08:14:13 -0000 *************** *** 697,702 **** --- 697,703 ---- * limiting. */ if (subquery->hasAggs || + subquery->hasWindow || subquery->groupClause || subquery->havingQual || subquery->sortClause || Index: src/backend/optimizer/util/clauses.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v retrieving revision 1.259 diff -c -r1.259 clauses.c *** src/backend/optimizer/util/clauses.c 15 May 2008 17:37:49 -0000 1.259 --- src/backend/optimizer/util/clauses.c 28 Jul 2008 08:14:13 -0000 *************** *** 838,843 **** --- 838,850 ---- return true; /* else fall through to check args */ } + else if (IsA(node, WinAggref)) + { + WinAggref *winagg = (WinAggref *) node; + + if (func_volatile(winagg->aggfnoid) == PROVOLATILE_VOLATILE) + return true; + } else if (IsA(node, OpExpr)) { OpExpr *expr = (OpExpr *) node; *************** *** 3798,3803 **** --- 3805,3818 ---- return true; } break; + case T_WinAggref: + { + WinAggref *expr = (WinAggref *)node; + if(expression_tree_walker((Node *) expr->args, + walker, context)) + return true; + } + break; case T_ArrayRef: { ArrayRef *aref = (ArrayRef *) node; *************** *** 4264,4269 **** --- 4279,4294 ---- return (Node *) newnode; } break; + case T_WinAggref: + { + WinAggref *winagg = (WinAggref *) node; + WinAggref *newnode; + + FLATCOPY(newnode, winagg, WinAggref); + MUTATE(newnode->args, winagg->args, List *); + return (Node *) newnode; + } + break; case T_ArrayRef: { ArrayRef *arrayref = (ArrayRef *) node; Index: src/backend/optimizer/util/tlist.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v retrieving revision 1.78 diff -c -r1.78 tlist.c *** src/backend/optimizer/util/tlist.c 1 Jan 2008 19:45:50 -0000 1.78 --- src/backend/optimizer/util/tlist.c 28 Jul 2008 08:14:13 -0000 *************** *** 244,246 **** --- 244,247 ---- return false; /* tlist shorter than colTypes */ return true; } + Index: src/backend/optimizer/util/var.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/optimizer/util/var.c,v retrieving revision 1.74 diff -c -r1.74 var.c *** src/backend/optimizer/util/var.c 12 May 2008 00:00:49 -0000 1.74 --- src/backend/optimizer/util/var.c 28 Jul 2008 08:14:13 -0000 *************** *** 53,58 **** --- 53,63 ---- int sublevels_up; } flatten_join_alias_vars_context; + typedef struct + { + Node *node; + } find_winexpr_context; + static bool pull_varnos_walker(Node *node, pull_varnos_context *context); static bool pull_varattnos_walker(Node *node, Bitmapset **varattnos); *************** *** 68,73 **** --- 73,79 ---- static Node *flatten_join_alias_vars_mutator(Node *node, flatten_join_alias_vars_context *context); static Relids alias_relid_set(PlannerInfo *root, Relids relids); + static bool find_winexpr_walker(Node *node, find_winexpr_context *context); /* *************** *** 707,709 **** --- 713,748 ---- bms_free(tmprelids); return result; } + + /* + * find_winexpr - + * find window evaluation node in the given TargetEntry + */ + Node * + find_winexpr(TargetEntry *tle) + { + find_winexpr_context context; + + context.node = NULL; + find_winexpr_walker((Node *) tle->expr, &context); + + return context.node; + } + + /* + * find_winexpr_walker - + */ + static bool + find_winexpr_walker(Node *node, find_winexpr_context *context) + { + if (node == NULL) + return false; + + if (IsA(node, WinAggref)) + { + context->node = node; + return true; + } + + return expression_tree_walker(node, find_winexpr_walker, (void *) context); + } Index: src/backend/parser/analyze.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/parser/analyze.c,v retrieving revision 1.373 diff -c -r1.373 analyze.c *** src/backend/parser/analyze.c 18 Jul 2008 20:26:06 -0000 1.373 --- src/backend/parser/analyze.c 28 Jul 2008 08:14:13 -0000 *************** *** 727,732 **** --- 727,736 ---- &qry->targetList, &qry->sortClause); + qry->windowList = transformWinDef(pstate, + pstate->p_windef_list, + &qry->targetList); + qry->limitOffset = transformLimitClause(pstate, stmt->limitOffset, "OFFSET"); qry->limitCount = transformLimitClause(pstate, stmt->limitCount, *************** *** 745,750 **** --- 749,755 ---- qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasAggs = pstate->p_hasAggs; + qry->hasWindow = pstate->p_hasWindow; if (pstate->p_hasAggs || qry->groupClause || qry->havingQual) parseCheckAggregates(pstate, qry); Index: src/backend/parser/gram.y =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/parser/gram.y,v retrieving revision 2.618 diff -c -r2.618 gram.y *** src/backend/parser/gram.y 18 Jul 2008 03:32:52 -0000 2.618 --- src/backend/parser/gram.y 28 Jul 2008 08:14:14 -0000 *************** *** 363,368 **** --- 363,370 ---- %type document_or_content %type xml_whitespace_option + %type partition_clause opt_partition_clause + %type window_definition opt_window_definition /* * If you make any token changes, update the keyword table in *************** *** 421,429 **** NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF NULLS_P NUMERIC OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OR ! ORDER OUT_P OUTER_P OVERLAPS OVERLAY OWNED OWNER ! PARSER PARTIAL PASSWORD PLACING PLANS POSITION PRECISION PRESERVE PREPARE PREPARED PRIMARY PRIOR PRIVILEGES PROCEDURAL PROCEDURE --- 423,431 ---- NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF NULLS_P NUMERIC OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OR ! ORDER OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER ! PARSER PARTIAL PARTITION PASSWORD PLACING PLANS POSITION PRECISION PRESERVE PREPARE PREPARED PRIMARY PRIOR PRIVILEGES PROCEDURAL PROCEDURE *************** *** 7901,7907 **** * (Note that many of the special SQL functions wouldn't actually make any * sense as functional index entries, but we ignore that consideration here.) */ ! func_expr: func_name '(' ')' { FuncCall *n = makeNode(FuncCall); n->funcname = $1; --- 7903,7909 ---- * (Note that many of the special SQL functions wouldn't actually make any * sense as functional index entries, but we ignore that consideration here.) */ ! func_expr: func_name '(' ')' opt_window_definition { FuncCall *n = makeNode(FuncCall); n->funcname = $1; *************** *** 7909,7918 **** n->agg_star = FALSE; n->agg_distinct = FALSE; n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } ! | func_name '(' expr_list ')' { FuncCall *n = makeNode(FuncCall); n->funcname = $1; --- 7911,7921 ---- n->agg_star = FALSE; n->agg_distinct = FALSE; n->func_variadic = FALSE; + n->win_definition = (WinDef *) $4; n->location = @1; $$ = (Node *)n; } ! | func_name '(' expr_list ')' opt_window_definition { FuncCall *n = makeNode(FuncCall); n->funcname = $1; *************** *** 7920,7925 **** --- 7923,7929 ---- n->agg_star = FALSE; n->agg_distinct = FALSE; n->func_variadic = FALSE; + n->win_definition = (WinDef *) $5; n->location = @1; $$ = (Node *)n; } *************** *** 7931,7936 **** --- 7935,7941 ---- n->agg_star = FALSE; n->agg_distinct = FALSE; n->func_variadic = TRUE; + n->win_definition = NULL; n->location = @1; $$ = (Node *)n; } *************** *** 7942,7947 **** --- 7947,7953 ---- n->agg_star = FALSE; n->agg_distinct = FALSE; n->func_variadic = TRUE; + n->win_definition = NULL; n->location = @1; $$ = (Node *)n; } *************** *** 7957,7962 **** --- 7963,7969 ---- * for that in FuncCall at the moment. */ n->func_variadic = FALSE; + n->win_definition = NULL; n->location = @1; $$ = (Node *)n; } *************** *** 7968,7977 **** n->agg_star = FALSE; n->agg_distinct = TRUE; n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } ! | func_name '(' '*' ')' { /* * We consider AGGREGATE(*) to invoke a parameterless --- 7975,7985 ---- n->agg_star = FALSE; n->agg_distinct = TRUE; n->func_variadic = FALSE; + n->win_definition = NULL; n->location = @1; $$ = (Node *)n; } ! | func_name '(' '*' ')' opt_window_definition { /* * We consider AGGREGATE(*) to invoke a parameterless *************** *** 7989,7994 **** --- 7997,8003 ---- n->agg_star = TRUE; n->agg_distinct = FALSE; n->func_variadic = FALSE; + n->win_definition = (WinDef *) $5; n->location = @1; $$ = (Node *)n; } *************** *** 8409,8414 **** --- 8418,8455 ---- ; /* + * Window Definitions + * + * In SQL2003 window may appear after a function call or after HAVING, with the same syntax. + * If there is window syntax after HAVING, some of window can refer to it. + */ + opt_window_definition: OVER window_definition { $$ = $2; } + | /*EMPTY*/ { $$ = NULL; } + ; + window_definition: '(' opt_partition_clause opt_sort_clause ')' + { + WinDef *n = makeNode(WinDef); + n->partitionClause = $2; + n->orderClause = $3; + n->expr_list = NIL; + n->location = @1; + $$ = (Node *)n; + } + ; + + opt_partition_clause: partition_clause { $$ = $1; } + | /*EMPTY*/ { $$ = NIL; } + ; + + partition_clause: PARTITION BY expr_list + { + $$ = $3; + } + ; + + + + /* * Supporting nonterminals for expressions. */ Index: src/backend/parser/keywords.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/parser/keywords.c,v retrieving revision 1.199 diff -c -r1.199 keywords.c *** src/backend/parser/keywords.c 16 Jul 2008 01:30:22 -0000 1.199 --- src/backend/parser/keywords.c 28 Jul 2008 08:14:14 -0000 *************** *** 280,291 **** --- 280,293 ---- {"order", ORDER, RESERVED_KEYWORD}, {"out", OUT_P, COL_NAME_KEYWORD}, {"outer", OUTER_P, TYPE_FUNC_NAME_KEYWORD}, + {"over", OVER, RESERVED_KEYWORD}, {"overlaps", OVERLAPS, TYPE_FUNC_NAME_KEYWORD}, {"overlay", OVERLAY, COL_NAME_KEYWORD}, {"owned", OWNED, UNRESERVED_KEYWORD}, {"owner", OWNER, UNRESERVED_KEYWORD}, {"parser", PARSER, UNRESERVED_KEYWORD}, {"partial", PARTIAL, UNRESERVED_KEYWORD}, + {"partition", PARTITION, RESERVED_KEYWORD}, {"password", PASSWORD, UNRESERVED_KEYWORD}, {"placing", PLACING, RESERVED_KEYWORD}, {"plans", PLANS, UNRESERVED_KEYWORD}, Index: src/backend/parser/parse_agg.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/parser/parse_agg.c,v retrieving revision 1.79 diff -c -r1.79 parse_agg.c *** src/backend/parser/parse_agg.c 1 Jan 2008 19:45:50 -0000 1.79 --- src/backend/parser/parse_agg.c 28 Jul 2008 08:14:14 -0000 *************** *** 67,73 **** */ if (min_varlevel == 0) { ! if (checkExprHasAggs((Node *) agg->args)) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), errmsg("aggregate function calls cannot be nested"))); --- 67,73 ---- */ if (min_varlevel == 0) { ! if (checkExprHasAggs((Node *) agg->args) || checkExprHasWinAggs((Node *) agg->args)) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), errmsg("aggregate function calls cannot be nested"))); *************** *** 83,88 **** --- 83,121 ---- pstate->p_hasAggs = true; } + /* + * transformWinAggregateCall - + * + */ + void + transformWinAggregateCall(ParseState *pstate, WinAggref *agg) + { + int min_varlevel; + + /* + * The aggregate's level is the same as the level of the lowest-level + * variable or aggregate in its arguments; or if it contains no variables + * at all, we presume it to be local. + */ + min_varlevel = find_minimum_var_level((Node *) agg->args); + + /* + * An aggregate can't directly contain another aggregate call of the same + * level (though outer aggs are okay). We can skip this check if we + * didn't find any local vars or aggs. + */ + if (min_varlevel == 0) + { + if (checkExprHasWinAggs((Node *) agg->args)) + ereport(ERROR, + (errcode(ERRCODE_GROUPING_ERROR), + errmsg("aggregate function calls cannot be nested"))); + } + + if (min_varlevel < 0) + min_varlevel = 0; + agg->agglevelsup = min_varlevel; + } /* * parseCheckAggregates Index: src/backend/parser/parse_clause.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/parser/parse_clause.c,v retrieving revision 1.170 diff -c -r1.170 parse_clause.c *** src/backend/parser/parse_clause.c 19 Jun 2008 00:46:05 -0000 1.170 --- src/backend/parser/parse_clause.c 28 Jul 2008 08:14:14 -0000 *************** *** 40,47 **** #define ORDER_CLAUSE 0 #define GROUP_CLAUSE 1 #define DISTINCT_ON_CLAUSE 2 ! static char *clauseText[] = {"ORDER BY", "GROUP BY", "DISTINCT ON"}; static void extractRemainingColumns(List *common_colnames, List *src_colnames, List *src_colvars, --- 40,49 ---- #define ORDER_CLAUSE 0 #define GROUP_CLAUSE 1 #define DISTINCT_ON_CLAUSE 2 + #define OVER_CLAUSE 3 + #define PARTITION_CLAUSE 4 ! static char *clauseText[] = {"ORDER BY", "GROUP BY", "DISTINCT ON", "OVER", "PARTITION"}; static void extractRemainingColumns(List *common_colnames, List *src_colnames, List *src_colvars, *************** *** 66,71 **** --- 68,75 ---- Var *l_colvar, Var *r_colvar); static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause); + static WindowClause *findWindowClause(List *wclist, List *partitionClause, + List *orderClause); /* *************** *** 1278,1283 **** --- 1282,1299 ---- } /* + * in OVER (), findTargetlistEntry must find TargetEntry which is + * already transformed. + */ + if(clause == OVER_CLAUSE) + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("%s is not in select list", + clauseText[clause]))); + } + + /* * If no matches, construct a new target entry which is appended to the * end of the target list. This target is given resjunk = TRUE so that it * will not be projected into the final tuple. *************** *** 1451,1456 **** --- 1467,1603 ---- } /* + * transformOrderClause - + * + * OrderClause is a set of SortBys. Only tag type is different. + */ + List * + transformOrderClause(ParseState *pstate, + List *orderlist, + List **targetlist, + bool resolveUnknown) + { + List *result = NIL; + ListCell *l; + TargetEntry *tle; + + foreach(l, orderlist) + { + SortBy *sortby = lfirst(l); + + tle = findTargetlistEntry(pstate, sortby->node, + targetlist, ORDER_CLAUSE); + result = addTargetToSortList(pstate, tle, + result, *targetlist, + sortby->sortby_dir, + sortby->sortby_nulls, + sortby->useOp, + resolveUnknown); + } + + return result; + } + + /* + * transformPartitionClause - + * + * Almost everything PartitionClause has is the same as GroupClause. + */ + List * + transformPartitionClause(ParseState *pstate, + List *partitionlist, + List **targetlist) + { + List *result = NIL; + ListCell *l; + TargetEntry *tle; + + foreach(l, partitionlist) + { + Oid restype; + PartitionClause *pc; + Oid sort_op; + + tle = findTargetlistEntry(pstate, lfirst(l), targetlist, PARTITION_CLAUSE); + + restype = exprType((Node *) tle->expr); + + if (restype == UNKNOWNOID) + tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr, + restype, TEXTOID, -1, + COERCION_IMPLICIT, + COERCE_IMPLICIT_CAST); + pc = makeNode(PartitionClause); + pc->tleSortGroupRef = assignSortGroupRef(tle, *targetlist); + sort_op = ordering_oper_opid(exprType((Node *) tle->expr)); + pc->sortop = sort_op; + pc->nulls_first = false; + result = lappend(result, pc); + } + + return result; + } + + /* + * transformWinDef - + * + */ + List * + transformWinDef(ParseState *pstate, + List *windefinition, + List **targetlist) + { + List *result = NIL; + ListCell *l; + Index winref = 1; + + foreach(l, windefinition) + { + WinDef *windef = (WinDef *) lfirst(l); + List *partitionClause = NIL; + List *orderClause = NIL; + WindowClause *wc; + ListCell *lc; + + partitionClause = transformPartitionClause(pstate, + windef->partitionClause, + targetlist); + orderClause = transformOrderClause(pstate, + windef->orderClause, + targetlist, + true); + + /* + * If there is the same node that has been in the list, + * refer to it. + */ + wc = findWindowClause(result, partitionClause, orderClause); + if (!wc) + { + wc = makeNode(WindowClause); + wc->partitionClause = partitionClause; + wc->orderClause = orderClause; + wc->winref = winref++; + + result = lappend(result, wc); + pstate->p_hasWindow = true; + } + + foreach(lc, windef->expr_list) + { + Node *node = (Node *) lfirst(lc); + + if (IsA(node, WinAggref)) + { + ((WinAggref *) node)->winref = wc->winref; + } + } + } + + return result; + } + + /* * transformDistinctClause - * transform a DISTINCT or DISTINCT ON clause * *************** *** 1777,1779 **** --- 1924,1948 ---- } return false; } + + /* + * find_window_clause - + * + */ + static WindowClause * + findWindowClause(List *wclist, List *partitionClause, List *orderClause) + { + ListCell *l; + + foreach(l, wclist) + { + WindowClause *wc = (WindowClause *) lfirst(l); + if (equal(wc->partitionClause, partitionClause) && + equal(wc->orderClause, orderClause)) + { + return wc; + } + } + + return NULL; + } Index: src/backend/parser/parse_expr.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/parser/parse_expr.c,v retrieving revision 1.229 diff -c -r1.229 parse_expr.c *** src/backend/parser/parse_expr.c 16 Jul 2008 01:30:22 -0000 1.229 --- src/backend/parser/parse_expr.c 28 Jul 2008 08:14:14 -0000 *************** *** 359,365 **** list_make1(n), list_make1(result), false, false, false, ! true, -1); } } /* process trailing subscripts, if any */ --- 359,365 ---- list_make1(n), list_make1(result), false, false, false, ! true, NULL, -1); } } /* process trailing subscripts, if any */ *************** *** 482,488 **** list_make1(makeString(name2)), list_make1(node), false, false, false, ! true, cref->location); } break; } --- 482,488 ---- list_make1(makeString(name2)), list_make1(node), false, false, false, ! true, NULL, cref->location); } break; } *************** *** 512,518 **** list_make1(makeString(name3)), list_make1(node), false, false, false, ! true, cref->location); } break; } --- 512,518 ---- list_make1(makeString(name3)), list_make1(node), false, false, false, ! true, NULL, cref->location); } break; } *************** *** 553,559 **** list_make1(makeString(name4)), list_make1(node), false, false, false, ! true, cref->location); } break; } --- 553,559 ---- list_make1(makeString(name4)), list_make1(node), false, false, false, ! true, NULL, cref->location); } break; } *************** *** 1034,1039 **** --- 1034,1040 ---- fn->agg_distinct, fn->func_variadic, false, + fn->win_definition, fn->location); } *************** *** 1889,1894 **** --- 1890,1898 ---- case T_Aggref: type = ((Aggref *) expr)->aggtype; break; + case T_WinAggref: + type = ((WinAggref *) expr)->aggtype; + break; case T_ArrayRef: { ArrayRef *arrayref = (ArrayRef *) expr; Index: src/backend/parser/parse_func.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/parser/parse_func.c,v retrieving revision 1.203 diff -c -r1.203 parse_func.c *** src/backend/parser/parse_func.c 16 Jul 2008 01:30:22 -0000 1.203 --- src/backend/parser/parse_func.c 28 Jul 2008 08:14:15 -0000 *************** *** 15,20 **** --- 15,21 ---- #include "postgres.h" #include "access/heapam.h" + #include "catalog/pg_aggregate.h" #include "catalog/pg_inherits.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" *************** *** 39,44 **** --- 40,46 ---- Node *first_arg, int location); static void unknown_attribute(ParseState *pstate, Node *relref, char *attname, int location); + static bool isWindowFunction(Oid aggfnoid); /* *************** *** 63,69 **** Node * ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, bool agg_star, bool agg_distinct, bool func_variadic, ! bool is_column, int location) { Oid rettype; Oid funcid; --- 65,71 ---- Node * ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, bool agg_star, bool agg_distinct, bool func_variadic, ! bool is_column, WinDef *windef, int location) { Oid rettype; Oid funcid; *************** *** 291,321 **** } else { ! /* aggregate function */ ! Aggref *aggref = makeNode(Aggref); ! aggref->aggfnoid = funcid; ! aggref->aggtype = rettype; ! aggref->args = fargs; ! aggref->aggstar = agg_star; ! aggref->aggdistinct = agg_distinct; ! /* ! * Reject attempt to call a parameterless aggregate without (*) ! * syntax. This is mere pedantry but some folks insisted ... ! */ ! if (fargs == NIL && !agg_star) ! ereport(ERROR, ! (errcode(ERRCODE_WRONG_OBJECT_TYPE), ! errmsg("%s(*) must be used to call a parameterless aggregate function", ! NameListToString(funcname)), ! parser_errposition(pstate, location))); ! /* parse_agg.c does additional aggregate-specific processing */ ! transformAggregateCall(pstate, aggref); ! retval = (Node *) aggref; if (retset) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), --- 293,360 ---- } else { ! /* ! * an aggregate function with window definition is ! * a window aggregate function ! */ ! if (windef) ! { ! WinAggref *winagg = makeNode(WinAggref); ! winagg->aggfnoid = funcid; ! winagg->aggtype = rettype; ! winagg->args = fargs; ! /* TODO: we are not sure if winagg can take star and distinct... */ ! winagg->aggstar = agg_star; ! winagg->aggdistinct = agg_distinct; ! /* ! * Reject attempt to call a parameterless aggregate without (*) ! * syntax. This is mere pedantry but some folks insisted ... ! */ ! if (fargs == NIL && !agg_star && ! !isWindowFunction(winagg->aggfnoid)) ! ereport(ERROR, ! (errcode(ERRCODE_WRONG_OBJECT_TYPE), ! errmsg("%s(*) must be used to call a parameterless aggregate function", ! NameListToString(funcname)), ! parser_errposition(pstate, location))); ! ! transformWinAggregateCall(pstate, winagg); ! retval = (Node *) winagg; ! ! pstate->p_windef_list = lappend(pstate->p_windef_list, windef); ! windef->expr_list = lappend(windef->expr_list, winagg); ! } ! else ! { ! /* aggregate function */ ! Aggref *aggref = makeNode(Aggref); ! aggref->aggfnoid = funcid; ! aggref->aggtype = rettype; ! aggref->args = fargs; ! aggref->aggstar = agg_star; ! aggref->aggdistinct = agg_distinct; + /* + * Reject attempt to call a parameterless aggregate without (*) + * syntax. This is mere pedantry but some folks insisted ... + */ + if (fargs == NIL && !agg_star) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s(*) must be used to call a parameterless aggregate function", + NameListToString(funcname)), + parser_errposition(pstate, location))); + + /* parse_agg.c does additional aggregate-specific processing */ + transformAggregateCall(pstate, aggref); + + retval = (Node *) aggref; + + } if (retset) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), *************** *** 1368,1370 **** --- 1407,1435 ---- return oid; } + + + /* + * is window function, not window aggregate? + */ + static bool + isWindowFunction(Oid aggfnoid) + { + HeapTuple aggTuple; + Form_pg_aggregate aggform; + bool result = false; + + aggTuple = SearchSysCache(AGGFNOID, + ObjectIdGetDatum(aggfnoid), + 0, 0, 0); + if (!HeapTupleIsValid(aggTuple)) + elog(ERROR, "cache lookup failed for window aggregate %u", aggfnoid); + aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple); + if (OidIsValid(aggform->aggfinalfn) && + func_volatile(aggform->aggfinalfn) == PROVOLATILE_VOLATILE) + result = true; + ReleaseSysCache(aggTuple); + + return result; + } + Index: src/backend/rewrite/rewriteManip.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v retrieving revision 1.107 diff -c -r1.107 rewriteManip.c *** src/backend/rewrite/rewriteManip.c 1 Jan 2008 19:45:51 -0000 1.107 --- src/backend/rewrite/rewriteManip.c 28 Jul 2008 08:14:15 -0000 *************** *** 29,34 **** --- 29,36 ---- static bool checkExprHasAggs_walker(Node *node, checkExprHasAggs_context *context); + static bool checkExprHasWinAggs_walker(Node *node, + checkExprHasAggs_context *context); static bool checkExprHasSubLink_walker(Node *node, void *context); static Relids offset_relid_set(Relids relids, int offset); static Relids adjust_relid_set(Relids relids, int oldrelid, int newrelid); *************** *** 88,93 **** --- 90,139 ---- (void *) context); } + bool + checkExprHasWinAggs(Node *node) + { + checkExprHasAggs_context context; + + context.sublevels_up = 0; + + /* + * Must be prepared to start with a Query or a bare expression tree; if + * it's a Query, we don't want to increment sublevels_up. + */ + return query_or_expression_tree_walker(node, + checkExprHasWinAggs_walker, + (void *) &context, + 0); + } + + static bool + checkExprHasWinAggs_walker(Node *node, checkExprHasAggs_context *context) + { + if (node == NULL) + return false; + if (IsA(node, WinAggref)) + { + if (((WinAggref *) node)->agglevelsup == context->sublevels_up) + return true; /* abort the tree traversal and return true */ + /* else fall through to examine argument */ + } + if (IsA(node, Query)) + { + /* Recurse into subselects */ + bool result; + + context->sublevels_up++; + result = query_tree_walker((Query *) node, + checkExprHasWinAggs_walker, + (void *) context, 0); + context->sublevels_up--; + return result; + } + return expression_tree_walker(node, checkExprHasWinAggs_walker, + (void *) context); + } + /* * checkExprHasSubLink - * Check if an expression contains a SubLink. Index: src/backend/utils/adt/ruleutils.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v retrieving revision 1.278 diff -c -r1.278 ruleutils.c *** src/backend/utils/adt/ruleutils.c 18 Jul 2008 03:32:52 -0000 1.278 --- src/backend/utils/adt/ruleutils.c 28 Jul 2008 08:14:15 -0000 *************** *** 175,180 **** --- 175,181 ---- 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_winagg_expr(WinAggref *winagg, deparse_context *context); static void get_coercion_expr(Node *arg, deparse_context *context, Oid resulttype, int32 resulttypmod, Node *parentNode); *************** *** 3583,3588 **** --- 3584,3593 ---- get_agg_expr((Aggref *) node, context); break; + case T_WinAggref: + get_winagg_expr((WinAggref *) node, context); + break; + case T_ArrayRef: { ArrayRef *aref = (ArrayRef *) node; *************** *** 4536,4541 **** --- 4541,4580 ---- appendStringInfoChar(buf, ')'); } + /* + * get_winagg_expr - Parse back an WinAggref node + */ + static void + get_winagg_expr(WinAggref *winagg, deparse_context *context) + { + StringInfo buf = context->buf; + Oid argtypes[FUNC_MAX_ARGS]; + int nargs; + ListCell *l; + + nargs = 0; + foreach(l, winagg->args) + { + if (nargs >= FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg("too many arguments"))); + argtypes[nargs] = exprType((Node *) lfirst(l)); + nargs++; + } + + appendStringInfo(buf, "%s(%s", + generate_function_name(winagg->aggfnoid, + nargs, argtypes, NULL), + winagg->aggdistinct ? "DISTINCT " : ""); + /* aggstar can be set only in zero-argument aggregates */ + if (winagg->aggstar) + appendStringInfoChar(buf, '*'); + else + get_rule_expr((Node *) winagg->args, context, true); + appendStringInfoChar(buf, ')'); + } + /* ---------- * get_coercion_expr * Index: src/include/catalog/pg_aggregate.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/catalog/pg_aggregate.h,v retrieving revision 1.66 diff -c -r1.66 pg_aggregate.h *** src/include/catalog/pg_aggregate.h 27 Mar 2008 03:57:34 -0000 1.66 --- src/include/catalog/pg_aggregate.h 28 Jul 2008 08:14:16 -0000 *************** *** 220,225 **** --- 220,233 ---- /* xml */ DATA(insert ( 2901 xmlconcat2 - 0 142 _null_ )); + /* window functions */ + DATA(insert ( 3898 - row_number_final 0 20 _null_ )); + DATA(insert ( 3899 - rank_final 0 20 _null_ )); + DATA(insert ( 3900 - dense_rank_final 0 20 _null_ )); + DATA(insert ( 3901 int8inc percent_rank_final 0 20 "0" )); + DATA(insert ( 3902 int8inc cume_dist_final 0 20 "0" )); + DATA(insert ( 3903 ntile_trans ntile_final 0 1016 "{0,0}" )); + /* * prototypes for functions in pg_aggregate.c */ Index: src/include/catalog/pg_proc.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/catalog/pg_proc.h,v retrieving revision 1.509 diff -c -r1.509 pg_proc.h *** src/include/catalog/pg_proc.h 18 Jul 2008 03:32:53 -0000 1.509 --- src/include/catalog/pg_proc.h 28 Jul 2008 08:14:17 -0000 *************** *** 4460,4465 **** --- 4460,4487 ---- DESCR("is txid visible in snapshot?"); + DATA(insert OID = 3790 ( row_number_final PGNSP PGUID 12 1 0 0 f f f f v 1 20 "20" _null_ _null_ _null_ row_number_final _null_ _null_ _null_)); + DESCR("final function of row_number"); + DATA(insert OID = 3791 ( rank_final PGNSP PGUID 12 1 0 0 f f f f v 1 20 "20" _null_ _null_ _null_ rank_final _null_ _null_ _null_)); + DESCR("final function of rank"); + DATA(insert OID = 3792 ( dense_rank_final PGNSP PGUID 12 1 0 0 f f f f v 1 20 "20" _null_ _null_ _null_ dense_rank_final _null_ _null_ _null_)); + DESCR("final function of dense_rank"); + DATA(insert OID = 3793 ( percent_rank_final PGNSP PGUID 12 1 0 0 f f f f v 1 701 "20" _null_ _null_ _null_ percent_rank_final _null_ _null_ _null_)); + DESCR("final function of percent_rank"); + DATA(insert OID = 3794 ( cume_dist_final PGNSP PGUID 12 1 0 0 f f f f v 1 701 "20" _null_ _null_ _null_ cume_dist_final _null_ _null_ _null_)); + DESCR("final function of cume_dist"); + DATA(insert OID = 3795 ( ntile_trans PGNSP PGUID 12 1 0 0 f f f f v 2 1016 "1016 20" _null_ _null_ _null_ ntile_trans _null_ _null_ _null_)); + DESCR("transitional function of ntile"); + DATA(insert OID = 3796 ( ntile_final PGNSP PGUID 12 1 0 0 f f f f v 1 20 "1016" _null_ _null_ _null_ ntile_final _null_ _null_ _null_)); + DESCR("final function of ntile"); + + DATA(insert OID = 3898 ( row_number PGNSP PGUID 12 1 0 0 t f t f v 0 20 "" _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_)); + DATA(insert OID = 3899 ( rank PGNSP PGUID 12 1 0 0 t f t f v 0 20 "" _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_)); + DATA(insert OID = 3900 ( dense_rank PGNSP PGUID 12 1 0 0 t f t f v 0 20 "" _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_)); + DATA(insert OID = 3901 ( percent_rank PGNSP PGUID 12 1 0 0 t f t f v 0 701 "" _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_)); + DATA(insert OID = 3902 ( cume_dist PGNSP PGUID 12 1 0 0 t f t f v 0 701 "" _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_)); + DATA(insert OID = 3903 ( ntile PGNSP PGUID 12 1 0 0 t f t f v 1 20 "20" _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_)); + /* * Symbolic values for provolatile column: these indicate whether the result * of a function is dependent *only* on the values of its explicit arguments, Index: src/include/nodes/execnodes.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/nodes/execnodes.h,v retrieving revision 1.185 diff -c -r1.185 execnodes.h *** src/include/nodes/execnodes.h 26 Jul 2008 19:15:35 -0000 1.185 --- src/include/nodes/execnodes.h 28 Jul 2008 08:14:18 -0000 *************** *** 123,128 **** --- 123,132 ---- Datum *ecxt_aggvalues; /* precomputed values for Aggref nodes */ bool *ecxt_aggnulls; /* null flags for Aggref nodes */ + /* Windowed values */ + Datum *ecxt_winaggvalues; /* TODO: comment */ + bool *ecxt_winaggnulls; /* TODO: comment */ + /* Value to substitute for CaseTestExpr nodes in expression */ Datum caseValue_datum; bool caseValue_isNull; *************** *** 503,508 **** --- 507,523 ---- } AggrefExprState; /* ---------------- + * WinAggrefExprState node + * ---------------- + */ + typedef struct WinAggrefExprState + { + ExprState xprstate; + List *args; /* states of argument expressions */ + int aggno; /* ID number for agg within its plan node */ + } WinAggrefExprState; + + /* ---------------- * ArrayRefExprState node * * Note: array types can be fixed-length (typlen > 0), but only when the *************** *** 1467,1470 **** --- 1482,1521 ---- TupleTableSlot *subSlot; /* tuple last obtained from subplan */ } LimitState; + /* these are private structures in nodeWindow.c */ + typedef struct WindowStatePerAggData *WindowStatePerAgg; + + /* ---------------- + * WindowState - + * + * a state object used in nodeWindow.c. Similar to AggState, it holds + * another econtext for input tuples and per-agg information, but also + * WindowFrame list which is used to iterate inside the current partition and + * preserve the previous aggregated values. + * ---------------- + */ + typedef struct WindowState + { + ScanState ss; /* its first field is NodeTag */ + + FmgrInfo *prtEqfunctions; /* for partition by columns */ + FmgrInfo *ordEqfunctions; /* for order by columns */ + List *aggs; /* all WinAggref nodes in targetlist & quals */ + int numaggs; /* number of aggregates */ + WindowStatePerAgg peragg; /* per-WinAggref information */ + + TupleTableSlot *currentslot; /* represents CURRENT ROW */ + ExprContext *tmpcontext; /* econetxt for input expression */ + bool win_done; /* indicates completion of Window scan */ + HeapTuple prt_firstTuple; /* copy of first tuple of current partition */ + bool partition_processing; /* partition is in the process right now */ + bool frame_processing; /* frame is in the process right now */ + bool need_aggregate; /* whether trans functions are processed */ + + MemoryContext wincontext; /* memory context for long-lived data */ + + void *ts_partition; /* store whole of the current partition input */ + int eflags; /* flags passed at InitNode */ + } WindowState; + #endif /* EXECNODES_H */ Index: src/include/nodes/nodes.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/nodes/nodes.h,v retrieving revision 1.206 diff -c -r1.206 nodes.h *** src/include/nodes/nodes.h 20 Mar 2008 21:42:48 -0000 1.206 --- src/include/nodes/nodes.h 28 Jul 2008 08:14:18 -0000 *************** *** 67,72 **** --- 67,73 ---- T_Hash, T_SetOp, T_Limit, + T_Window, /* * TAGS FOR PLAN STATE NODES (execnodes.h) *************** *** 99,104 **** --- 100,106 ---- T_HashState, T_SetOpState, T_LimitState, + T_WindowState, /* * TAGS FOR PRIMITIVE NODES (primnodes.h) *************** *** 110,115 **** --- 112,118 ---- T_Const, T_Param, T_Aggref, + T_WinAggref, T_ArrayRef, T_FuncExpr, T_OpExpr, *************** *** 155,160 **** --- 158,164 ---- T_ExprState = 400, T_GenericExprState, T_AggrefExprState, + T_WinAggrefExprState, T_ArrayRefExprState, T_FuncExprState, T_ScalarArrayOpExprState, *************** *** 321,327 **** --- 325,335 ---- T_ColumnRef, T_ParamRef, T_A_Const, + T_WinDef, T_FuncCall, + T_OrderClause, + T_PartitionClause, + T_WindowClause, T_A_Indices, T_A_Indirection, T_A_ArrayExpr, Index: src/include/nodes/parsenodes.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/nodes/parsenodes.h,v retrieving revision 1.368 diff -c -r1.368 parsenodes.h *** src/include/nodes/parsenodes.h 18 Jul 2008 03:32:53 -0000 1.368 --- src/include/nodes/parsenodes.h 28 Jul 2008 08:14:18 -0000 *************** *** 108,113 **** --- 108,114 ---- bool hasAggs; /* has aggregates in tlist or havingQual */ bool hasSubLinks; /* has subquery SubLink */ + bool hasWindow; /* has Window process */ List *rtable; /* list of range table entries */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses) */ *************** *** 124,129 **** --- 125,132 ---- List *sortClause; /* a list of SortClause's */ + List *windowList; + Node *limitOffset; /* # of result tuples to skip (int8 expr) */ Node *limitCount; /* # of result tuples to return (int8 expr) */ *************** *** 262,267 **** --- 265,271 ---- bool agg_star; /* argument was really '*' */ bool agg_distinct; /* arguments were labeled DISTINCT */ bool func_variadic; /* last argument was labeled VARIADIC */ + struct WinDef *win_definition; /* window definition */ int location; /* token location, or -1 if unknown */ } FuncCall; *************** *** 645,650 **** --- 649,700 ---- typedef SortClause GroupClause; /* + * OrderClause - + * representation of ORDER BY in Window + */ + typedef SortClause OrderClause; + + + /* + * PartitionClause - + * representaition of PATITION BY in Window + */ + typedef SortClause PartitionClause; + + /* + * WinDef - + * + * This may come after function call expression or after HAVING clause. + * expr_list holds expression pointers attached with this window, which + * will be referred in the transformation process. Since window declaration + * appears more than once in a query, duplicated WinDefs are created, then + * are aggregated in transformation. + */ + typedef struct WinDef + { + NodeTag type; + List *partitionClause; + List *orderClause; + List *expr_list; + int location; + } WinDef; + + /* + * WindowCaluse - + * A unit of Window. + * + * winref is an identification and may be referred by window expressions. + */ + typedef struct WindowClause + { + NodeTag type; + List *partitionClause; + List *orderClause; + Index winref; + } WindowClause; + + + /* * RowMarkClause - * representation of FOR UPDATE/SHARE clauses * Index: src/include/nodes/plannodes.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/nodes/plannodes.h,v retrieving revision 1.100 diff -c -r1.100 plannodes.h *** src/include/nodes/plannodes.h 13 Apr 2008 20:51:21 -0000 1.100 --- src/include/nodes/plannodes.h 28 Jul 2008 08:14:18 -0000 *************** *** 491,496 **** --- 491,504 ---- long numGroups; /* estimated number of groups in input */ } Agg; + typedef struct Partition + { + Plan plan; + int numCols; + AttrNumber *prtColIdx; + Oid *prtOperators; + } Partition; + /* ---------------- * unique node * ---------------- *************** *** 550,553 **** --- 558,591 ---- Node *limitCount; /* COUNT parameter, or NULL if none */ } Limit; + /* ---------------- + * window frame type + * ---------------- + */ + typedef enum WindowFrameType + { + WINDOW_FRAME_NONE, + WINDOW_FRAME_ROWS, + WINFOW_FRAME_RANGE + } WindowFrameType; + + /* ---------------- + * window node + * ---------------- + */ + typedef struct Window + { + Plan plan; + int prtNumCols; /* number of columns for partition boundary */ + AttrNumber *prtColIdx; /* indices of partition boundary */ + Oid *prtOperators; /* equation operators of partition columns */ + int ordNumCols; /* number of columns for order keys */ + AttrNumber *ordColIdx; /* indices of order keys */ + Oid *ordOperators; /* equation operators of order columns */ + WindowFrameType frameType; /* which type of frame? */ + Node *preceding; /* reserved. PRECEDING ... Const Expr */ + Node *following; /* reserved. FOLLOWING ... Const Expr */ + Index winref; /* ID of this window, associated with winagg */ + } Window; + #endif /* PLANNODES_H */ Index: src/include/nodes/primnodes.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/nodes/primnodes.h,v retrieving revision 1.137 diff -c -r1.137 primnodes.h *** src/include/nodes/primnodes.h 1 Jan 2008 19:45:58 -0000 1.137 --- src/include/nodes/primnodes.h 28 Jul 2008 08:14:18 -0000 *************** *** 220,225 **** --- 220,237 ---- bool aggdistinct; /* TRUE if it's agg(DISTINCT ...) */ } Aggref; + typedef struct WinAggref + { + Expr xpr; + Oid aggfnoid; /* pg_proc Oid of the aggregate */ + Oid aggtype; /* type Oid of result of the aggregate */ + List *args; /* arguments to the aggregate */ + Index agglevelsup; /* > 0 if agg belongs to outer query */ + bool aggstar; /* TRUE if argument list was really '*' */ + bool aggdistinct; /* TRUE if it's agg(DISTINCT ...) */ + Index winref; /* tie with WinDef */ + } WinAggref; + /* ---------------- * ArrayRef: describes an array subscripting operation * Index: src/include/optimizer/planmain.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/optimizer/planmain.h,v retrieving revision 1.108 diff -c -r1.108 planmain.h *** src/include/optimizer/planmain.h 2 May 2008 21:26:10 -0000 1.108 --- src/include/optimizer/planmain.h 28 Jul 2008 08:14:18 -0000 *************** *** 34,39 **** --- 34,40 ---- */ extern Plan *optimize_minmax_aggregates(PlannerInfo *root, List *tlist, Path *best_path); + extern Aggref *find_aggref(Node *node); /* * prototypes for plan/createplan.c *************** *** 65,70 **** --- 66,74 ---- List *distinctList, AttrNumber flagColIdx); extern Result *make_result(PlannerInfo *root, List *tlist, Node *resconstantqual, Plan *subplan); + extern Window *make_window(PlannerInfo *root, List *tlist, + WindowClause *parse, Oid *prtOperators, Oid *ordOperators, + Plan *lefttree); extern bool is_projection_capable_plan(Plan *plan); /* Index: src/include/optimizer/var.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/optimizer/var.h,v retrieving revision 1.37 diff -c -r1.37 var.h *** src/include/optimizer/var.h 1 Jan 2008 19:45:58 -0000 1.37 --- src/include/optimizer/var.h 28 Jul 2008 08:14:18 -0000 *************** *** 27,31 **** --- 27,32 ---- extern int find_minimum_var_level(Node *node); extern List *pull_var_clause(Node *node, bool includeUpperVars); extern Node *flatten_join_alias_vars(PlannerInfo *root, Node *node); + extern Node *find_winexpr(TargetEntry *tle); #endif /* VAR_H */ Index: src/include/parser/parse_agg.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/parser/parse_agg.h,v retrieving revision 1.36 diff -c -r1.36 parse_agg.h *** src/include/parser/parse_agg.h 1 Jan 2008 19:45:58 -0000 1.36 --- src/include/parser/parse_agg.h 28 Jul 2008 08:14:18 -0000 *************** *** 16,21 **** --- 16,22 ---- #include "parser/parse_node.h" extern void transformAggregateCall(ParseState *pstate, Aggref *agg); + extern void transformWinAggregateCall(ParseState *pstate, WinAggref *agg); extern void parseCheckAggregates(ParseState *pstate, Query *qry); Index: src/include/parser/parse_clause.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/parser/parse_clause.h,v retrieving revision 1.49 diff -c -r1.49 parse_clause.h *** src/include/parser/parse_clause.h 1 Jan 2008 19:45:58 -0000 1.49 --- src/include/parser/parse_clause.h 28 Jul 2008 08:14:18 -0000 *************** *** 30,38 **** --- 30,45 ---- List **targetlist, List *sortClause); extern List *transformSortClause(ParseState *pstate, List *orderlist, List **targetlist, bool resolveUnknown); + extern List *transformOrderClause(ParseState *pstate, List *orderlist, + List **targetlist, bool resolveUnknown); + extern List *transformPartitionClause(ParseState *pstate, List *partitionlist, + List **targetlist); extern List *transformDistinctClause(ParseState *pstate, List *distinctlist, List **targetlist, List **sortClause); + extern List *transformWinDef(ParseState *pstate, + List *windefinition, List **targetlist); + extern List *addAllTargetsToSortList(ParseState *pstate, List *sortlist, List *targetlist, bool resolveUnknown); *************** *** 40,46 **** --- 47,58 ---- List *sortlist, List *targetlist, SortByDir sortby_dir, SortByNulls sortby_nulls, List *sortby_opname, bool resolveUnknown); + extern List *addTargetToOrderList(ParseState *pstate, TargetEntry *tle, + List *orderlist, List *targetlist, + SortByDir sortby_dir, SortByNulls sortby_nulls, + List *sortby_opname, bool resolveUnknown); extern Index assignSortGroupRef(TargetEntry *tle, List *tlist); + extern Index assignOverRef(TargetEntry *tle, List *tlist); extern bool targetIsInSortList(TargetEntry *tle, Oid sortop, List *sortList); #endif /* PARSE_CLAUSE_H */ Index: src/include/parser/parse_func.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/parser/parse_func.h,v retrieving revision 1.60 diff -c -r1.60 parse_func.h *** src/include/parser/parse_func.h 16 Jul 2008 01:30:23 -0000 1.60 --- src/include/parser/parse_func.h 28 Jul 2008 08:14:18 -0000 *************** *** 44,50 **** extern Node *ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, bool agg_star, bool agg_distinct, bool func_variadic, ! bool is_column, int location); extern FuncDetailCode func_get_detail(List *funcname, List *fargs, int nargs, Oid *argtypes, bool expand_variadic, --- 44,50 ---- extern Node *ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, bool agg_star, bool agg_distinct, bool func_variadic, ! bool is_column, WinDef *win_def, int location); extern FuncDetailCode func_get_detail(List *funcname, List *fargs, int nargs, Oid *argtypes, bool expand_variadic, Index: src/include/parser/parse_node.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/parser/parse_node.h,v retrieving revision 1.54 diff -c -r1.54 parse_node.h *** src/include/parser/parse_node.h 19 Jun 2008 00:46:06 -0000 1.54 --- src/include/parser/parse_node.h 28 Jul 2008 08:14:18 -0000 *************** *** 80,85 **** --- 80,87 ---- bool p_is_update; Relation p_target_relation; RangeTblEntry *p_target_rangetblentry; + List *p_windef_list; + bool p_hasWindow; } ParseState; extern ParseState *make_parsestate(ParseState *parentParseState); Index: src/include/rewrite/rewriteManip.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/rewrite/rewriteManip.h,v retrieving revision 1.44 diff -c -r1.44 rewriteManip.h *** src/include/rewrite/rewriteManip.h 1 Jan 2008 19:45:58 -0000 1.44 --- src/include/rewrite/rewriteManip.h 28 Jul 2008 08:14:18 -0000 *************** *** 34,39 **** --- 34,40 ---- extern void AddInvertedQual(Query *parsetree, Node *qual); extern bool checkExprHasAggs(Node *node); + extern bool checkExprHasWinAggs(Node *node); extern bool checkExprHasSubLink(Node *node); extern Node *ResolveNew(Node *node, int target_varno, int sublevels_up, Index: src/interfaces/ecpg/preproc/preproc.y =================================================================== RCS file: /projects/cvsroot/pgsql/src/interfaces/ecpg/preproc/preproc.y,v retrieving revision 1.369 diff -c -r1.369 preproc.y *** src/interfaces/ecpg/preproc/preproc.y 16 Jul 2008 01:30:23 -0000 1.369 --- src/interfaces/ecpg/preproc/preproc.y 28 Jul 2008 08:14:20 -0000 *************** *** 465,473 **** NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF NULLS_P NUMERIC OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OR ORDER ! OUT_P OUTER_P OVERLAPS OVERLAY OWNED OWNER ! PARSER PARTIAL PASSWORD PLACING PLANS POSITION PRECISION PRESERVE PREPARE PREPARED PRIMARY PRIOR PRIVILEGES PROCEDURAL PROCEDURE --- 465,473 ---- NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF NULLS_P NUMERIC OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OR ORDER ! OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER ! PARSER PARTIAL PARTITION PASSWORD PLACING PLANS POSITION PRECISION PRESERVE PREPARE PREPARED PRIMARY PRIOR PRIVILEGES PROCEDURAL PROCEDURE