From: dan Date: Wed, 13 Jun 2018 20:29:38 +0000 (+0000) Subject: Fix problems with "RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING" window X-Git-Tag: version-3.25.0~178^2~27 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=13078caac4b3f656a839b35512114c4844d2f58f;p=thirdparty%2Fsqlite.git Fix problems with "RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING" window frames. FossilOrigin-Name: c34f31dbd79891249ee9485e91f6ea558ee1db62e04fb0fff2c051612b8fa5e7 --- diff --git a/manifest b/manifest index 27851dacfd..dadf035fa0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sanother\sissue\sto\sdo\swith\swindow-functions\sin\saggregate\squeries. -D 2018-06-12T20:53:38.832 +C Fix\sproblems\swith\s"RANGE\sBETWEEN\sCURRENT\sROW\sAND\sUNBOUNDED\sFOLLOWING"\swindow\nframes. +D 2018-06-13T20:29:38.362 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 498b77b89a8cb42f2ee20fcd6317f279a45c0d6ff40d27825f94b69884c09bbe @@ -583,7 +583,7 @@ F src/where.c fe1a6f97c12cc9472ccce86166ba3f827cf61d6ae770c036a6396b63863baac4 F src/whereInt.h b90ef9b9707ef750eab2a7a080c48fb4900315033274689def32d0cf5a81ebe4 F src/wherecode.c 3317f2b083a66d3e65a03edf316ade4ccb0a99c9956273282ebb579b95d4ba96 F src/whereexpr.c 6f022d6cc9daf56495f191b199352f783aff5cf268ba136b4d8cea3fb62d8c7d -F src/window.c 45d149fe9926b7e9c610ef5234b6eef08f22cbdff855aa3f367b6af17499e90b +F src/window.c 4a26ff629a2207fbb766b64eec5de56a642db2ee1a58ca7f3d9bf7241ca2265d F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d @@ -1622,8 +1622,8 @@ F test/window2.tcl 0983de5eade5eeda49469244799d5331bfe3199fca3f6c6d2a836aa08f4fb F test/window2.test 79747b2edde4ad424e0752b27529aedc86e91f3d8d88846fa17ff0cb67f65086 F test/window3.tcl 654d61d73e10db089b22514d498bb23ec310f720c0f4b5f69f67fda83d672048 F test/window3.test 41727668ee31d2ba50f78efcb5bf1bda2c5cffd889aa65243511004669d1ac25 -F test/window4.tcl a77a7ab3c60517abe06307e4204d65d11f5474c8062f30f536755dd083bf8224 -F test/window4.test 0fb98450ff5478f91b4f8c9440d4463ded30a7337029da4272894ccff0f227e2 +F test/window4.tcl 09167855f695ef94312da965532bc13f8027411de8ce442664fa74949f9df011 +F test/window4.test eb0cf5740de803a4a9373b2c30b73986a4fb1662149260ccf05458abba312ba5 F test/with1.test 58475190cd8caaeebea8cfeb2a264ec97a0c492b8ffe9ad20cefbb23df462f96 F test/with2.test e0030e2f0267a910d6c0e4f46f2dfe941c1cc0d4f659ba69b3597728e7e8f1ab F test/with3.test 5e8ce2c585170bbbc0544e2a01a4941fa0be173ba5265e5c92eb588cd99a232d @@ -1740,7 +1740,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P fe7081e0952950f577234fcbb58f3c1efa4579267654fd2f713dc4804e470e7e -R 7cbc79b684a204310b1cd3eb56d614ea +P 6413e38a174044c28fa9b8b937e6c972d144547a246e6f2882e782538300d042 +R 30609a9614da284dabc36852804237fe U dan -Z 4b5b4b6d9afcbd2c7bfd885559718021 +Z 71b05cd13cd6b1243eec19b7dd0b41c2 diff --git a/manifest.uuid b/manifest.uuid index 5adea5b5a2..8d9afc357c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6413e38a174044c28fa9b8b937e6c972d144547a246e6f2882e782538300d042 \ No newline at end of file +c34f31dbd79891249ee9485e91f6ea558ee1db62e04fb0fff2c051612b8fa5e7 \ No newline at end of file diff --git a/src/window.c b/src/window.c index de3b778226..395d6733be 100644 --- a/src/window.c +++ b/src/window.c @@ -826,6 +826,7 @@ void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){ /* ** Return 0 if the two window objects are identical, or non-zero otherwise. +** Identical window objects can be processed in a single scan. */ int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2){ if( p1->eType!=p2->eType ) return 1; @@ -838,10 +839,24 @@ int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2){ return 0; } -static void windowAggInit(Parse *pParse, Window *pMWin){ + +/* +** This is called by code in select.c before it calls sqlite3WhereBegin() +** to begin iterating through the sub-query results. It is used to allocate +** and initialize registers and cursors used by sqlite3WindowCodeStep(). +*/ +void sqlite3WindowCodeInit(Parse *pParse, Window *pMWin){ Window *pWin; + Vdbe *v = sqlite3GetVdbe(pParse); + int nPart = (pMWin->pPartition ? pMWin->pPartition->nExpr : 0); + nPart += (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0); + if( nPart ){ + pMWin->regPart = pParse->nMem+1; + pParse->nMem += nPart; + sqlite3VdbeAddOp3(v, OP_Null, 0, pMWin->regPart, pMWin->regPart+nPart-1); + } + for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - Vdbe *v = sqlite3GetVdbe(pParse); FuncDef *p = pWin->pFunc; if( (p->funcFlags & SQLITE_FUNC_MINMAX) && pWin->eStart!=TK_UNBOUNDED ){ ExprList *pList = pWin->pOwner->x.pList; @@ -874,33 +889,31 @@ static void windowAggInit(Parse *pParse, Window *pMWin){ } } -void sqlite3WindowCodeInit(Parse *pParse, Window *pWin){ - Vdbe *v = sqlite3GetVdbe(pParse); - int nPart = (pWin->pPartition ? pWin->pPartition->nExpr : 0); - nPart += (pWin->pOrderBy ? pWin->pOrderBy->nExpr : 0); - if( nPart ){ - pWin->regPart = pParse->nMem+1; - pParse->nMem += nPart; - sqlite3VdbeAddOp3(v, OP_Null, 0, pWin->regPart, pWin->regPart+nPart-1); - } - windowAggInit(pParse, pWin); -} - +/* +** A "PRECEDING " (bEnd==0) or "FOLLOWING " (bEnd==1) has just +** been evaluated and the result left in register reg. This function generates +** VM code to check that the value is a non-negative integer and throws +** an exception if it is not. +*/ static void windowCheckFrameValue(Parse *pParse, int reg, int bEnd){ static const char *azErr[] = { "frame starting offset must be a non-negative integer", "frame ending offset must be a non-negative integer" }; Vdbe *v = sqlite3GetVdbe(pParse); - int regZero = ++pParse->nMem; - + int regZero = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp2(v, OP_Integer, 0, regZero); sqlite3VdbeAddOp2(v, OP_MustBeInt, reg, sqlite3VdbeCurrentAddr(v)+2); sqlite3VdbeAddOp3(v, OP_Ge, regZero, sqlite3VdbeCurrentAddr(v)+2, reg); sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_ERROR, OE_Abort); sqlite3VdbeAppendP4(v, (void*)azErr[bEnd], P4_STATIC); + sqlite3ReleaseTempReg(pParse, regZero); } +/* +** Return the number of arguments passed to the window-function associated +** with the object passed as the only argument to this function. +*/ static int windowArgCount(Window *pWin){ ExprList *pList = pWin->pOwner->x.pList; return (pList ? pList->nExpr : 0); @@ -909,14 +922,28 @@ static int windowArgCount(Window *pWin){ /* ** Generate VM code to invoke either xStep() (if bInverse is 0) or ** xInverse (if bInverse is non-zero) for each window function in the -** linked list starting at pMWin. +** linked list starting at pMWin. Or, for built-in window functions +** that do not use the standard function API, generate the required +** inline VM code. +** +** If argument csr is greater than or equal to 0, then argument reg is +** the first register in an array of registers guaranteed to be large +** enough to hold the array of arguments for each function. In this case +** the arguments are extracted from the current row of csr into the +** array of registers before invoking OP_AggStep. +** +** Or, if csr is less than zero, then the array of registers at reg is +** already populated with all columns from the current row of the sub-query. +** +** If argument regPartSize is non-zero, then it is a register containing the +** number of rows in the current partition. */ static void windowAggStep( Parse *pParse, - Window *pMWin, - int csr, - int bInverse, - int reg, + Window *pMWin, /* Linked list of window functions */ + int csr, /* Read arguments from this cursor */ + int bInverse, /* True to invoke xInverse instead of xStep */ + int reg, /* Array of registers */ int regPartSize /* Register containing size of partition */ ){ Vdbe *v = sqlite3GetVdbe(pParse); @@ -997,6 +1024,12 @@ static void windowAggStep( } } +/* +** Generate VM code to invoke either xValue() (bFinal==0) or xFinalize() +** (bFinal==1) for each window function in the linked list starting at +** pMWin. Or, for built-in window-functions that do not use the standard +** API, generate the equivalent VM code. +*/ static void windowAggFinal(Parse *pParse, Window *pMWin, int bFinal){ Vdbe *v = sqlite3GetVdbe(pParse); Window *pWin; @@ -1029,13 +1062,18 @@ static void windowAggFinal(Parse *pParse, Window *pMWin, int bFinal){ } } +/* +** This function generates VM code to invoke the sub-routine at address +** lblFlushPart once for each partition with the entire partition cached in +** the Window.iEphCsr temp table. +*/ static void windowPartitionCache( Parse *pParse, - Select *p, - WhereInfo *pWInfo, - int regFlushPart, - int lblFlushPart, - int *pRegSize + Select *p, /* The rewritten SELECT statement */ + WhereInfo *pWInfo, /* WhereInfo to call WhereEnd() on */ + int regFlushPart, /* Register to use with Gosub lblFlushPart */ + int lblFlushPart, /* Subroutine to Gosub to */ + int *pRegSize /* OUT: Register containing partition size */ ){ Window *pMWin = p->pWin; Vdbe *v = sqlite3GetVdbe(pParse); @@ -1085,6 +1123,19 @@ static void windowPartitionCache( sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart); } +/* +** Invoke the sub-routine at regGosub (generated by code in select.c) to +** return the current row of Window.iEphCsr. If all window functions are +** aggregate window functions that use the standard API, a single +** OP_Gosub instruction is all that this routine generates. Extra VM code +** for per-row processing is only generated for the following built-in window +** functions: +** +** nth_value() +** first_value() +** lag() +** lead() +*/ static void windowReturnOneRow( Parse *pParse, Window *pMWin, @@ -1148,15 +1199,29 @@ static void windowReturnOneRow( sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub); } +/* +** Invoke the code generated by windowReturnOneRow() and, optionally, the +** xInverse() function for each window function, for one or more rows +** from the Window.iEphCsr temp table. This routine generates VM code +** similar to: +** +** while( regCtr>0 ){ +** regCtr--; +** windowReturnOneRow() +** if( bInverse ){ +** AggStep (xInverse) +** } +** Next (Window.iEphCsr) +** } +*/ static void windowReturnRows( Parse *pParse, - Window *pMWin, - int regCtr, - int bFinal, - int regGosub, - int addrGosub, - int regInvArg, - int regInvSize + Window *pMWin, /* List of window functions */ + int regCtr, /* Register containing number of rows */ + int regGosub, /* Register for Gosub addrGosub */ + int addrGosub, /* Address of sub-routine for ReturnOneRow */ + int regInvArg, /* Array of registers for xInverse args */ + int regInvSize /* Register containing size of partition */ ){ int addr; Vdbe *v = sqlite3GetVdbe(pParse); @@ -1349,7 +1414,6 @@ static void windowCodeRowExprStep( int regPeerVal = 0; /* Array of values identifying peer group */ int iPeer = 0; /* Column offset in eph-table of peer vals */ int nPeerVal; /* Number of peer values */ - int bRange = 0; int regSize = 0; assert( pMWin->eStart==TK_PRECEDING @@ -1363,13 +1427,6 @@ static void windowCodeRowExprStep( || pMWin->eEnd==TK_PRECEDING ); - if( pMWin->eType==TK_RANGE - && pMWin->eStart==TK_CURRENT - && pMWin->eEnd==TK_UNBOUNDED - ){ - bRange = 1; - } - /* Allocate register and label for the "flush_partition" sub-routine. */ regFlushPart = ++pParse->nMem; lblFlushPart = sqlite3VdbeMakeLabel(v); @@ -1457,38 +1514,10 @@ static void windowCodeRowExprStep( if( pMWin->eStart==TK_FOLLOWING ){ addrIfPos2 = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0 , 1); } - if( bRange ){ - assert( pMWin->eStart==TK_CURRENT && pMWin->pOrderBy ); - regPeer = ++pParse->nMem; - regPeerVal = pParse->nMem+1; - iPeer = pMWin->nBufferCol + (pMWin->pPartition?pMWin->pPartition->nExpr:0); - nPeerVal = pMWin->pOrderBy->nExpr; - pParse->nMem += (2 * nPeerVal); - for(k=0; kiEphCsr, iPeer+k, regPeerVal+k); - } - sqlite3VdbeAddOp2(v, OP_Integer, 0, regPeer); - } - windowAggFinal(pParse, pMWin, 0); - if( bRange ){ - sqlite3VdbeAddOp2(v, OP_AddImm, regPeer, 1); - } windowReturnOneRow(pParse, pMWin, regGosub, addrGosub); sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)+2); sqlite3VdbeAddOp2(v, OP_Goto, 0, lblFlushDone); - if( bRange ){ - KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pMWin->pOrderBy,0,0); - int addrJump = sqlite3VdbeCurrentAddr(v)-4; - for(k=0; kiEphCsr, iPeer+k, iOut); - } - sqlite3VdbeAddOp3(v, OP_Compare, regPeerVal, regPeerVal+nPeerVal, nPeerVal); - sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO); - addr = sqlite3VdbeCurrentAddr(v)+1; - sqlite3VdbeAddOp3(v, OP_Jump, addr, addrJump, addr); - } if( pMWin->eStart==TK_FOLLOWING ){ sqlite3VdbeJumpHere(v, addrIfPos2); } @@ -1501,15 +1530,8 @@ static void windowCodeRowExprStep( if( pMWin->eStart==TK_PRECEDING ){ addrJumpHere = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0 , 1); } - if( bRange ){ - sqlite3VdbeAddOp3(v, OP_IfPos, regPeer, sqlite3VdbeCurrentAddr(v)+2, 1); - addrJumpHere = sqlite3VdbeAddOp0(v, OP_Goto); - } sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+1); windowAggStep(pParse, pMWin, csrStart, 1, regArg, regSize); - if( bRange ){ - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrJumpHere-1); - } if( addrJumpHere ){ sqlite3VdbeJumpHere(v, addrJumpHere); } @@ -1561,8 +1583,6 @@ static void windowCodeRowExprStep( ** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ** RANGE BETWEEN CURRENT ROW AND CURRENT ROW -** -** TODO. */ static void windowCodeCacheStep( Parse *pParse, @@ -1582,7 +1602,7 @@ static void windowCodeCacheStep( int regNewPeer; int addrGoto; /* Address of Goto used to jump flush_par.. */ - int addrRewind; /* Address of Rewind that starts loop */ + int addrNext; /* Jump here for next iteration of loop */ int regFlushPart; int lblFlushPart; int csrLead; @@ -1590,12 +1610,17 @@ static void windowCodeCacheStep( int regArg; /* Register array to martial function args */ int regSize; int nArg; + int bReverse; + int lblEmpty; assert( (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT) || (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_UNBOUNDED) || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_CURRENT) + || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED) ); + lblEmpty = sqlite3VdbeMakeLabel(v); + bReverse = (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED); regNewPeer = pParse->nMem+1; pParse->nMem += nPeer; @@ -1618,11 +1643,19 @@ static void windowCodeCacheStep( regArg = windowInitAccum(pParse, pMWin); sqlite3VdbeAddOp2(v, OP_Integer, 0, regCtr); - addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, csrLead); - sqlite3VdbeAddOp1(v, OP_Rewind, pMWin->iEphCsr); + sqlite3VdbeAddOp2(v, OP_Rewind, csrLead, lblEmpty); + sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, lblEmpty); + + if( bReverse ){ + int addr = sqlite3VdbeCurrentAddr(v); + windowAggStep(pParse, pMWin, csrLead, 0, regArg, regSize); + sqlite3VdbeAddOp2(v, OP_Next, csrLead, addr); + sqlite3VdbeAddOp2(v, OP_Rewind, csrLead, lblEmpty); + } + addrNext = sqlite3VdbeCurrentAddr(v); - if( pOrderBy && pMWin->eEnd==TK_CURRENT ){ - int bCurrent = (pMWin->eEnd==TK_CURRENT && pMWin->eStart==TK_CURRENT); + if( pOrderBy && (pMWin->eEnd==TK_CURRENT || pMWin->eStart==TK_CURRENT) ){ + int bCurrent = (pMWin->eStart==TK_CURRENT); int addrJump = 0; /* Address of OP_Jump below */ if( pMWin->eType==TK_RANGE ){ int iOff = pMWin->nBufferCol + (pPart ? pPart->nExpr : 0); @@ -1637,20 +1670,21 @@ static void windowCodeCacheStep( sqlite3VdbeAddOp3(v, OP_Copy, regNewPeer, regPeer, nPeer-1); } - windowReturnRows(pParse, pMWin, regCtr, 0, regGosub, addrGosub, + windowReturnRows(pParse, pMWin, regCtr, regGosub, addrGosub, (bCurrent ? regArg : 0), (bCurrent ? regSize : 0) ); if( addrJump ) sqlite3VdbeJumpHere(v, addrJump); } - windowAggStep(pParse, pMWin, csrLead, 0, regArg, regSize); + if( bReverse==0 ){ + windowAggStep(pParse, pMWin, csrLead, 0, regArg, regSize); + } sqlite3VdbeAddOp2(v, OP_AddImm, regCtr, 1); - sqlite3VdbeAddOp2(v, OP_Next, csrLead, addrRewind+2); + sqlite3VdbeAddOp2(v, OP_Next, csrLead, addrNext); - windowReturnRows(pParse, pMWin, regCtr, 1, regGosub, addrGosub, 0, 0); + windowReturnRows(pParse, pMWin, regCtr, regGosub, addrGosub, 0, 0); - sqlite3VdbeJumpHere(v, addrRewind); - sqlite3VdbeJumpHere(v, addrRewind+1); + sqlite3VdbeResolveLabel(v, lblEmpty); sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr); sqlite3VdbeAddOp1(v, OP_Return, regFlushPart); @@ -1805,6 +1839,11 @@ static void windowCodeDefaultStep( sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)-1); } +/* +** Allocate and return a duplicate of the Window object indicated by the +** third argument. Set the Window.pOwner field of the new object to +** pOwner. +*/ Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p){ Window *pNew = 0; if( p ){ @@ -1839,40 +1878,46 @@ void sqlite3WindowCodeStep( int addrGosub /* OP_Gosub here to return each row */ ){ Window *pMWin = p->pWin; - Window *pWin; + ExprList *pOrderBy = pMWin->pOrderBy; - /* Call windowCodeRowExprStep() for all window modes *except*: + /* Call windowCodeRowExprStep() for all "ROWS" window modes except: ** - ** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW - ** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING - ** RANGE BETWEEN CURRENT ROW AND CURRENT ROW - ** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW + ** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW */ if( (pMWin->eType==TK_ROWS - && (pMWin->eStart!=TK_UNBOUNDED||pMWin->eEnd!=TK_CURRENT||!pMWin->pOrderBy)) - || (pMWin->eStart==TK_CURRENT&&pMWin->eEnd==TK_UNBOUNDED&&pMWin->pOrderBy) + && (pMWin->eStart!=TK_UNBOUNDED || pMWin->eEnd!=TK_CURRENT || !pOrderBy)) ){ windowCodeRowExprStep(pParse, p, pWInfo, regGosub, addrGosub); - return; - } + }else{ + Window *pWin; + int bCache = 0; - /* Call windowCodeCacheStep() if there is a window function that requires - ** that the entire partition be cached in a temp table before any rows - ** are returned. */ - for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - FuncDef *pFunc = pWin->pFunc; - if( (pFunc->funcFlags & SQLITE_FUNC_WINDOW_SIZE) - || (pFunc->xSFunc==nth_valueStepFunc) - || (pFunc->xSFunc==first_valueStepFunc) - || (pFunc->xSFunc==leadStepFunc) - || (pFunc->xSFunc==lagStepFunc) - ){ + if( pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED && pOrderBy ){ + bCache = 1; + }else{ + /* Call windowCodeCacheStep() if there is a window function that requires + ** that the entire partition be cached in a temp table before any rows + ** are returned. */ + for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ + FuncDef *pFunc = pWin->pFunc; + if( (pFunc->funcFlags & SQLITE_FUNC_WINDOW_SIZE) + || (pFunc->xSFunc==nth_valueStepFunc) + || (pFunc->xSFunc==first_valueStepFunc) + || (pFunc->xSFunc==leadStepFunc) + || (pFunc->xSFunc==lagStepFunc) + ){ + bCache = 1; + break; + } + } + } + + /* Otherwise, call windowCodeDefaultStep(). */ + if( bCache ){ windowCodeCacheStep(pParse, p, pWInfo, regGosub, addrGosub); - return; + }else{ + windowCodeDefaultStep(pParse, p, pWInfo, regGosub, addrGosub); } } - - /* Otherwise, call windowCodeDefaultStep(). */ - windowCodeDefaultStep(pParse, p, pWInfo, regGosub, addrGosub); } diff --git a/test/window4.tcl b/test/window4.tcl index fcb6e61212..5c7466e75d 100644 --- a/test/window4.tcl +++ b/test/window4.tcl @@ -157,6 +157,12 @@ execsql_test 4.3 { SELECT abs(max(b) OVER (ORDER BY b)) FROM ttt GROUP BY b; } +execsql_test 4.4 { + SELECT sum(b) OVER ( + ORDER BY a RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + ) FROM ttt; +} + finish_test diff --git a/test/window4.test b/test/window4.test index f29bce7525..20b8a114b0 100644 --- a/test/window4.test +++ b/test/window4.test @@ -239,4 +239,10 @@ do_execsql_test 4.3 { SELECT abs(max(b) OVER (ORDER BY b)) FROM ttt GROUP BY b; } {1 2 3} +do_execsql_test 4.4 { + SELECT sum(b) OVER ( + ORDER BY a RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + ) FROM ttt; +} {18 17 15 12 11 9 6 5 3} + finish_test