diff --git a/manifest b/manifest index de95676ec9..b223acdcab 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\slatest\strunk\schanges\sinto\sthis\sbranch. -D 2018-05-25T09:36:27.522 +C Fix\s"RANGE\sBETWEEN\sCURRENT\sROW\sAND\sUNBOUNDED\sFOLLOWING"\swindow\sframe\nprocessing. +D 2018-05-25T20:30:17.495 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in bfc40f350586923e0419d2ea4b559c37ec10ee4b6e210e08c14401f8e340f0da @@ -582,7 +582,7 @@ F src/where.c 60ec752fcbe9f9e0271ac60548d159a540a1ee47a4f9fedc85e88a3d0e392dd1 F src/whereInt.h cbae2bcd37cfebdb7812a8b188cdb19634ced2b9346470d1c270556b0c33ea53 F src/wherecode.c 728c7f70731430ccdac807a79969873e1af6968bf1c4745dff3f9dd35f636cc8 F src/whereexpr.c e90b2e76dcabc81edff56633bf281bc01d93b71e0c81482dc06925ce39f5844a -F src/window.c 4f9f7ceffb08db3a51d3b6450d464206de335122ac9a3e1e70545eda27abd34f +F src/window.c 793ad5ffe29cf09f33db392f2e21f42de76f7292a1819e50b12740bff337b9d7 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d @@ -1615,8 +1615,8 @@ F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972 F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d F test/win32nolock.test ac4f08811a562e45a5755e661f45ca85892bdbbc F test/window1.test 5705337783d220b47f6fb4432264543b7557a05be8013d772f57d71f2fded271 -F test/window2.tcl fd079901ac103a99761803477161834208a243bdd6ec0fbbbd0fc93c7dca1945 -F test/window2.test 4f5efb59714ec741e510f12a24a3196316b90715860d79acb904fb5cc45a3dca +F test/window2.tcl 4d0fd12b30cb1cf3f0e125dc8ff7f88c048fdc6a9301df7542abea26627c1f9a +F test/window2.test 47bb1eb149a605d2082794c7036bdd130a4e6c7ee59ded740748c79b37013aac F test/with1.test 58475190cd8caaeebea8cfeb2a264ec97a0c492b8ffe9ad20cefbb23df462f96 F test/with2.test e0030e2f0267a910d6c0e4f46f2dfe941c1cc0d4f659ba69b3597728e7e8f1ab F test/with3.test 5e8ce2c585170bbbc0544e2a01a4941fa0be173ba5265e5c92eb588cd99a232d @@ -1733,7 +1733,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 5ac44872fd5c4f92851e7bf57d7207bb4d67de88ea2b5c746ff97f20bd6352e1 b816023ce07d01024d5769e16db924374a49bf909edd12dc1344a0a1ef693db5 -R 08a95b4b6cfd3d911449e2174d41c045 +P 6232519899efc568465d8fcc9fcd79d46a2ce4ec05109d26d5eb1ebd239cd596 +R 89fd55d38159d271a1d4c5a8e0028929 U dan -Z 91662d48d0166ff2fae671b2e45e9cba +Z b9c949b19ea308875db3121f11330208 diff --git a/manifest.uuid b/manifest.uuid index 9d48a0420d..d6da466c99 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6232519899efc568465d8fcc9fcd79d46a2ce4ec05109d26d5eb1ebd239cd596 \ No newline at end of file +b4e9c686697a5211a3bfa47e63f0684e3d4241d8c292cffe1a967bc39ad7cd8f \ No newline at end of file diff --git a/src/window.c b/src/window.c index 1c8b759d47..ecbbb9c35f 100644 --- a/src/window.c +++ b/src/window.c @@ -114,47 +114,95 @@ static void windowAggStep( } /* -** ROWS BETWEEN PRECEDING AND FOLLOWING +** ROWS BETWEEN PRECEDING AND FOLLOWING +** ---------------------------------------------------- ** -** ... -** if( new partition ){ -** Gosub flush_partition +** Pseudo-code for the implementation of this window frame type is as +** follows. sqlite3WhereBegin() has already been called to generate the +** top of the main loop when this function is called. +** +** Each time the sub-routine at addrGosub is invoked, a single output +** row is generated based on the current row indicated by Window.iEphCsr. +** +** ... +** if( new partition ){ +** Gosub flush_partition +** } +** Insert (record in eph-table) +** sqlite3WhereEnd() +** Gosub flush_partition +** +** flush_partition: +** Once { +** OpenDup (iEphCsr -> csrStart) +** OpenDup (iEphCsr -> csrEnd) ** } -** Insert (record in eph-table) -** sqlite3WhereEnd() -** Gosub flush_partition +** regStart = // PRECEDING expression +** regEnd = // FOLLOWING expression +** if( regStart<0 || regEnd<0 ){ error! } +** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done +** Next(csrEnd) // if EOF skip Aggstep +** Aggstep (csrEnd) +** if( (regEnd--)<=0 ){ +** AggFinal (xValue) +** Gosub addrGosub +** Next(csr) // if EOF goto flush_partition_done +** if( (regStart--)<=0 ){ +** AggStep (csrStart, xInverse) +** Next(csrStart) +** } +** } +** flush_partition_done: +** ResetSorter (csr) +** Return ** -** flush_partition: -** Once { -** OpenDup (iEphCsr -> csrStart) -** OpenDup (iEphCsr -> csrEnd) -** } -** regStart = // PRECEDING expression -** regEnd = // FOLLOWING expression -** if( regStart<0 || regEnd<0 ) throw exception! -** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done -** Aggstep (csrEnd) -** Next(csrEnd) // if EOF fall-through -** if( (regEnd--)<=0 ){ +** ROWS BETWEEN PRECEDING AND CURRENT ROW +** ROWS BETWEEN CURRENT ROW AND FOLLOWING +** ROWS BETWEEN UNBOUNDED PRECEDING AND FOLLOWING +** +** These are similar to the above. For "CURRENT ROW", intialize the +** register to 0. For "UNBOUNDED PRECEDING" to infinity. +** +** ROWS BETWEEN PRECEDING AND UNBOUNDED FOLLOWING +** ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +** +** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done +** while( 1 ){ +** Next(csrEnd) // Exit while(1) at EOF +** Aggstep (csrEnd) +** } +** while( 1 ){ ** AggFinal (xValue) ** Gosub addrGosub -** Next(csr) // if EOF goto flush_partition_done +** Next(csr) // if EOF goto flush_partition_done ** if( (regStart--)<=0 ){ ** AggStep (csrStart, xInverse) ** Next(csrStart) ** } ** } -** flush_partition_done: -** ResetSorter (csr) -** Return ** -** ROWS BETWEEN PRECEDING AND CURRENT ROW -** ROWS BETWEEN CURRENT ROW AND FOLLOWING -** ROWS BETWEEN PRECEDING AND UNBOUNDED FOLLOWING -** ROWS BETWEEN UNBOUNDED PRECEDING AND FOLLOWING +** For the "CURRENT ROW AND UNBOUNDED FOLLOWING" case, the final if() +** condition is always true (as if regStart were initialized to 0). ** -** These are similar to the above. For "CURRENT ROW", intialize the -** register to 0. For "UNBOUNDED ..." to infinity. +** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +** +** This is the only RANGE case handled by this routine. It modifies the +** second while( 1 ) loop in "ROWS BETWEEN CURRENT ... UNBOUNDED..." to +** be: +** +** while( 1 ){ +** AggFinal (xValue) +** while( 1 ){ +** regPeer++ +** Gosub addrGosub +** Next(csr) // if EOF goto flush_partition_done +** if( new peer ) break; +** } +** while( (regPeer--)>0 ){ +** AggStep (csrStart, xInverse) +** Next(csrStart) +** } +** } ** ** ROWS BETWEEN FOLLOWING AND FOLLOWING ** @@ -220,6 +268,11 @@ static void windowCodeRowExprStep( int addrIfPos1; int addrIfPos2; + int regPeer = 0; /* Number of peers in current group */ + 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 */ + assert( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_CURRENT || pMWin->eStart==TK_FOLLOWING @@ -334,6 +387,18 @@ static void windowCodeRowExprStep( if( pMWin->eStart==TK_FOLLOWING ){ addrIfPos2 = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0 , 1); } + if( pMWin->eType==TK_RANGE ){ + 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); + } for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult); sqlite3VdbeAddOp3(v, @@ -341,9 +406,24 @@ static void windowCodeRowExprStep( ); sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); } + if( pMWin->eType==TK_RANGE ){ + sqlite3VdbeAddOp2(v, OP_AddImm, regPeer, 1); + } sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub); sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)+2); sqlite3VdbeAddOp2(v, OP_Goto, 0, lblFlushDone); + if( pMWin->eType==TK_RANGE ){ + 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); } @@ -352,13 +432,21 @@ static void windowCodeRowExprStep( || pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_FOLLOWING ){ + int addrJumpHere = 0; if( pMWin->eStart==TK_PRECEDING ){ - addrIfPos2 = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0 , 1); + addrJumpHere = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0 , 1); + } + if( pMWin->eType==TK_RANGE ){ + 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, reg); - if( pMWin->eStart==TK_PRECEDING ){ - sqlite3VdbeJumpHere(v, addrIfPos2); + if( pMWin->eType==TK_RANGE ){ + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrJumpHere-1); + } + if( addrJumpHere ){ + sqlite3VdbeJumpHere(v, addrJumpHere); } } if( pMWin->eEnd==TK_FOLLOWING ){ @@ -554,52 +642,6 @@ static void windowCodeDefaultStep( ** Gosub addrGosub ** sqlite3WhereEnd() ** -** ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING -** ROWS BETWEEN CURRENT ROW AND CURRENT ROW -** ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING -** -**======================================================================== -** -** ROWS BETWEEN PRECEDING AND PRECEDING -** -** Replace the bit after "Rewind" in the above with: -** -** if( (regEnd--)<=0 ){ -** AggStep (csr3) -** Next (csr3) -** } -** AggFinal (xValue) -** Gosub addrGosub -** Next(csr) // if EOF goto flush_partition_done -** if( (regStart--)<=0 ){ -** AggStep (csr2, xInverse) -** Next (csr2) -** } -** -** ROWS BETWEEN FOLLOWING AND FOLLOWING -** -** regEnd = regEnd - regStart -** Rewind (csr,csr2,csr3) // if EOF goto flush_partition_done -** Aggstep (csr3) -** Next(csr3) // if EOF fall-through -** if( (regEnd--)<=0 ){ -** AggStep (csr2, xInverse) -** Next (csr2) -** if( (regStart--)<=0 ){ -** AggFinal (xValue) -** Gosub addrGosub -** Next(csr) // if EOF goto flush_partition_done -** } -** } -** -** ROWS BETWEEN UNBOUNDED PRECEDING AND PRECEDING -** ROWS BETWEEN FOLLOWING AND UNBOUNDED FOLLOWING -** -** Similar to the above, except with regStart or regEnd set to infinity, -** as appropriate. -** -** -** */ void sqlite3WindowCodeStep( Parse *pParse, @@ -611,8 +653,10 @@ void sqlite3WindowCodeStep( ){ Window *pMWin = p->pWin; - if( pMWin->pStart || pMWin->pEnd ){ - assert( pMWin->eType==TK_ROWS ); + if( (pMWin->eType==TK_ROWS + && (pMWin->eStart!=TK_UNBOUNDED || pMWin->eEnd!=TK_CURRENT)) + || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED) + ){ *pbLoop = 0; windowCodeRowExprStep(pParse, p, pWInfo, regGosub, addrGosub); return; diff --git a/test/window2.tcl b/test/window2.tcl index f1ab7a7ce9..8873093e02 100644 --- a/test/window2.tcl +++ b/test/window2.tcl @@ -288,6 +288,64 @@ execsql_test 2.22 { ) FROM t1 } +execsql_test 2.23 { + SELECT a, sum(d) OVER ( + ORDER BY d + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + ) FROM t1 +} + +execsql_test 2.24 { + SELECT a, sum(d) OVER ( + PARTITION BY a%2 + ORDER BY d + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + ) FROM t1 +} + +execsql_test 2.25 { + SELECT a, sum(d) OVER ( + ORDER BY d + ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING + ) FROM t1 +} + +execsql_test 2.26 { + SELECT a, sum(d) OVER ( + PARTITION BY b + ORDER BY d + ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING + ) FROM t1 +} + +execsql_test 2.27 { + SELECT a, sum(d) OVER ( + ORDER BY d + ROWS BETWEEN CURRENT ROW AND CURRENT ROW + ) FROM t1 +} + +execsql_test 2.28 { + SELECT a, sum(d) OVER ( + PARTITION BY b + ORDER BY d + ROWS BETWEEN CURRENT ROW AND CURRENT ROW + ) FROM t1 +} + +execsql_test 2.29 { + SELECT a, sum(d) OVER ( + ORDER BY d + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + ) FROM t1 +} +execsql_test 2.30 { + SELECT a, sum(d) OVER ( + ORDER BY b + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + ) FROM t1 +} + ========== puts $::fd finish_test ========== diff --git a/test/window2.test b/test/window2.test index f19b447600..67b6b5df74 100644 --- a/test/window2.test +++ b/test/window2.test @@ -199,6 +199,65 @@ do_execsql_test 2.22 { ) FROM t1 } {2 10 4 6 6 {} 1 8 3 5 5 {}} +do_execsql_test 2.23 { + SELECT a, sum(d) OVER ( + ORDER BY d + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + ) FROM t1 +} {1 21 2 20 3 18 4 15 5 11 6 6} + +do_execsql_test 2.24 { + SELECT a, sum(d) OVER ( + PARTITION BY a%2 + ORDER BY d + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + ) FROM t1 +} {2 12 4 10 6 6 1 9 3 8 5 5} + +do_execsql_test 2.25 { + SELECT a, sum(d) OVER ( + ORDER BY d + ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING + ) FROM t1 +} {1 21 2 21 3 21 4 21 5 21 6 21} + +do_execsql_test 2.26 { + SELECT a, sum(d) OVER ( + PARTITION BY b + ORDER BY d + ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING + ) FROM t1 +} {2 12 4 12 6 12 1 9 3 9 5 9} + +do_execsql_test 2.27 { + SELECT a, sum(d) OVER ( + ORDER BY d + ROWS BETWEEN CURRENT ROW AND CURRENT ROW + ) FROM t1 +} {1 1 2 2 3 3 4 4 5 5 6 6} + +do_execsql_test 2.28 { + SELECT a, sum(d) OVER ( + PARTITION BY b + ORDER BY d + ROWS BETWEEN CURRENT ROW AND CURRENT ROW + ) FROM t1 +} {2 2 4 4 6 6 1 1 3 3 5 5} + +do_execsql_test 2.29 { + SELECT a, sum(d) OVER ( + ORDER BY d + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + ) FROM t1 +} {1 21 2 20 3 18 4 15 5 11 6 6} + +do_execsql_test 2.30 { + SELECT a, sum(d) OVER ( + ORDER BY b + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + ) FROM t1 +} {2 21 4 21 6 21 1 9 3 9 5 9} + #========================================================================== finish_test