aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analyze.c3
-rw-r--r--src/attach.c2
-rw-r--r--src/expr.c28
-rw-r--r--src/func.c2
-rw-r--r--src/parse.y60
-rw-r--r--src/resolve.c48
-rw-r--r--src/select.c357
-rw-r--r--src/sqliteInt.h61
-rw-r--r--src/test1.c7
-rw-r--r--src/vdbe.c6
-rw-r--r--src/vdbeInt.h1
-rw-r--r--src/vdbemem.c17
-rw-r--r--src/window.c53
13 files changed, 608 insertions, 37 deletions
diff --git a/src/analyze.c b/src/analyze.c
index 48fd4951c..9492e2c2f 100644
--- a/src/analyze.c
+++ b/src/analyze.c
@@ -485,6 +485,7 @@ static const FuncDef statInitFuncdef = {
0, /* pNext */
statInit, /* xSFunc */
0, /* xFinalize */
+ 0, 0,
"stat_init", /* zName */
{0}
};
@@ -801,6 +802,7 @@ static const FuncDef statPushFuncdef = {
0, /* pNext */
statPush, /* xSFunc */
0, /* xFinalize */
+ 0, 0,
"stat_push", /* zName */
{0}
};
@@ -952,6 +954,7 @@ static const FuncDef statGetFuncdef = {
0, /* pNext */
statGet, /* xSFunc */
0, /* xFinalize */
+ 0, 0,
"stat_get", /* zName */
{0}
};
diff --git a/src/attach.c b/src/attach.c
index ca0fd9fd1..1f276156b 100644
--- a/src/attach.c
+++ b/src/attach.c
@@ -414,6 +414,7 @@ void sqlite3Detach(Parse *pParse, Expr *pDbname){
0, /* pNext */
detachFunc, /* xSFunc */
0, /* xFinalize */
+ 0, 0,
"sqlite_detach", /* zName */
{0}
};
@@ -433,6 +434,7 @@ void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){
0, /* pNext */
attachFunc, /* xSFunc */
0, /* xFinalize */
+ 0, 0,
"sqlite_attach", /* zName */
{0}
};
diff --git a/src/expr.c b/src/expr.c
index 6aff83a25..a1407a85f 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -1063,6 +1063,9 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
}else{
sqlite3ExprListDelete(db, p->x.pList);
}
+ if( !ExprHasProperty(p, EP_Reduced) ){
+ sqlite3WindowDelete(db, p->pWin);
+ }
}
if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken);
if( !ExprHasProperty(p, EP_Static) ){
@@ -1305,6 +1308,24 @@ static With *withDup(sqlite3 *db, With *p){
# define withDup(x,y) 0
#endif
+static Window *winDup(sqlite3 *db, Window *p){
+ Window *pNew = 0;
+ if( p ){
+ pNew = sqlite3DbMallocZero(db, sizeof(Window));
+ if( pNew ){
+ pNew->pFilter = sqlite3ExprDup(db, p->pFilter, 0);
+ pNew->pPartition = sqlite3ExprListDup(db, p->pPartition, 0);
+ pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, 0);
+ pNew->eType = p->eType;
+ pNew->eEnd = p->eEnd;
+ pNew->eStart = p->eStart;
+ pNew->pStart = sqlite3ExprDup(db, pNew->pStart, 0);
+ pNew->pEnd = sqlite3ExprDup(db, pNew->pEnd, 0);
+ }
+ }
+ return pNew;
+}
+
/*
** The following group of routines make deep copies of expressions,
** expression lists, ID lists, and select statements. The copies can
@@ -1469,6 +1490,7 @@ Select *sqlite3SelectDup(sqlite3 *db, Select *pDup, int flags){
pNew->addrOpenEphm[1] = -1;
pNew->nSelectRow = p->nSelectRow;
pNew->pWith = withDup(db, p->pWith);
+ pNew->pWin = winDup(db, p->pWin);
sqlite3SelectSetName(pNew, p->zSelName);
*pp = pNew;
pp = &pNew->pPrior;
@@ -3778,6 +3800,10 @@ expr_code_doover:
u8 enc = ENC(db); /* The text encoding used by this database */
CollSeq *pColl = 0; /* A collating sequence */
+ if( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) && pExpr->pWin ){
+ return pExpr->pWin->regResult;
+ }
+
if( ConstFactorOk(pParse) && sqlite3ExprIsConstantNotJoin(pExpr) ){
/* SQL functions can be expensive. So try to move constant functions
** out of the inner loop, even if that means an extra OP_Copy. */
@@ -3798,7 +3824,7 @@ expr_code_doover:
pDef = sqlite3FindFunction(db, "unknown", nFarg, enc, 0);
}
#endif
- if( pDef==0 || pDef->xFinalize!=0 ){
+ if( pDef==0 /* || pDef->xFinalize!=0 */ ){
sqlite3ErrorMsg(pParse, "unknown function: %s()", zId);
break;
}
diff --git a/src/func.c b/src/func.c
index 17a267e22..f5a839d39 100644
--- a/src/func.c
+++ b/src/func.c
@@ -1859,7 +1859,7 @@ void sqlite3RegisterBuiltinFunctions(void){
FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ),
FUNCTION(substr, 2, 0, 0, substrFunc ),
FUNCTION(substr, 3, 0, 0, substrFunc ),
- AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize ),
+ WFUNCTION(sum, 1, 0, sumStep, sumFinalize, sumFinalize, 0),
AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ),
AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ),
AGGREGATE2(count, 0, 0, 0, countStep, countFinalize,
diff --git a/src/parse.y b/src/parse.y
index aca3bfb1c..1d1a2ddaa 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -99,6 +99,8 @@
*/
struct TrigEvent { int a; IdList * b; };
+struct FrameBound { int eType; Expr *pExpr; };
+
/*
** Disable lookaside memory allocation for objects that might be
** shared across database connections.
@@ -209,7 +211,7 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);}
CONFLICT DATABASE DEFERRED DESC DETACH DO
EACH END EXCLUSIVE EXPLAIN FAIL FOR
IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH NO PLAN
- QUERY KEY OF OFFSET PRAGMA RAISE RECURSIVE RELEASE REPLACE RESTRICT ROW
+ QUERY KEY OF OFFSET PRAGMA RAISE RECURSIVE RELEASE REPLACE RESTRICT ROW ROWS
ROLLBACK SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL WITH WITHOUT
%ifdef SQLITE_OMIT_COMPOUND_SELECT
EXCEPT INTERSECT UNION
@@ -1001,11 +1003,12 @@ expr(A) ::= CAST LP expr(E) AS typetoken(T) RP. {
sqlite3ExprAttachSubtrees(pParse->db, A, E, 0);
}
%endif SQLITE_OMIT_CAST
-expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP. {
+expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP window(Z). {
if( Y && Y->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){
sqlite3ErrorMsg(pParse, "too many arguments on function %T", &X);
}
A = sqlite3ExprFunction(pParse, Y, &X);
+ sqlite3WindowAttach(pParse, A, Z);
if( D==SF_Distinct && A ){
A->flags |= EP_Distinct;
}
@@ -1017,6 +1020,59 @@ term(A) ::= CTIME_KW(OP). {
A = sqlite3ExprFunction(pParse, 0, &OP);
}
+
+%type window {Window*}
+%destructor window {sqlite3WindowDelete(pParse->db, $$);}
+
+%type frame_opt {Window*}
+%destructor frame_opt {sqlite3WindowDelete(pParse->db, $$);}
+
+%type part_opt {ExprList*}
+%destructor part_opt {sqlite3ExprListDelete(pParse->db, $$);}
+
+%type filter_opt {Expr*}
+%destructor filter_opt {sqlite3ExprDelete(pParse->db, $$);}
+
+%type range_or_rows {int}
+
+%type frame_bound {struct FrameBound}
+%destructor frame_bound {sqlite3ExprDelete(pParse->db, $$.pExpr);}
+
+window(A) ::= . { A = 0; }
+window(A) ::= filter_opt(W) OVER LP part_opt(X) orderby_opt(Y) frame_opt(Z) RP.{
+ if( Z ){
+ A = Z;
+ A->pFilter = W;
+ A->pPartition = X;
+ A->pOrderBy = Y;
+ }
+}
+
+part_opt(A) ::= PARTITION BY exprlist(X). { A = X; }
+part_opt(A) ::= . { A = 0; }
+filter_opt(A) ::= . { A = 0; }
+filter_opt(A) ::= FILTER LP WHERE expr(X) RP. { A = X; }
+
+frame_opt(A) ::= . {
+ A = sqlite3WindowAlloc(pParse, TK_RANGE, TK_UNBOUNDED, 0, TK_CURRENT, 0);
+}
+frame_opt(A) ::= range_or_rows(X) frame_bound(Y). {
+ A = sqlite3WindowAlloc(pParse, X, Y.eType, Y.pExpr, TK_CURRENT, 0);
+}
+frame_opt(A) ::= range_or_rows(X) BETWEEN frame_bound(Y) AND frame_bound(Z). {
+ A = sqlite3WindowAlloc(pParse, X, Y.eType, Y.pExpr, Z.eType, Z.pExpr);
+}
+
+range_or_rows(A) ::= RANGE. { A = TK_RANGE; }
+range_or_rows(A) ::= ROWS. { A = TK_ROWS; }
+
+frame_bound(A) ::= UNBOUNDED PRECEDING. { A.eType = TK_UNBOUNDED; A.pExpr = 0; }
+frame_bound(A) ::= expr(X) PRECEDING. { A.eType = TK_PRECEDING; A.pExpr = X; }
+frame_bound(A) ::= CURRENT ROW. { A.eType = TK_CURRENT ; A.pExpr = 0; }
+frame_bound(A) ::= expr(X) FOLLOWING. { A.eType = TK_FOLLOWING; A.pExpr = X; }
+frame_bound(A) ::= UNBOUNDED FOLLOWING. { A.eType = TK_UNBOUNDED; A.pExpr = 0; }
+
+
expr(A) ::= LP nexprlist(X) COMMA expr(Y) RP. {
ExprList *pList = sqlite3ExprListAppend(pParse, X, Y);
A = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
diff --git a/src/resolve.c b/src/resolve.c
index 4ed36a479..073fdf193 100644
--- a/src/resolve.c
+++ b/src/resolve.c
@@ -753,8 +753,11 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
NC_IdxExpr|NC_PartIdx);
}
}
- if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){
- sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId);
+ if( (is_agg && (pNC->ncFlags & NC_AllowAgg)==0)
+ || (pExpr->pWin && (pNC->ncFlags & NC_AllowWin)==0)
+ ){
+ const char *zType = pExpr->pWin ? "window" : "aggregate";
+ sqlite3ErrorMsg(pParse, "misuse of %s function %.*s()",zType,nId,zId);
pNC->nErr++;
is_agg = 0;
}else if( no_such_func && pParse->db->init.busy==0
@@ -772,19 +775,28 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
if( is_agg ) pNC->ncFlags &= ~NC_AllowAgg;
sqlite3WalkExprList(pWalker, pList);
if( is_agg ){
- NameContext *pNC2 = pNC;
- pExpr->op = TK_AGG_FUNCTION;
- pExpr->op2 = 0;
- while( pNC2 && !sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){
- pExpr->op2++;
- pNC2 = pNC2->pNext;
+ if( pExpr->pWin ){
+ pExpr->pWin->pNextWin = pNC->pWin;
+ pNC->pWin = pExpr->pWin;
+ pExpr->pWin->pFunc = pDef;
+ pExpr->pWin->nArg = pExpr->x.pList->nExpr;
}
- assert( pDef!=0 );
- if( pNC2 ){
- assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg );
- testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 );
- pNC2->ncFlags |= NC_HasAgg | (pDef->funcFlags & SQLITE_FUNC_MINMAX);
+ else
+ {
+ NameContext *pNC2 = pNC;
+ pExpr->op = TK_AGG_FUNCTION;
+ pExpr->op2 = 0;
+ while( pNC2 && !sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){
+ pExpr->op2++;
+ pNC2 = pNC2->pNext;
+ }
+ assert( pDef!=0 );
+ if( pNC2 ){
+ assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg );
+ testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 );
+ pNC2->ncFlags |= NC_HasAgg | (pDef->funcFlags & SQLITE_FUNC_MINMAX);
+ }
}
pNC->ncFlags |= NC_AllowAgg;
}
@@ -1234,6 +1246,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
nCompound = 0;
pLeftmost = p;
while( p ){
+ assert( p->pWin==0 );
assert( (p->selFlags & SF_Expanded)!=0 );
assert( (p->selFlags & SF_Resolved)==0 );
p->selFlags |= SF_Resolved;
@@ -1291,12 +1304,13 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
/* Set up the local name-context to pass to sqlite3ResolveExprNames() to
** resolve the result-set expression list.
*/
- sNC.ncFlags = NC_AllowAgg;
+ sNC.ncFlags = NC_AllowAgg|NC_AllowWin;
sNC.pSrcList = p->pSrc;
sNC.pNext = pOuterNC;
/* Resolve names in the result set. */
if( sqlite3ResolveExprListNames(&sNC, p->pEList) ) return WRC_Abort;
+ sNC.ncFlags &= ~NC_AllowWin;
/* If there are no aggregate functions in the result-set, and no GROUP BY
** expression, do not allow aggregates in any of the other expressions.
@@ -1345,7 +1359,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
** outer queries
*/
sNC.pNext = 0;
- sNC.ncFlags |= NC_AllowAgg;
+ sNC.ncFlags |= NC_AllowAgg|NC_AllowWin;
/* If this is a converted compound query, move the ORDER BY clause from
** the sub-query back to the parent query. At this point each term
@@ -1376,6 +1390,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
if( db->mallocFailed ){
return WRC_Abort;
}
+ sNC.ncFlags &= ~NC_AllowWin;
/* Resolve the GROUP BY clause. At the same time, make sure
** the GROUP BY clause does not contain aggregate functions.
@@ -1402,6 +1417,9 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
return WRC_Abort;
}
+ p->pWin = sNC.pWin;
+ sNC.pWin = 0;
+
/* Advance to the next term of the compound
*/
p = p->pPrior;
diff --git a/src/select.c b/src/select.c
index 3818ef517..2b5a3dc54 100644
--- a/src/select.c
+++ b/src/select.c
@@ -162,6 +162,7 @@ Select *sqlite3SelectNew(
pNew->pNext = 0;
pNew->pLimit = pLimit;
pNew->pWith = 0;
+ pNew->pWin = 0;
if( pParse->db->mallocFailed ) {
clearSelect(pParse->db, pNew, pNew!=&standin);
pNew = 0;
@@ -3719,6 +3720,8 @@ static int flattenSubquery(
pSub = pSubitem->pSelect;
assert( pSub!=0 );
+ if( p->pWin ) return 0;
+
pSubSrc = pSub->pSrc;
assert( pSubSrc );
/* Prior to version 3.1.2, when LIMIT and OFFSET had to be simple constants,
@@ -4588,6 +4591,27 @@ static void selectPopWith(Walker *pWalker, Select *p){
#define selectPopWith 0
#endif
+static int selectExpandSubquery(Parse *pParse, struct SrcList_item *pFrom){
+ Select *pSel = pFrom->pSelect;
+ Table *pTab;
+
+ pFrom->pTab = pTab = sqlite3DbMallocZero(pParse->db, sizeof(Table));
+ if( pTab==0 ) return WRC_Abort;
+ pTab->nTabRef = 1;
+ if( pFrom->zAlias ){
+ pTab->zName = sqlite3DbStrDup(pParse->db, pFrom->zAlias);
+ }else{
+ pTab->zName = sqlite3MPrintf(pParse->db, "subquery_%p", (void*)pTab);
+ }
+ while( pSel->pPrior ){ pSel = pSel->pPrior; }
+ sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol);
+ pTab->iPKey = -1;
+ pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
+ pTab->tabFlags |= TF_Ephemeral;
+
+ return WRC_Continue;
+}
+
/*
** This routine is a Walker callback for "expanding" a SELECT statement.
** "Expanding" means to do the following:
@@ -4660,6 +4684,8 @@ static int selectExpander(Walker *pWalker, Select *p){
assert( pSel!=0 );
assert( pFrom->pTab==0 );
if( sqlite3WalkSelect(pWalker, pSel) ) return WRC_Abort;
+ if( selectExpandSubquery(pParse, pFrom) ) return WRC_Abort;
+#if 0
pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table));
if( pTab==0 ) return WRC_Abort;
pTab->nTabRef = 1;
@@ -4674,6 +4700,7 @@ static int selectExpander(Walker *pWalker, Select *p){
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
pTab->tabFlags |= TF_Ephemeral;
#endif
+#endif
}else{
/* An ordinary table or view name in the FROM clause */
assert( pFrom->pTab==0 );
@@ -5375,6 +5402,201 @@ static int countOfViewOptimization(Parse *pParse, Select *p){
}
#endif /* SQLITE_COUNTOFVIEW_OPTIMIZATION */
+typedef struct WindowRewrite WindowRewrite;
+struct WindowRewrite {
+ Window *pWin;
+ ExprList *pSub;
+};
+
+static int selectWindowRewriteSelectCb(Walker *pWalker, Select *pSelect){
+ return WRC_Prune;
+}
+
+static int selectWindowRewriteExprCb(Walker *pWalker, Expr *pExpr){
+ struct WindowRewrite *p = pWalker->u.pRewrite;
+ Parse *pParse = pWalker->pParse;
+ int rc = WRC_Continue;
+
+ switch( pExpr->op ){
+ case TK_COLUMN: {
+ Expr *pDup = sqlite3ExprDup(pParse->db, pExpr, 0);
+ p->pSub = sqlite3ExprListAppend(pParse, p->pSub, pDup);
+ if( p->pSub ){
+ assert( ExprHasProperty(pExpr, EP_Static)==0 );
+ ExprSetProperty(pExpr, EP_Static);
+ sqlite3ExprDelete(pParse->db, pExpr);
+ ExprClearProperty(pExpr, EP_Static);
+ memset(pExpr, 0, sizeof(Expr));
+
+ pExpr->op = TK_COLUMN;
+ pExpr->iColumn = p->pSub->nExpr-1;
+ pExpr->iTable = p->pWin->iEphCsr;
+ }
+
+ break;
+ }
+
+ case TK_FUNCTION:
+ if( pExpr->pWin ){
+ rc = WRC_Prune;
+ pExpr->pWin->pOwner = pExpr;
+ }
+ break;
+
+ default: /* no-op */
+ break;
+ }
+
+ return rc;
+}
+
+static int selectWindowRewriteEList(
+ Parse *pParse,
+ Window *pWin,
+ ExprList *pEList, /* Rewrite expressions in this list */
+ ExprList **ppSub /* IN/OUT: Sub-select expression-list */
+){
+ Walker sWalker;
+ WindowRewrite sRewrite;
+ int rc;
+
+ memset(&sWalker, 0, sizeof(Walker));
+ memset(&sRewrite, 0, sizeof(WindowRewrite));
+
+ sRewrite.pSub = *ppSub;
+ sRewrite.pWin = pWin;
+
+ sWalker.pParse = pParse;
+ sWalker.xExprCallback = selectWindowRewriteExprCb;
+ sWalker.xSelectCallback = selectWindowRewriteSelectCb;
+ sWalker.u.pRewrite = &sRewrite;
+
+ rc = sqlite3WalkExprList(&sWalker, pEList);
+
+ *ppSub = sRewrite.pSub;
+ return rc;
+}
+
+static ExprList *exprListAppendList(
+ Parse *pParse, /* Parsing context */
+ ExprList *pList, /* List to which to append. Might be NULL */
+ ExprList *pAppend /* List of values to append. Might be NULL */
+){
+ if( pAppend ){
+ int i;
+ int nInit = pList ? pList->nExpr : 0;
+ for(i=0; i<pAppend->nExpr; i++){
+ Expr *pDup = sqlite3ExprDup(pParse->db, pAppend->a[i].pExpr, 0);
+ pList = sqlite3ExprListAppend(pParse, pList, pDup);
+ if( pList ) pList->a[nInit+i].sortOrder = pAppend->a[i].sortOrder;
+ }
+ }
+ return pList;
+}
+
+/*
+** If the SELECT statement passed as the second argument does not invoke
+** any SQL window functions, this function is a no-op. Otherwise, it
+** rewrites the SELECT statement so that window function xStep functions
+** are invoked in the correct order. The simplest version of the
+** transformation is:
+**
+** SELECT win(args...) OVER (<list1>) FROM <src> ORDER BY <list2>
+**
+** to
+**
+** SELECT win(args...) FROM (
+** SELECT args... FROM <src> ORDER BY <list1>
+** ) ORDER BY <list2>
+**
+** where <src> may contain WHERE, GROUP BY and HAVING clauses, and <list1>
+** is the concatenation of the PARTITION BY and ORDER BY clauses in the
+** OVER clause.
+**
+*/
+static int selectWindowRewrite(Parse *pParse, Select *p){
+ int rc = SQLITE_OK;
+ if( p->pWin ){
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ int i;
+ sqlite3 *db = pParse->db;
+ Select *pSub = 0; /* The subquery */
+ SrcList *pSrc = p->pSrc;
+ Expr *pWhere = p->pWhere;
+ ExprList *pGroupBy = p->pGroupBy;
+ Expr *pHaving = p->pHaving;
+ ExprList *pSort = 0;
+
+ ExprList *pSublist = 0; /* Expression list for sub-query */
+ Window *pWin = p->pWin;
+
+ /* TODO: This is of course temporary requirements */
+ assert( pWin->pNextWin==0 );
+
+ p->pSrc = 0;
+ p->pWhere = 0;
+ p->pGroupBy = 0;
+ p->pHaving = 0;
+
+ pWin->regAccum = ++pParse->nMem;
+ pWin->regResult = ++pParse->nMem;
+
+ /* Assign a cursor number for the ephemeral table used to buffer rows.
+ ** The OpenEphemeral instruction is coded later, after it is known how
+ ** many columns the table will have. */
+ pWin->iEphCsr = pParse->nTab++;
+
+ rc = selectWindowRewriteEList(pParse, pWin, p->pEList, &pSublist);
+ if( rc ) return rc;
+ rc = selectWindowRewriteEList(pParse, pWin, p->pOrderBy, &pSublist);
+ if( rc ) return rc;
+ pWin->nBufferCol = (pSublist ? pSublist->nExpr : 0);
+
+ /* Create the ORDER BY clause for the sub-select. This is the concatenation
+ ** of the window PARTITION and ORDER BY clauses. Append the same
+ ** expressions to the sub-select expression list. They are required to
+ ** figure out where boundaries for partitions and sets of peer rows. */
+ pSort = sqlite3ExprListDup(db, pWin->pPartition, 0);
+ if( pWin->pOrderBy ){
+ pSort = exprListAppendList(pParse, pSort, pWin->pOrderBy);
+ }
+ pSublist = exprListAppendList(pParse, pSublist, pSort);
+
+ /* Also append the arguments passed to the window function to the
+ ** sub-select expression list. */
+ pWin->iArgCol = (pSublist ? pSublist->nExpr : 0);
+ pSublist = exprListAppendList(pParse, pSublist, pWin->pOwner->x.pList);
+
+ pSub = sqlite3SelectNew(
+ pParse, pSublist, pSrc, pWhere, pGroupBy, pHaving, pSort, 0, 0
+ );
+ p->pSrc = sqlite3SrcListAppend(db, 0, 0, 0);
+ if( p->pSrc ){
+ int iTab;
+ ExprList *pList = 0;
+ p->pSrc->a[0].pSelect = pSub;
+ sqlite3SrcListAssignCursors(pParse, p->pSrc);
+ if( selectExpandSubquery(pParse, &p->pSrc->a[0]) ){
+ rc = SQLITE_NOMEM;
+ }else{
+ pSub->selFlags |= SF_Expanded;
+ }
+ }
+
+#if SELECTTRACE_ENABLED
+ if( sqlite3SelectTrace & 0x108 ){
+ SELECTTRACE(0x104,pParse,p, ("after window rewrite:\n"));
+ sqlite3TreeViewSelect(0, p, 0);
+ }
+#endif
+
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pWin->iEphCsr, pWin->nBufferCol);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum);
+ }
+
+ return rc;
+}
+
/*
** Generate code for the SELECT statement given in the p argument.
**
@@ -5443,7 +5665,6 @@ int sqlite3Select(
sqlite3SelectPrep(pParse, p, 0);
memset(&sSort, 0, sizeof(sSort));
sSort.pOrderBy = p->pOrderBy;
- pTabList = p->pSrc;
if( pParse->nErr || db->mallocFailed ){
goto select_end;
}
@@ -5460,6 +5681,11 @@ int sqlite3Select(
generateColumnNames(pParse, p);
}
+ if( (rc = selectWindowRewrite(pParse, p)) ){
+ goto select_end;
+ }
+ pTabList = p->pSrc;
+
/* Try to various optimizations (flattening subqueries, and strength
** reduction of join operators) in the FROM clause up into the main query
*/
@@ -5833,11 +6059,24 @@ int sqlite3Select(
}
if( !isAgg && pGroupBy==0 ){
+ Window *pWin = p->pWin;
+ int regPart = 0;
+
/* No aggregate functions and no GROUP BY clause */
u16 wctrlFlags = (sDistinct.isTnct ? WHERE_WANT_DISTINCT : 0);
assert( WHERE_USE_LIMIT==SF_FixedLimit );
wctrlFlags |= p->selFlags & SF_FixedLimit;
+ if( pWin ){
+ int nPart = (pWin->pPartition ? pWin->pPartition->nExpr : 0);
+ nPart += (pWin->pOrderBy ? pWin->pOrderBy->nExpr : 0);
+ if( nPart ){
+ regPart = pParse->nMem+1;
+ pParse->nMem += nPart;
+ sqlite3VdbeAddOp3(v, OP_Null, 0, regPart, regPart+nPart-1);
+ }
+ }
+
/* Begin the database scan. */
SELECTTRACE(1,pParse,p,("WhereBegin\n"));
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy,
@@ -5865,15 +6104,117 @@ int sqlite3Select(
sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex);
}
- /* Use the standard inner loop. */
assert( p->pEList==pEList );
- selectInnerLoop(pParse, p, -1, &sSort, &sDistinct, pDest,
- sqlite3WhereContinueLabel(pWInfo),
- sqlite3WhereBreakLabel(pWInfo));
+ if( p->pWin ){
+ int k;
+ int iSubCsr = p->pSrc->a[0].iCursor;
+ int nSub = p->pSrc->a[0].pTab->nCol;
+ int reg = pParse->nMem+1;
+ int regRecord = reg+nSub;
+ int regRowid = regRecord+1;
+ int regGosub = regRowid+1;
+ int addr;
+ int addrGosub;
+
+ pParse->nMem += nSub + 3;
+
+ /* Martial the row returned by the sub-select into an array of
+ ** registers. */
+ for(k=0; k<nSub; k++){
+ sqlite3VdbeAddOp3(v, OP_Column, iSubCsr, k, reg+k);
+ }
- /* End the database scan loop.
- */
- sqlite3WhereEnd(pWInfo);
+ /* Check if this is the start of a new partition or peer group. */
+ if( regPart ){
+ ExprList *pPart = pWin->pPartition;
+ int nPart = (pPart ? pPart->nExpr : 0);
+ ExprList *pOrderBy = pWin->pOrderBy;
+ int nPeer = (pOrderBy ? pOrderBy->nExpr : 0);
+ int addrGoto = 0;
+ int addrJump = 0;
+
+ if( pPart ){
+ int regNewPart = reg + pWin->nBufferCol;
+ KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pPart, 0, 0);
+ addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, regPart, nPart);
+ sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
+ addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
+ sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, pWin->nArg);
+ sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
+ sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult);
+ if( pOrderBy ){
+ addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
+ }
+ }
+
+ if( pOrderBy ){
+ int regNewPeer = reg + pWin->nBufferCol + nPart;
+ int regPeer = regPart + nPart;
+
+ KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pOrderBy, 0, 0);
+ if( addrJump ) sqlite3VdbeJumpHere(v, addrJump);
+ addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer, nPeer);
+ sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
+ addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
+ sqlite3VdbeAddOp3(v,
+ OP_AggFinal, pWin->regAccum, pWin->nArg, pWin->regResult
+ );
+ sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
+
+ if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto);
+ }
+
+ addrGosub = sqlite3VdbeAddOp1(v, OP_Gosub, regGosub);
+ sqlite3VdbeAddOp1(v, OP_ResetSorter, pWin->iEphCsr);
+ sqlite3VdbeAddOp3(v,OP_Copy,reg+pWin->nBufferCol,regPart,nPart+nPeer-1);
+
+ sqlite3VdbeJumpHere(v, addrJump);
+ }
+
+ /* Invoke step function for window functions */
+ sqlite3VdbeAddOp3(v, OP_AggStep0, 0, reg+pWin->iArgCol, pWin->regAccum);
+ sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, (u8)pWin->nArg);
+
+ /* Buffer the current row in the ephemeral table. */
+ if( pWin->nBufferCol>0 ){
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, pWin->nBufferCol, regRecord);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Blob, 0, regRecord);
+ sqlite3VdbeAppendP4(v, (void*)"", 0);
+ }
+ sqlite3VdbeAddOp2(v, OP_NewRowid, pWin->iEphCsr, regRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, pWin->iEphCsr, regRecord, regRowid);
+
+ /* End the database scan loop. */
+ sqlite3WhereEnd(pWInfo);
+
+ sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, pWin->nArg);
+ sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
+ sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult);
+ sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, sqlite3VdbeCurrentAddr(v)+2);
+
+ sqlite3VdbeAddOp0(v, OP_Goto);
+ if( regPart ){
+ sqlite3VdbeJumpHere(v, addrGosub);
+ }
+ addr = sqlite3VdbeAddOp1(v, OP_Rewind, pWin->iEphCsr);
+ selectInnerLoop(pParse, p, -1, &sSort, &sDistinct, pDest, addr+1, 0);
+ sqlite3VdbeAddOp2(v, OP_Next, pWin->iEphCsr, addr+1);
+ sqlite3VdbeJumpHere(v, addr);
+ sqlite3VdbeAddOp1(v, OP_Return, regGosub);
+ sqlite3VdbeJumpHere(v, addr-1); /* OP_Goto jumps here */
+
+ }else{
+ /* Use the standard inner loop. */
+ selectInnerLoop(pParse, p, -1, &sSort, &sDistinct, pDest,
+ sqlite3WhereContinueLabel(pWInfo),
+ sqlite3WhereBreakLabel(pWInfo));
+
+ /* End the database scan loop.
+ */
+ sqlite3WhereEnd(pWInfo);
+ }
}else{
/* This case when there exist aggregate functions or a GROUP BY clause
** or both */
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 7b19e0a98..8a1376fd2 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -1107,6 +1107,7 @@ typedef struct VTable VTable;
typedef struct VtabCtx VtabCtx;
typedef struct Walker Walker;
typedef struct WhereInfo WhereInfo;
+typedef struct Window Window;
typedef struct With With;
/* A VList object records a mapping between parameters/variables/wildcards
@@ -1588,6 +1589,8 @@ struct FuncDef {
FuncDef *pNext; /* Next function with same name */
void (*xSFunc)(sqlite3_context*,int,sqlite3_value**); /* func or agg-step */
void (*xFinalize)(sqlite3_context*); /* Agg finalizer */
+ void (*xValue)(sqlite3_context*); /* Current agg value */
+ void (*xInverse)(sqlite3_context*,int,sqlite3_value**); /* inverse agg-step */
const char *zName; /* SQL name of the function. */
union {
FuncDef *pHash; /* Next with a different name but the same hash */
@@ -1678,6 +1681,12 @@ struct FuncDestructor {
** are interpreted in the same way as the first 4 parameters to
** FUNCTION().
**
+** WFUNCTION(zName, nArg, iArg, xStep, xFinal, xValue, xInverse)
+** Used to create an aggregate function definition implemented by
+** the C functions xStep and xFinal. The first four parameters
+** are interpreted in the same way as the first 4 parameters to
+** FUNCTION().
+**
** LIKEFUNC(zName, nArg, pArg, flags)
** Used to create a scalar function definition of a function zName
** that accepts nArg arguments and is implemented by a call to C
@@ -1688,31 +1697,35 @@ struct FuncDestructor {
*/
#define FUNCTION(zName, nArg, iArg, bNC, xFunc) \
{nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
- SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} }
+ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
#define VFUNCTION(zName, nArg, iArg, bNC, xFunc) \
{nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
- SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} }
+ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
#define DFUNCTION(zName, nArg, iArg, bNC, xFunc) \
{nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8, \
- 0, 0, xFunc, 0, #zName, {0} }
+ 0, 0, xFunc, 0, 0, 0, #zName, {0} }
#define PURE_DATE(zName, nArg, iArg, bNC, xFunc) \
{nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \
- (void*)&sqlite3Config, 0, xFunc, 0, #zName, {0} }
+ (void*)&sqlite3Config, 0, xFunc, 0, 0, 0, #zName, {0} }
#define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \
{nArg,SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\
- SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} }
+ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
#define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \
{nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
- pArg, 0, xFunc, 0, #zName, }
+ pArg, 0, xFunc, 0, 0, 0, #zName, }
#define LIKEFUNC(zName, nArg, arg, flags) \
{nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|flags, \
- (void *)arg, 0, likeFunc, 0, #zName, {0} }
+ (void *)arg, 0, likeFunc, 0, 0, 0, #zName, {0} }
#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \
{nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL), \
- SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,#zName, {0}}
+ SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,0,0,#zName, {0}}
#define AGGREGATE2(zName, nArg, arg, nc, xStep, xFinal, extraFlags) \
{nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|extraFlags, \
- SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,#zName, {0}}
+ SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,0,0,#zName, {0}}
+
+#define WFUNCTION(zName, nArg, arg, xStep, xFinal, xValue, xInverse) \
+ {nArg, SQLITE_UTF8, \
+ SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xValue,xInverse,#zName, {0}}
/*
** All current savepoints are stored in a linked list starting at
@@ -2413,6 +2426,7 @@ struct Expr {
AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
Table *pTab; /* Table for TK_COLUMN expressions. Can be NULL
** for a column of an index on an expression */
+ Window *pWin; /* Window definition for window functions */
};
/*
@@ -2699,6 +2713,7 @@ struct NameContext {
int nRef; /* Number of names resolved by this context */
int nErr; /* Number of errors encountered while resolving names */
u16 ncFlags; /* Zero or more NC_* flags defined below */
+ Window *pWin; /* List of window functions in this context */
};
/*
@@ -2721,6 +2736,7 @@ struct NameContext {
#define NC_UUpsert 0x0200 /* True if uNC.pUpsert is used */
#define NC_MinMaxAgg 0x1000 /* min/max aggregates seen. See note above */
#define NC_Complex 0x2000 /* True if a function or subquery seen */
+#define NC_AllowWin 0x4000 /* Window functions are allowed here */
/*
** An instance of the following object describes a single ON CONFLICT
@@ -2788,6 +2804,7 @@ struct Select {
Select *pNext; /* Next select to the left in a compound */
Expr *pLimit; /* LIMIT expression. NULL means not used. */
With *pWith; /* WITH clause attached to this select. Or NULL. */
+ Window *pWin; /* List of window functions */
};
/*
@@ -3401,6 +3418,7 @@ struct Walker {
struct IdxExprTrans *pIdxTrans; /* Convert idxed expr to column */
ExprList *pGroupBy; /* GROUP BY clause */
Select *pSelect; /* HAVING to WHERE clause ctx */
+ struct WindowRewrite *pRewrite; /* Window rewrite context */
} u;
};
@@ -3451,6 +3469,31 @@ struct TreeView {
};
#endif /* SQLITE_DEBUG */
+struct Window {
+ Expr *pFilter;
+ ExprList *pPartition;
+ ExprList *pOrderBy;
+ u8 eType; /* TK_RANGE or TK_ROWS */
+ u8 eStart; /* UNBOUNDED, CURRENT, PRECEDING or FOLLOWING */
+ u8 eEnd; /* UNBOUNDED, CURRENT, PRECEDING or FOLLOWING */
+ Expr *pStart; /* Expression for "<expr> PRECEDING" */
+ Expr *pEnd; /* Expression for "<expr> FOLLOWING" */
+ Window *pNextWin; /* Next window function belonging to this SELECT */
+ int iEphCsr; /* Temp table used by this window */
+ int regAccum;
+ int regResult;
+ FuncDef *pFunc;
+ int nArg;
+
+ Expr *pOwner; /* Expression object this window is attached to */
+ int nBufferCol; /* Number of columns in buffer table */
+ int iArgCol; /* Offset of first argument for this function */
+};
+
+void sqlite3WindowDelete(sqlite3*, Window*);
+Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*);
+void sqlite3WindowAttach(Parse*, Expr*, Window*);
+
/*
** Assuming zIn points to the first byte of a UTF-8 character,
** advance zIn to point to the first byte of the next UTF-8 character.
diff --git a/src/test1.c b/src/test1.c
index f2511d259..b62c3104f 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -7799,6 +7799,9 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
extern int sqlite3_fts3_enable_parentheses;
#endif
#endif
+#if defined(SQLITE_ENABLE_SELECTTRACE)
+ extern int sqlite3SelectTrace;
+#endif
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
@@ -7884,6 +7887,10 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
(char*)&sqlite3_sync_count, TCL_LINK_INT);
Tcl_LinkVar(interp, "sqlite_fullsync_count",
(char*)&sqlite3_fullsync_count, TCL_LINK_INT);
+#if defined(SQLITE_ENABLE_SELECTTRACE)
+ Tcl_LinkVar(interp, "sqlite3SelectTrace",
+ (char*)&sqlite3SelectTrace, TCL_LINK_INT);
+#endif
#if defined(SQLITE_ENABLE_FTS3) && defined(SQLITE_TEST)
Tcl_LinkVar(interp, "sqlite_fts3_enable_parentheses",
(char*)&sqlite3_fts3_enable_parentheses, TCL_LINK_INT);
diff --git a/src/vdbe.c b/src/vdbe.c
index 70537ce11..7ab104455 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -6313,7 +6313,11 @@ case OP_AggFinal: {
assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
pMem = &aMem[pOp->p1];
assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 );
- rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc);
+ if( pOp->p3 ){
+ rc = sqlite3VdbeMemAggValue(pMem, &aMem[pOp->p3], pOp->p4.pFunc);
+ }else{
+ rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc);
+ }
if( rc ){
sqlite3VdbeError(p, "%s", sqlite3_value_text(pMem));
goto abort_due_to_error;
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index 44f901abf..24d6bf91d 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -494,6 +494,7 @@ void sqlite3VdbeMemCast(Mem*,u8,u8);
int sqlite3VdbeMemFromBtree(BtCursor*,u32,u32,Mem*);
void sqlite3VdbeMemRelease(Mem *p);
int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
+int sqlite3VdbeMemAggValue(Mem*, Mem*, FuncDef*);
const char *sqlite3OpcodeName(int);
int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
int sqlite3VdbeMemClearAndResize(Mem *pMem, int n);
diff --git a/src/vdbemem.c b/src/vdbemem.c
index d118d2bb9..a64ed0963 100644
--- a/src/vdbemem.c
+++ b/src/vdbemem.c
@@ -415,6 +415,23 @@ int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
return ctx.isError;
}
+int sqlite3VdbeMemAggValue(Mem *pAccum, Mem *pOut, FuncDef *pFunc){
+ sqlite3_context ctx;
+ Mem t;
+ assert( pFunc!=0 );
+ assert( pFunc->xValue!=0 );
+ assert( (pAccum->flags & MEM_Null)!=0 || pFunc==pAccum->u.pDef );
+ assert( pAccum->db==0 || sqlite3_mutex_held(pAccum->db->mutex) );
+ memset(&ctx, 0, sizeof(ctx));
+ memset(&t, 0, sizeof(t));
+ t.flags = MEM_Null;
+ t.db = pAccum->db;
+ ctx.pOut = pOut;
+ ctx.pMem = pAccum;
+ ctx.pFunc = pFunc;
+ pFunc->xValue(&ctx);
+ return ctx.isError;
+}
/*
** If the memory cell contains a value that must be freed by
** invoking the external callback in Mem.xDel, then this routine
diff --git a/src/window.c b/src/window.c
new file mode 100644
index 000000000..57d467424
--- /dev/null
+++ b/src/window.c
@@ -0,0 +1,53 @@
+/*
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+*/
+#include "sqliteInt.h"
+
+void sqlite3WindowDelete(sqlite3 *db, Window *p){
+ if( p ){
+ sqlite3ExprDelete(db, p->pFilter);
+ sqlite3ExprListDelete(db, p->pPartition);
+ sqlite3ExprListDelete(db, p->pOrderBy);
+ sqlite3ExprDelete(db, p->pEnd);
+ sqlite3ExprDelete(db, p->pStart);
+ sqlite3DbFree(db, p);
+ }
+}
+
+Window *sqlite3WindowAlloc(
+ Parse *pParse,
+ int eType,
+ int eEnd, Expr *pEnd,
+ int eStart, Expr *pStart
+){
+ Window *pWin = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
+
+ if( pWin ){
+ pWin->eType = eType;
+ pWin->eStart = eStart;
+ pWin->eEnd = eEnd;
+ pWin->pEnd = pEnd;
+ pWin->pStart = pStart;
+ }else{
+ sqlite3ExprDelete(pParse->db, pEnd);
+ sqlite3ExprDelete(pParse->db, pStart);
+ }
+
+ return pWin;
+}
+
+void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){
+ if( p ){
+ p->pWin = pWin;
+ }else{
+ sqlite3WindowDelete(pParse->db, pWin);
+ }
+}