aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/btree.c8
-rw-r--r--src/expr.c37
-rw-r--r--src/parse.y51
-rw-r--r--src/prepare.c6
-rw-r--r--src/resolve.c22
-rw-r--r--src/select.c26
-rw-r--r--src/sqliteInt.h8
-rw-r--r--src/vdbeaux.c10
-rw-r--r--src/walker.c20
-rw-r--r--src/window.c12
10 files changed, 139 insertions, 61 deletions
diff --git a/src/btree.c b/src/btree.c
index 6c73a9422..5f82f1cc0 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -8307,11 +8307,13 @@ static int balance(BtCursor *pCur){
VVA_ONLY( int balance_deeper_called = 0 );
do {
- int iPage = pCur->iPage;
+ int iPage;
MemPage *pPage = pCur->pPage;
if( NEVER(pPage->nFree<0) && btreeComputeFreeSpace(pPage) ) break;
- if( iPage==0 ){
+ if( pPage->nOverflow==0 && pPage->nFree<=nMin ){
+ break;
+ }else if( (iPage = pCur->iPage)==0 ){
if( pPage->nOverflow ){
/* The root page of the b-tree is overfull. In this case call the
** balance_deeper() function to create a new child for the root-page
@@ -8332,8 +8334,6 @@ static int balance(BtCursor *pCur){
}else{
break;
}
- }else if( pPage->nOverflow==0 && pPage->nFree<=nMin ){
- break;
}else{
MemPage * const pParent = pCur->apPage[iPage-1];
int const iIdx = pCur->aiIdx[iPage-1];
diff --git a/src/expr.c b/src/expr.c
index c4f201a13..6fbeb827f 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -1027,7 +1027,7 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
assert( !ExprHasProperty(p, EP_WinFunc) || p->y.pWin!=0 || db->mallocFailed );
assert( p->op!=TK_FUNCTION || ExprHasProperty(p, EP_TokenOnly|EP_Reduced)
- || p->y.pWin==0 || ExprHasProperty(p, EP_WinFunc) );
+ || p->y.pWin==0 || ExprHasProperty(p, EP_WinFunc|EP_Filter) );
#ifdef SQLITE_DEBUG
if( ExprHasProperty(p, EP_Leaf) && !ExprHasProperty(p, EP_TokenOnly) ){
assert( p->pLeft==0 );
@@ -1040,15 +1040,24 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
assert( p->x.pList==0 || p->pRight==0 );
if( p->pLeft && p->op!=TK_SELECT_COLUMN ) sqlite3ExprDeleteNN(db, p->pLeft);
if( p->pRight ){
+ assert( !ExprHasProperty(p, (EP_WinFunc|EP_Filter)) );
sqlite3ExprDeleteNN(db, p->pRight);
}else if( ExprHasProperty(p, EP_xIsSelect) ){
+ assert( !ExprHasProperty(p, (EP_WinFunc|EP_Filter)) );
sqlite3SelectDelete(db, p->x.pSelect);
}else{
sqlite3ExprListDelete(db, p->x.pList);
- }
- if( ExprHasProperty(p, EP_WinFunc) ){
- assert( p->op==TK_FUNCTION );
- sqlite3WindowDelete(db, p->y.pWin);
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( ExprHasProperty(p, (EP_WinFunc|EP_Filter)) ){
+ if( ExprHasProperty(p, EP_WinFunc) ){
+ assert( p->op==TK_FUNCTION && !ExprHasProperty(p, EP_Filter) );
+ sqlite3WindowDelete(db, p->y.pWin);
+ }else{
+ assert( p->op==TK_FUNCTION || p->op==TK_AGG_FUNCTION );
+ sqlite3ExprDeleteNN(db, p->y.pFilter);
+ }
+ }
+#endif
}
}
if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken);
@@ -1264,7 +1273,7 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){
}
/* Fill in pNew->pLeft and pNew->pRight. */
- if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly|EP_WinFunc) ){
+ if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly|EP_WinFunc|EP_Filter) ){
zAlloc += dupedExprNodeSize(p, dupFlags);
if( !ExprHasProperty(pNew, EP_TokenOnly|EP_Leaf) ){
pNew->pLeft = p->pLeft ?
@@ -1277,6 +1286,10 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){
pNew->y.pWin = sqlite3WindowDup(db, pNew, p->y.pWin);
assert( ExprHasProperty(pNew, EP_WinFunc) );
}
+ if( ExprHasProperty(p, EP_Filter) ){
+ pNew->y.pFilter = sqlite3ExprDup(db, p->y.pFilter, 0);
+ assert( ExprHasProperty(pNew, EP_Filter) );
+ }
#endif /* SQLITE_OMIT_WINDOWFUNC */
if( pzBuffer ){
*pzBuffer = zAlloc;
@@ -1332,8 +1345,8 @@ static With *withDup(sqlite3 *db, With *p){
** objects found there, assembling them onto the linked list at Select->pWin.
*/
static int gatherSelectWindowsCallback(Walker *pWalker, Expr *pExpr){
- if( pExpr->op==TK_FUNCTION && pExpr->y.pWin!=0 ){
- assert( ExprHasProperty(pExpr, EP_WinFunc) );
+ if( pExpr->op==TK_FUNCTION && ExprHasProperty(pExpr, EP_WinFunc) ){
+ assert( pExpr->y.pWin );
pExpr->y.pWin->pNextWin = pWalker->u.pSelect->pWin;
pWalker->u.pSelect->pWin = pExpr->y.pWin;
}
@@ -4862,6 +4875,14 @@ int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTab){
}else if( ALWAYS(pB->u.zToken!=0) && strcmp(pA->u.zToken,pB->u.zToken)!=0 ){
return 2;
}
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ else if( pA->op==TK_AGG_FUNCTION ){
+ assert( ExprHasProperty(pA, EP_WinFunc)==0 );
+ if( sqlite3ExprCompare(pParse, pA->y.pFilter, pB->y.pFilter, iTab) ){
+ return 2;
+ }
+ }
+#endif
}
if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 2;
if( (combinedFlags & EP_TokenOnly)==0 ){
diff --git a/src/parse.y b/src/parse.y
index fc5bff16d..3cef3e5e6 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -1044,13 +1044,23 @@ expr(A) ::= id(X) LP STAR RP. {
}
%ifndef SQLITE_OMIT_WINDOWFUNC
-expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP over_clause(Z). {
+%type filter_over {
+ struct FunctionTail {
+ Window *pWin;
+ Expr *pFilter;
+ }
+}
+%destructor filter_over {
+ sqlite3WindowDelete(pParse->db, $$.pWin);
+ sqlite3ExprDelete(pParse->db, $$.pFilter);
+}
+expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP filter_over(F). {
A = sqlite3ExprFunction(pParse, Y, &X, D);
- sqlite3WindowAttach(pParse, A, Z);
+ sqlite3WindowAttach(pParse, A, F.pFilter, F.pWin);
}
-expr(A) ::= id(X) LP STAR RP over_clause(Z). {
+expr(A) ::= id(X) LP STAR RP filter_over(F). {
A = sqlite3ExprFunction(pParse, 0, &X, 0);
- sqlite3WindowAttach(pParse, A, Z);
+ sqlite3WindowAttach(pParse, A, F.pFilter, F.pWin);
}
%endif
@@ -1657,8 +1667,11 @@ windowdefn(A) ::= nm(X) AS LP window(Y) RP. {
%type part_opt {ExprList*}
%destructor part_opt {sqlite3ExprListDelete(pParse->db, $$);}
-%type filter_opt {Expr*}
-%destructor filter_opt {sqlite3ExprDelete(pParse->db, $$);}
+%type filter_clause {Expr*}
+%destructor filter_clause {sqlite3ExprDelete(pParse->db, $$);}
+
+%type over_clause {Window*}
+%destructor over_clause {sqlite3WindowDelete(pParse->db, $$);}
%type range_or_rows {int}
@@ -1724,25 +1737,31 @@ frame_exclude(A) ::= GROUP|TIES(X). {A = @X; /*A-overwrites-X*/}
%destructor window_clause {sqlite3WindowListDelete(pParse->db, $$);}
window_clause(A) ::= WINDOW windowdefn_list(B). { A = B; }
-%type over_clause {Window*}
-%destructor over_clause {sqlite3WindowDelete(pParse->db, $$);}
-over_clause(A) ::= filter_opt(W) OVER LP window(Z) RP. {
+filter_over(F) ::= filter_clause(A) over_clause(B). {
+ F.pFilter = A;
+ F.pWin = B;
+}
+filter_over(F) ::= over_clause(B). {
+ F.pFilter = 0;
+ F.pWin = B;
+}
+filter_over(F) ::= filter_clause(A). {
+ F.pFilter = A;
+ F.pWin = 0;
+}
+
+over_clause(A) ::= OVER LP window(Z) RP. {
A = Z;
assert( A!=0 );
- A->pFilter = W;
}
-over_clause(A) ::= filter_opt(W) OVER nm(Z). {
+over_clause(A) ::= OVER nm(Z). {
A = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
if( A ){
A->zName = sqlite3DbStrNDup(pParse->db, Z.z, Z.n);
- A->pFilter = W;
- }else{
- sqlite3ExprDelete(pParse->db, W);
}
}
-filter_opt(A) ::= . { A = 0; }
-filter_opt(A) ::= FILTER LP WHERE expr(X) RP. { A = X; }
+filter_clause(A) ::= FILTER LP WHERE expr(X) RP. { A = X; }
%endif /* SQLITE_OMIT_WINDOWFUNC */
/*
diff --git a/src/prepare.c b/src/prepare.c
index 3f1a79b14..f385c1fc7 100644
--- a/src/prepare.c
+++ b/src/prepare.c
@@ -635,7 +635,7 @@ static int sqlite3Prepare(
rc = sParse.rc;
#ifndef SQLITE_OMIT_EXPLAIN
- if( rc==SQLITE_OK && sParse.pVdbe && sParse.explain ){
+ if( sParse.explain && rc==SQLITE_OK && sParse.pVdbe ){
static const char * const azColName[] = {
"addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment",
"id", "parent", "notused", "detail"
@@ -660,8 +660,8 @@ static int sqlite3Prepare(
if( db->init.busy==0 ){
sqlite3VdbeSetSql(sParse.pVdbe, zSql, (int)(sParse.zTail-zSql), prepFlags);
}
- if( sParse.pVdbe && (rc!=SQLITE_OK || db->mallocFailed) ){
- sqlite3VdbeFinalize(sParse.pVdbe);
+ if( rc!=SQLITE_OK || db->mallocFailed ){
+ if( sParse.pVdbe ) sqlite3VdbeFinalize(sParse.pVdbe);
assert(!(*ppStmt));
}else{
*ppStmt = (sqlite3_stmt*)sParse.pVdbe;
diff --git a/src/resolve.c b/src/resolve.c
index 8dac077ee..37aaa9260 100644
--- a/src/resolve.c
+++ b/src/resolve.c
@@ -826,22 +826,23 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
if( 0==IN_RENAME_OBJECT ){
#ifndef SQLITE_OMIT_WINDOWFUNC
+ Window *pWin = (ExprHasProperty(pExpr, EP_WinFunc) ? pExpr->y.pWin : 0);
assert( is_agg==0 || (pDef->funcFlags & SQLITE_FUNC_MINMAX)
|| (pDef->xValue==0 && pDef->xInverse==0)
|| (pDef->xValue && pDef->xInverse && pDef->xSFunc && pDef->xFinalize)
);
- if( pDef && pDef->xValue==0 && ExprHasProperty(pExpr, EP_WinFunc) ){
+ if( pDef && pDef->xValue==0 && pWin ){
sqlite3ErrorMsg(pParse,
"%.*s() may not be used as a window function", nId, zId
);
pNC->nErr++;
}else if(
(is_agg && (pNC->ncFlags & NC_AllowAgg)==0)
- || (is_agg && (pDef->funcFlags&SQLITE_FUNC_WINDOW) && !pExpr->y.pWin)
- || (is_agg && pExpr->y.pWin && (pNC->ncFlags & NC_AllowWin)==0)
+ || (is_agg && (pDef->funcFlags&SQLITE_FUNC_WINDOW) && !pWin)
+ || (is_agg && pWin && (pNC->ncFlags & NC_AllowWin)==0)
){
const char *zType;
- if( (pDef->funcFlags & SQLITE_FUNC_WINDOW) || pExpr->y.pWin ){
+ if( (pDef->funcFlags & SQLITE_FUNC_WINDOW) || pWin ){
zType = "window";
}else{
zType = "aggregate";
@@ -849,6 +850,12 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
sqlite3ErrorMsg(pParse, "misuse of %s function %.*s()",zType,nId,zId);
pNC->nErr++;
is_agg = 0;
+ }else if( is_agg==0 && ExprHasProperty(pExpr, EP_Filter) ){
+ sqlite3ErrorMsg(pParse,
+ "filter clause may not be used with non-aggregate %.*s()",
+ nId, zId
+ );
+ pNC->nErr++;
}
#else
if( (is_agg && (pNC->ncFlags & NC_AllowAgg)==0) ){
@@ -874,7 +881,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
** Or arguments of other window functions. But aggregate functions
** may be arguments for window functions. */
#ifndef SQLITE_OMIT_WINDOWFUNC
- pNC->ncFlags &= ~(NC_AllowWin | (!pExpr->y.pWin ? NC_AllowAgg : 0));
+ pNC->ncFlags &= ~(NC_AllowWin | (!pWin ? NC_AllowAgg : 0));
#else
pNC->ncFlags &= ~NC_AllowAgg;
#endif
@@ -883,7 +890,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
sqlite3WalkExprList(pWalker, pList);
if( is_agg ){
#ifndef SQLITE_OMIT_WINDOWFUNC
- if( pExpr->y.pWin ){
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
Select *pSel = pNC->pWinSelect;
if( IN_RENAME_OBJECT==0 ){
sqlite3WindowUpdate(pParse, pSel->pWinDefn, pExpr->y.pWin, pDef);
@@ -904,6 +911,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
NameContext *pNC2 = pNC;
pExpr->op = TK_AGG_FUNCTION;
pExpr->op2 = 0;
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ sqlite3WalkExpr(pWalker, pExpr->y.pFilter);
+#endif
while( pNC2 && !sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){
pExpr->op2++;
pNC2 = pNC2->pNext;
diff --git a/src/select.c b/src/select.c
index 53d33a7ef..8b18e20d0 100644
--- a/src/select.c
+++ b/src/select.c
@@ -4403,7 +4403,9 @@ static u8 minMaxQuery(sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){
assert( *ppMinMax==0 );
assert( pFunc->op==TK_AGG_FUNCTION );
- if( pEList==0 || pEList->nExpr!=1 ) return eRet;
+ if( pEList==0 || pEList->nExpr!=1 || ExprHasProperty(pFunc, EP_Filter) ){
+ return eRet;
+ }
zFunc = pFunc->u.zToken;
if( sqlite3StrICmp(zFunc, "min")==0 ){
eRet = WHERE_ORDERBY_MIN;
@@ -4450,7 +4452,7 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
if( pExpr->op!=TK_AGG_FUNCTION ) return 0;
if( NEVER(pAggInfo->nFunc==0) ) return 0;
if( (pAggInfo->aFunc[0].pFunc->funcFlags&SQLITE_FUNC_COUNT)==0 ) return 0;
- if( pExpr->flags&EP_Distinct ) return 0;
+ if( ExprHasProperty(pExpr, EP_Distinct|EP_Filter) ) return 0;
return pTab;
}
@@ -5330,6 +5332,11 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){
int regAgg;
ExprList *pList = pF->pExpr->x.pList;
assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) );
+ if( ExprHasProperty(pF->pExpr, EP_Filter) ){
+ Expr *pFilter = pF->pExpr->y.pFilter;
+ addrNext = sqlite3VdbeMakeLabel(pParse);
+ sqlite3ExprIfFalse(pParse, pFilter, addrNext, SQLITE_JUMPIFNULL);
+ }
if( pList ){
nArg = pList->nExpr;
regAgg = sqlite3GetTempRange(pParse, nArg);
@@ -5339,7 +5346,9 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){
regAgg = 0;
}
if( pF->iDistinct>=0 ){
- addrNext = sqlite3VdbeMakeLabel(pParse);
+ if( addrNext==0 ){
+ addrNext = sqlite3VdbeMakeLabel(pParse);
+ }
testcase( nArg==0 ); /* Error condition */
testcase( nArg>1 ); /* Also an error */
codeDistinct(pParse, pF->iDistinct, addrNext, 1, regAgg);
@@ -6222,9 +6231,16 @@ int sqlite3Select(
minMaxFlag = WHERE_ORDERBY_NORMAL;
}
for(i=0; i<sAggInfo.nFunc; i++){
- assert( !ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_xIsSelect) );
+ Expr *pExpr = sAggInfo.aFunc[i].pExpr;
+ assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
sNC.ncFlags |= NC_InAggFunc;
- sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->x.pList);
+ sqlite3ExprAnalyzeAggList(&sNC, pExpr->x.pList);
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ assert( !ExprHasProperty(pExpr, EP_WinFunc) );
+ if( pExpr->y.pFilter ){
+ sqlite3ExprAnalyzeAggregates(&sNC, pExpr->y.pFilter);
+ }
+#endif
sNC.ncFlags &= ~NC_InAggFunc;
}
sAggInfo.mxReg = pParse->nMem;
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 1c4618331..c0f2e0a18 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -2491,7 +2491,8 @@ struct Expr {
union {
Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL
** for a column of an index on an expression */
- Window *pWin; /* TK_FUNCTION: Window definition for the func */
+ Window *pWin; /* EP_WinFunc: Window definition for the func */
+ Expr *pFilter; /* EP_Filter: Filter definition for the func */
struct { /* TK_IN, TK_SELECT, and TK_EXISTS */
int iAddr; /* Subroutine entry address */
int regReturn; /* Register used to hold return address */
@@ -2536,6 +2537,7 @@ struct Expr {
#define EP_Static 0x8000000 /* Held in memory not obtained from malloc() */
#define EP_IsTrue 0x10000000 /* Always has boolean value of TRUE */
#define EP_IsFalse 0x20000000 /* Always has boolean value of FALSE */
+#define EP_Filter 0x40000000 /* TK_[AGG_]FUNCTION with Expr.y.pFilter set */
/*
** The EP_Propagate mask is a set of properties that automatically propagate
@@ -3602,7 +3604,7 @@ struct Window {
void sqlite3WindowDelete(sqlite3*, Window*);
void sqlite3WindowListDelete(sqlite3 *db, Window *p);
Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*, u8);
-void sqlite3WindowAttach(Parse*, Expr*, Window*);
+void sqlite3WindowAttach(Parse*, Expr*, Expr*, Window*);
int sqlite3WindowCompare(Parse*, Window*, Window*);
void sqlite3WindowCodeInit(Parse*, Window*);
void sqlite3WindowCodeStep(Parse*, Select*, WhereInfo*, int, int);
@@ -3617,7 +3619,7 @@ Window *sqlite3WindowAssemble(Parse*, Window*, ExprList*, ExprList*, Token*);
#else
# define sqlite3WindowDelete(a,b)
# define sqlite3WindowFunctions()
-# define sqlite3WindowAttach(a,b,c)
+# define sqlite3WindowAttach(a,b,c,d)
#endif
/*
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index c21d7db40..5781d8f21 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -4607,7 +4607,11 @@ static int vdbeRecordCompareString(
nCmp = MIN( pPKey2->aMem[0].n, nStr );
res = memcmp(&aKey1[szHdr], pPKey2->aMem[0].z, nCmp);
- if( res==0 ){
+ if( res>0 ){
+ res = pPKey2->r2;
+ }else if( res<0 ){
+ res = pPKey2->r1;
+ }else{
res = nStr - pPKey2->aMem[0].n;
if( res==0 ){
if( pPKey2->nField>1 ){
@@ -4621,10 +4625,6 @@ static int vdbeRecordCompareString(
}else{
res = pPKey2->r1;
}
- }else if( res>0 ){
- res = pPKey2->r2;
- }else{
- res = pPKey2->r1;
}
}
diff --git a/src/walker.c b/src/walker.c
index eff358525..44e3d1836 100644
--- a/src/walker.c
+++ b/src/walker.c
@@ -63,18 +63,26 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){
if( pExpr->pLeft && walkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort;
assert( pExpr->x.pList==0 || pExpr->pRight==0 );
if( pExpr->pRight ){
+ assert( !ExprHasProperty(pExpr, EP_WinFunc|EP_Filter) );
pExpr = pExpr->pRight;
continue;
}else if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+ assert( !ExprHasProperty(pExpr, EP_WinFunc|EP_Filter) );
if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort;
- }else if( pExpr->x.pList ){
- if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
- }
+ }else{
+ if( pExpr->x.pList ){
+ if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
+ }
#ifndef SQLITE_OMIT_WINDOWFUNC
- if( ExprHasProperty(pExpr, EP_WinFunc) ){
- if( walkWindowList(pWalker, pExpr->y.pWin) ) return WRC_Abort;
- }
+ if( ExprHasProperty(pExpr, EP_WinFunc|EP_Filter) ){
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ if( walkWindowList(pWalker, pExpr->y.pWin) ) return WRC_Abort;
+ }else if( ExprHasProperty(pExpr, EP_Filter) ){
+ if( walkExpr(pWalker, pExpr->y.pFilter) ) return WRC_Abort;
+ }
+ }
#endif
+ }
}
break;
}
diff --git a/src/window.c b/src/window.c
index dcd7107a4..b91b0f050 100644
--- a/src/window.c
+++ b/src/window.c
@@ -1193,13 +1193,10 @@ void sqlite3WindowChain(Parse *pParse, Window *pWin, Window *pList){
/*
** Attach window object pWin to expression p.
*/
-void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){
+void sqlite3WindowAttach(Parse *pParse, Expr *p, Expr *pFilter, Window *pWin){
if( p ){
assert( p->op==TK_FUNCTION );
- /* This routine is only called for the parser. If pWin was not
- ** allocated due to an OOM, then the parser would fail before ever
- ** invoking this routine */
- if( ALWAYS(pWin) ){
+ if( pWin ){
p->y.pWin = pWin;
ExprSetProperty(p, EP_WinFunc);
pWin->pOwner = p;
@@ -1207,9 +1204,14 @@ void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){
sqlite3ErrorMsg(pParse,
"DISTINCT is not supported for window functions");
}
+ pWin->pFilter = pFilter;
+ }else if( pFilter ){
+ p->y.pFilter = pFilter;
+ ExprSetProperty(p, EP_Filter);
}
}else{
sqlite3WindowDelete(pParse->db, pWin);
+ sqlite3ExprDelete(pParse->db, pFilter);
}
}