]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix "RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING" window frame
authordan <dan@noemail.net>
Fri, 25 May 2018 20:30:17 +0000 (20:30 +0000)
committerdan <dan@noemail.net>
Fri, 25 May 2018 20:30:17 +0000 (20:30 +0000)
processing.

FossilOrigin-Name: b4e9c686697a5211a3bfa47e63f0684e3d4241d8c292cffe1a967bc39ad7cd8f

manifest
manifest.uuid
src/window.c
test/window2.tcl
test/window2.test

index de95676ec97957e9445c4235404647e234732d8b..b223acdcab9531d415ae672bdd093acef0c77971 100644 (file)
--- 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
index 9d48a0420da65e882c87db73acc7c6eb7286f1f0..d6da466c997851ea03277409ab715394dd200443 100644 (file)
@@ -1 +1 @@
-6232519899efc568465d8fcc9fcd79d46a2ce4ec05109d26d5eb1ebd239cd596
\ No newline at end of file
+b4e9c686697a5211a3bfa47e63f0684e3d4241d8c292cffe1a967bc39ad7cd8f
\ No newline at end of file
index 1c8b759d47711c3bc1b0683ea40838d9c19a3cc6..ecbbb9c35ffe59159858982fccc63afc80e22900 100644 (file)
@@ -114,47 +114,95 @@ static void windowAggStep(
 }
 
 /*
-** ROWS BETWEEN <expr> PRECEDING    AND <expr> FOLLOWING
+** ROWS BETWEEN <expr1> PRECEDING AND <expr2> 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 = <expr1>                // PRECEDING expression
+**     regEnd = <expr2>                  // 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 = <expr1>            // PRECEDING expression
-**   regEnd = <expr2>          // 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 <expr> PRECEDING    AND CURRENT ROW
+** ROWS BETWEEN CURRENT ROW         AND <expr> FOLLOWING
+** ROWS BETWEEN UNBOUNDED PRECEDING AND <expr> FOLLOWING
+**
+**   These are similar to the above. For "CURRENT ROW", intialize the
+**   register to 0. For "UNBOUNDED PRECEDING" to infinity.
+**
+** ROWS BETWEEN <expr> 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 <expr> PRECEDING    AND CURRENT ROW
-** ROWS BETWEEN CURRENT ROW         AND <expr> FOLLOWING
-** ROWS BETWEEN <expr> PRECEDING    AND UNBOUNDED FOLLOWING
-** ROWS BETWEEN UNBOUNDED PRECEDING AND <expr> 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 <expr> FOLLOWING    AND <expr> 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; k<nPeerVal; k++){
+      sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, 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; k<nPeerVal; k++){
+      int iOut = regPeerVal + nPeerVal + k;
+      sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, 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 <expr> PRECEDING    AND <expr> 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 <expr> FOLLOWING    AND <expr> 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 <expr> PRECEDING
-** ROWS BETWEEN <expr> 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;
index f1ab7a7ce9c467cd212deaf709382bcdc2a03e5e..8873093e0256fb6b811dc870724f074c0bea89ce 100644 (file)
@@ -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
 ==========
index f19b447600e88154ef741b49d4af12eca08df6b1..67b6b5df748fd92616b75936694ab544792012bf 100644 (file)
@@ -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