Index: src/window.c ================================================================== --- src/window.c +++ src/window.c @@ -409,18 +409,23 @@ pWin->eStart = p->eStart; pWin->eEnd = p->eEnd; } if( pFunc->funcFlags & SQLITE_FUNC_WINDOW ){ sqlite3 *db = pParse->db; + if( pWin->pFilter ){ + sqlite3ErrorMsg(pParse, + "FILTER clause may only be used with aggregate window functions" + ); + }else if( pFunc->xSFunc==row_numberStepFunc || pFunc->xSFunc==ntileStepFunc ){ sqlite3ExprDelete(db, pWin->pStart); sqlite3ExprDelete(db, pWin->pEnd); pWin->pStart = pWin->pEnd = 0; pWin->eType = TK_ROWS; pWin->eStart = TK_UNBOUNDED; pWin->eEnd = TK_CURRENT; - } + }else if( pFunc->xSFunc==dense_rankStepFunc || pFunc->xSFunc==rankStepFunc || pFunc->xSFunc==percent_rankStepFunc || pFunc->xSFunc==cume_distStepFunc ){ sqlite3ExprDelete(db, pWin->pStart); @@ -600,10 +605,14 @@ ** window function - one for the accumulator, another for interim ** results. */ for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ pWin->iArgCol = (pSublist ? pSublist->nExpr : 0); pSublist = exprListAppendList(pParse, pSublist, pWin->pOwner->x.pList); + if( pWin->pFilter ){ + Expr *pFilter = sqlite3ExprDup(db, pWin->pFilter, 0); + pSublist = sqlite3ExprListAppend(pParse, pSublist, pFilter); + } pWin->regAccum = ++pParse->nMem; pWin->regResult = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); } @@ -817,18 +826,33 @@ }else if( pWin->pFunc->xSFunc==leadStepFunc || pWin->pFunc->xSFunc==lagStepFunc ){ /* no-op */ }else{ + int addrIf = 0; + if( pWin->pFilter ){ + int regTmp; + if( csr>0 ){ + regTmp = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+pWin->nArg,regTmp); + }else{ + regTmp = regArg + pWin->nArg; + } + addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1); + if( csr>0 ){ + sqlite3ReleaseTempReg(pParse, regTmp); + } + } if( pWin->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ CollSeq *pColl; - pColl = sqlite3ExprCollSeq(pParse, pWin->pOwner->x.pList->a[0].pExpr); + pColl = sqlite3ExprNNCollSeq(pParse, pWin->pOwner->x.pList->a[0].pExpr); sqlite3VdbeAddOp4(v, OP_CollSeq, 0,0,0, (const char*)pColl, P4_COLLSEQ); } sqlite3VdbeAddOp3(v, OP_AggStep0, bInverse, regArg, pWin->regAccum); sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); sqlite3VdbeChangeP5(v, (u8)nArg); + if( addrIf ) sqlite3VdbeJumpHere(v, addrIf); } } } static void windowAggFinal(Parse *pParse, Window *pMWin, int bFinal){ @@ -853,10 +877,11 @@ } sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, pWin->nArg); sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); if( bFinal ){ sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult); + sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); }else{ sqlite3VdbeChangeP3(v, -1, pWin->regResult); } } } Index: test/window1.test ================================================================== --- test/window1.test +++ test/window1.test @@ -212,7 +212,11 @@ } { b 1 1 b 2 2 b 3 3 b 4 4 b 5 5 b 6 6 b 7 7 a 1 1 a 2 2 a 3 3 a 4 4 a 5 5 a 6 6 a 7 7 } +do_catchsql_test 3.5 { + SELECT x, lag(x) FILTER (WHERE (x%2)=0) OVER w FROM t1 + WINDOW w AS (ORDER BY x) +} {1 {FILTER clause may only be used with aggregate window functions}} finish_test Index: test/window3.tcl ================================================================== --- test/window3.tcl +++ test/window3.tcl @@ -282,11 +282,10 @@ " execsql_test 1.$tn.14.1 " SELECT string_agg(CAST(b AS TEXT), '.') OVER (ORDER BY a $window) FROM t2 " - execsql_test 1.$tn.14.2 " SELECT string_agg(CAST(b AS TEXT), '.') OVER (PARTITION BY b%10 ORDER BY a $window) FROM t2 " execsql_test 1.$tn.14.3 " SELECT string_agg(CAST(b AS TEXT), '.') OVER ( ORDER BY b,a $window ) FROM t2 @@ -298,9 +297,29 @@ SELECT string_agg(CAST(b AS TEXT), '.') OVER ( ORDER BY b%10,a $window ) FROM t2 " execsql_test 1.$tn.14.6 " SELECT string_agg(CAST(b AS TEXT), '.') OVER (PARTITION BY b%2,a ORDER BY b%10 $window) FROM t2 " + + execsql_test 1.$tn.15.1 " + SELECT string_agg(CAST(b AS TEXT), '.') + FILTER (WHERE a%2=0) OVER (ORDER BY a $window) FROM t2 + " + + execsql_test 1.$tn.15.2 " + SELECT string_agg(CAST(b AS TEXT), '.') + FILTER (WHERE 0=1) OVER (ORDER BY a $window) FROM t2 + " + + execsql_test 1.$tn.15.3 " + SELECT string_agg(CAST(b AS TEXT), '.') + FILTER (WHERE 1=0) OVER (PARTITION BY (a%10) ORDER BY a $window) FROM t2 + " + + execsql_test 1.$tn.15.4 " + SELECT string_agg(CAST(b AS TEXT), '.') + FILTER (WHERE a%2=0) OVER (PARTITION BY (a%10) ORDER BY a $window) FROM t2 + " } finish_test Index: test/window3.test ================================================================== --- test/window3.test +++ test/window3.test cannot compute difference between binary files Index: test/window4.tcl ================================================================== --- test/window4.tcl +++ test/window4.tcl @@ -94,8 +94,14 @@ SELECT a, count(*) OVER abc, count(*) OVER def FROM t5 WINDOW abc AS (ORDER BY a), def AS (ORDER BY a DESC) ORDER BY a; } + +execsql_test 3.4 { + SELECT a, max(a) FILTER (WHERE (a%2)=0) OVER w FROM t5 + WINDOW w AS (ORDER BY a) +} + finish_test Index: test/window4.test ================================================================== --- test/window4.test +++ test/window4.test @@ -174,6 +174,16 @@ WINDOW abc AS (ORDER BY a), def AS (ORDER BY a DESC) ORDER BY a; } {1 1 5 2 2 4 3 3 3 4 4 2 5 5 1} +explain_i { + SELECT a, max(a) FILTER (WHERE (a%2)=0) OVER w FROM t5 + WINDOW w AS (ORDER BY a) + } + breakpoint +do_execsql_test 3.4 { + SELECT a, max(a) FILTER (WHERE (a%2)=0) OVER w FROM t5 + WINDOW w AS (ORDER BY a) +} {1 {} 2 2 3 2 4 4 5 4} + finish_test