]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Allow "<expr> PRECEDING" to be used to specify the end of a window frame.
authordan <dan@noemail.net>
Thu, 24 May 2018 21:10:57 +0000 (21:10 +0000)
committerdan <dan@noemail.net>
Thu, 24 May 2018 21:10:57 +0000 (21:10 +0000)
FossilOrigin-Name: 7b709a989c188dbcf429989a0785294b36c8a7e89b5de8cefc25decf1f5b7f5a

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

index 3a161e7fea94664ea353308e5a3c047f7232a8b3..5ebfde392c041920887ca0437cfd19c72cdaf806 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Support\sother\sframe\stypes\sthat\suse\s"<expr>\sPRECEDING"\sor\s"<expr>\sFOLLOWING"\sas\nstart\sor\send\sconditions.
-D 2018-05-24T17:49:14.994
+C Allow\s"<expr>\sPRECEDING"\sto\sbe\sused\sto\sspecify\sthe\send\sof\sa\swindow\sframe.
+D 2018-05-24T21:10:57.618
 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 dc58ad62f2bb06d2e289ce65375b7d0047646b73c11a09fb0325febac0aebba7
+F src/window.c e4441e8ee3f83df85ff542048d3eed1c199ce6f90f3edcfeb1c4c1f53991b125
 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 798cfc8bef0f08a27a0ba64e147d8c72e9409c1673cc4ccff2ee7f150aa084e4
-F test/window2.test ca65b0818ddc948a7b0b07ee16a3c489dafcb958203bf8b75356eacd4696a206
+F test/window2.tcl 6df00293e3691a0d17624de339ce91632252e160701d9be663c21e3706f1dfd0
+F test/window2.test ee542c30bf8502f6df6f1a7304c2ca1d44143dfdd82dfad331f0b3b287de414c
 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 3a203660f1e4da3b8d2d605c494f4843f6e00752f28042b49e11d7d6550dd406
-R 91a6e47e9aa2b2bca4e7ec8da35f9bee
+P ec7b648c7f0ee266653561bbb9daa45b9be0d8a1a14f11dc93bce467c35154e6
+R cae23b80a7b926c27766da973f4ae9e5
 U dan
-Z 468858d850092ff66af7324bc283099f
+Z 6786b4d33cda910bcdf7493ef1afdc20
index 09eb988a9cffe2d9f59171e022ec0cf88da4bf24..c007a10b23f245e4bee8982d3ecc10fa601e07ea 100644 (file)
@@ -1 +1 @@
-ec7b648c7f0ee266653561bbb9daa45b9be0d8a1a14f11dc93bce467c35154e6
\ No newline at end of file
+7b709a989c188dbcf429989a0785294b36c8a7e89b5de8cefc25decf1f5b7f5a
\ No newline at end of file
index 47e418191531a9ae8938d618cab8bb11f560c038..433c6e3876b5af1cbb08320251f05ffd2e714854 100644 (file)
@@ -93,6 +93,26 @@ static void windowCheckFrameValue(Parse *pParse, int reg, int bEnd){
   sqlite3VdbeAppendP4(v, (void*)azErr[bEnd], P4_STATIC);
 }
 
+static void windowAggStep(
+  Parse *pParse, 
+  Window *pMWin, 
+  int csr,
+  int bInverse, 
+  int reg
+){
+  Vdbe *v = sqlite3GetVdbe(pParse);
+  Window *pWin;
+  for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
+    int i;
+    for(i=0; i<pWin->nArg; i++){
+      sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+i, reg+i);
+    }
+    sqlite3VdbeAddOp3(v, OP_AggStep0, bInverse, reg, pWin->regAccum);
+    sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
+    sqlite3VdbeChangeP5(v, (u8)pWin->nArg);
+  }
+}
+
 /*
 ** ROWS BETWEEN <expr> PRECEDING    AND <expr> FOLLOWING
 **
@@ -105,26 +125,26 @@ static void windowCheckFrameValue(Parse *pParse, int reg, int bEnd){
 **   Gosub flush_partition
 **
 ** flush_partition:
-**   OpenDup (csr -> csr2)
-**   OpenDup (csr -> csr3)
-**   regPrec = <expr1>            // PRECEDING expression
-**   regFollow = <expr2>          // FOLLOWING expression
-**   if( regPrec<0 || regFollow<0 ) throw exception!
-**   Rewind (csr,csr2,csr3)       // if EOF goto flush_partition_done
-**     Aggstep (csr3)
-**     Next(csr3)                 // if EOF fall-through
-**     if( (regFollow--)<=0 ){
+**   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 ){
 **       AggFinal (xValue)
 **       Gosub addrGosub
 **       Next(csr)                // if EOF goto flush_partition_done
-**       if( (regPrec--)<=0 ){
-**         AggStep (csr2, xInverse)
-**         Next(csr2)
+**       if( (regStart--)<=0 ){
+**         AggStep (csrStart, xInverse)
+**         Next(csrStart)
 **       }
 **     }
 ** flush_partition_done:
-**   Close (csr2)
-**   Close (csr3)
 **   ResetSorter (csr)
 **   Return
 **
@@ -136,6 +156,38 @@ static void windowCheckFrameValue(Parse *pParse, int reg, int bEnd){
 **   These are similar to the above. For "CURRENT ROW", intialize the
 **   register to 0. For "UNBOUNDED ..." to infinity.
 **
+** ROWS BETWEEN <expr> FOLLOWING    AND <expr> FOLLOWING
+**
+**   regEnd = regEnd - regStart
+**   Rewind (csr,csrStart,csrEnd)   // if EOF goto flush_partition_done
+**     Aggstep (csrEnd)
+**     Next(csrEnd)                 // if EOF fall-through
+**     if( (regEnd--)<=0 ){
+**       AggStep (csrStart, xInverse)
+**       Next (csrStart)
+**       if( (regStart--)<=0 ){
+**         AggFinal (xValue)
+**         Gosub addrGosub
+**         Next(csr)              // if EOF goto flush_partition_done
+**       }
+**     }
+**
+** ROWS BETWEEN <expr> PRECEDING    AND <expr> PRECEDING
+**
+**   Replace the bit after "Rewind" in the above with:
+**
+**     if( (regEnd--)<=0 ){
+**       AggStep (csrEnd)
+**       Next (csrEnd)
+**     }
+**     AggFinal (xValue)
+**     Gosub addrGosub
+**     Next(csr)                  // if EOF goto flush_partition_done
+**     if( (regStart--)<=0 ){
+**       AggStep (csr2, xInverse)
+**       Next (csr2)
+**     }
+**
 */
 static void windowCodeRowExprStep(
   Parse *pParse, 
@@ -151,19 +203,20 @@ static void windowCodeRowExprStep(
   int iSubCsr = p->pSrc->a[0].iCursor;
   int nSub = p->pSrc->a[0].pTab->nCol;
   int regFlushPart;               /* Register for "Gosub flush_partition" */
-  int addrFlushPart;              /* Label for "Gosub flush_partition" */
-  int addrDone;                   /* Label for "Gosub flush_partition_done" */
+  int lblFlushPart;               /* Label for "Gosub flush_partition" */
+  int lblFlushDone;               /* Label for "Gosub flush_partition_done" */
 
   int reg = pParse->nMem+1;
   int regRecord = reg+nSub;
   int regRowid = regRecord+1;
   int addr;
-  int csrPrec = pParse->nTab++;
-  int csrFollow = pParse->nTab++;
-  int regPrec;                    /* Value of <expr> PRECEDING */
-  int regFollow;                  /* Value of <expr> FOLLOWING */
+  int csrStart = pParse->nTab++;
+  int csrEnd = pParse->nTab++;
+  int regStart;                    /* Value of <expr> PRECEDING */
+  int regEnd;                      /* Value of <expr> FOLLOWING */
   int addrNext;
   int addrGoto;
+  int addrTop;
   int addrIfPos1;
   int addrIfPos2;
 
@@ -174,17 +227,18 @@ static void windowCodeRowExprStep(
   assert( pMWin->eEnd==TK_FOLLOWING 
        || pMWin->eEnd==TK_CURRENT 
        || pMWin->eEnd==TK_UNBOUNDED 
+       || pMWin->eEnd==TK_PRECEDING 
   );
 
   pParse->nMem += nSub + 2;
 
   /* Allocate register and label for the "flush_partition" sub-routine. */
   regFlushPart = ++pParse->nMem;
-  addrFlushPart = sqlite3VdbeMakeLabel(v);
-  addrDone = sqlite3VdbeMakeLabel(v);
+  lblFlushPart = sqlite3VdbeMakeLabel(v);
+  lblFlushDone = sqlite3VdbeMakeLabel(v);
 
-  regPrec = ++pParse->nMem;
-  regFollow = ++pParse->nMem;
+  regStart = ++pParse->nMem;
+  regEnd = ++pParse->nMem;
 
   /* Martial the row returned by the sub-select into an array of 
   ** registers. */
@@ -205,7 +259,7 @@ static void windowCodeRowExprStep(
     addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart,nPart);
     sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
     addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, addr+4, addr+2);
-    sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, addrFlushPart);
+    sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart);
     sqlite3VdbeAddOp3(v, OP_Copy, regNewPart, pMWin->regPart, nPart);
   }
 
@@ -217,62 +271,61 @@ static void windowCodeRowExprStep(
   sqlite3WhereEnd(pWInfo);
 
   /* Invoke "flush_partition" to deal with the final (or only) partition */
-  sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, addrFlushPart);
+  sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart);
   addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
 
   /* flush_partition: */
-  sqlite3VdbeResolveLabel(v, addrFlushPart);
+  sqlite3VdbeResolveLabel(v, lblFlushPart);
   sqlite3VdbeAddOp2(v, OP_Once, 0, sqlite3VdbeCurrentAddr(v)+3);
-  sqlite3VdbeAddOp2(v, OP_OpenDup, csrPrec, pMWin->iEphCsr);
-  sqlite3VdbeAddOp2(v, OP_OpenDup, csrFollow, pMWin->iEphCsr);
+  sqlite3VdbeAddOp2(v, OP_OpenDup, csrStart, pMWin->iEphCsr);
+  sqlite3VdbeAddOp2(v, OP_OpenDup, csrEnd, pMWin->iEphCsr);
 
-  /* If either regPrec or regFollow are not non-negative integers, throw 
+  /* If either regStart or regEnd are not non-negative integers, throw 
   ** an exception.  */
   if( pMWin->pStart ){
     assert( pMWin->eStart==TK_PRECEDING );
-    sqlite3ExprCode(pParse, pMWin->pStart, regPrec);
-    windowCheckFrameValue(pParse, regPrec, 0);
+    sqlite3ExprCode(pParse, pMWin->pStart, regStart);
+    windowCheckFrameValue(pParse, regStart, 0);
   }
   if( pMWin->pEnd ){
-    assert( pMWin->eEnd==TK_FOLLOWING );
-    sqlite3ExprCode(pParse, pMWin->pEnd, regFollow);
-    windowCheckFrameValue(pParse, regFollow, 1);
+    sqlite3ExprCode(pParse, pMWin->pEnd, regEnd);
+    windowCheckFrameValue(pParse, regEnd, 1);
   }
 
-  sqlite3VdbeAddOp2(v, OP_Null, 0, pMWin->regResult);
-  sqlite3VdbeAddOp2(v, OP_Null, 0, pMWin->regAccum);
+  for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
+    sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult);
+    sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum);
+  }
 
-  sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, addrDone);
-  sqlite3VdbeAddOp2(v, OP_Rewind, csrPrec, addrDone);
+  sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, lblFlushDone);
+  sqlite3VdbeAddOp2(v, OP_Rewind, csrStart, lblFlushDone);
   sqlite3VdbeChangeP5(v, 1);
-  sqlite3VdbeAddOp2(v, OP_Rewind, csrFollow, addrDone);
+  sqlite3VdbeAddOp2(v, OP_Rewind, csrEnd, lblFlushDone);
   sqlite3VdbeChangeP5(v, 1);
 
   /* Invoke AggStep function for each window function using the row that
-  ** csrFollow currently points to. Or, if csrFollow is already at EOF,
+  ** csrEnd currently points to. Or, if csrEnd is already at EOF,
   ** do nothing.  */
-  addrNext = sqlite3VdbeCurrentAddr(v);
-  sqlite3VdbeAddOp2(v, OP_Next, csrFollow, addrNext+2);
-  sqlite3VdbeAddOp0(v, OP_Goto);
-  for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
-    int i;
-    for(i=0; i<pWin->nArg; i++){
-      sqlite3VdbeAddOp3(v, OP_Column, csrFollow, pWin->iArgCol+i, reg+i);
-    }
-    sqlite3VdbeAddOp3(v, OP_AggStep0, 0, reg, pWin->regAccum);
-    sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
-    sqlite3VdbeChangeP5(v, (u8)pWin->nArg);
+  addrTop = sqlite3VdbeCurrentAddr(v);
+  if( pMWin->eEnd==TK_PRECEDING ){
+    addrIfPos1 = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0 , 1);
   }
+  sqlite3VdbeAddOp2(v, OP_Next, csrEnd, sqlite3VdbeCurrentAddr(v)+2);
+  addr = sqlite3VdbeAddOp0(v, OP_Goto);
+  windowAggStep(pParse, pMWin, csrEnd, 0, reg);
   if( pMWin->eEnd==TK_UNBOUNDED ){
-    sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNext);
-    sqlite3VdbeJumpHere(v, addrNext+1);
-    addrNext = sqlite3VdbeCurrentAddr(v);
+    sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop);
+    sqlite3VdbeJumpHere(v, addr);
+    addrTop = sqlite3VdbeCurrentAddr(v);
   }else{
-    sqlite3VdbeJumpHere(v, addrNext+1);
+    sqlite3VdbeJumpHere(v, addr);
+    if( pMWin->eEnd==TK_PRECEDING ){
+      sqlite3VdbeJumpHere(v, addrIfPos1);
+    }
   }
 
   if( pMWin->eEnd==TK_FOLLOWING ){
-    addrIfPos1 = sqlite3VdbeAddOp3(v, OP_IfPos, regFollow, 0 , 1);
+    addrIfPos1 = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0 , 1);
   }
   for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
     sqlite3VdbeAddOp3(v, 
@@ -282,22 +335,14 @@ static void windowCodeRowExprStep(
   }
   sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
   sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)+2);
-  sqlite3VdbeAddOp2(v, OP_Goto, 0, addrDone);
+  sqlite3VdbeAddOp2(v, OP_Goto, 0, lblFlushDone);
 
   if( pMWin->eStart==TK_CURRENT || pMWin->eStart==TK_PRECEDING ){
     if( pMWin->eStart==TK_PRECEDING ){
-      addrIfPos2 = sqlite3VdbeAddOp3(v, OP_IfPos, regPrec, 0 , 1);
-    }
-    sqlite3VdbeAddOp2(v, OP_Next, csrPrec, sqlite3VdbeCurrentAddr(v)+1);
-    for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
-      int i;
-      for(i=0; i<pWin->nArg; i++){
-        sqlite3VdbeAddOp3(v, OP_Column, csrPrec, pWin->iArgCol+i, reg+i);
-      }
-      sqlite3VdbeAddOp3(v, OP_AggStep0, 1, reg, pWin->regAccum);
-      sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
-      sqlite3VdbeChangeP5(v, (u8)pWin->nArg);
+      addrIfPos2 = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0 , 1);
     }
+    sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+1);
+    windowAggStep(pParse, pMWin, csrStart, 1, reg);
     if( pMWin->eStart==TK_PRECEDING ){
       sqlite3VdbeJumpHere(v, addrIfPos2);
     }
@@ -305,10 +350,10 @@ static void windowCodeRowExprStep(
   if( pMWin->eEnd==TK_FOLLOWING ){
     sqlite3VdbeJumpHere(v, addrIfPos1);
   }
-  sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNext);
+  sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop);
 
   /* flush_partition_done: */
-  sqlite3VdbeResolveLabel(v, addrDone);
+  sqlite3VdbeResolveLabel(v, lblFlushDone);
   sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
   sqlite3VdbeAddOp1(v, OP_Return, regFlushPart);
 
@@ -505,28 +550,28 @@ static void windowCodeDefaultStep(
 **
 **   Replace the bit after "Rewind" in the above with:
 **
-**     if( (regFollow--)<=0 ){
+**     if( (regEnd--)<=0 ){
 **       AggStep (csr3)
 **       Next (csr3)
 **     }
 **     AggFinal (xValue)
 **     Gosub addrGosub
 **     Next(csr)                  // if EOF goto flush_partition_done
-**     if( (regPrec--)<=0 ){
+**     if( (regStart--)<=0 ){
 **       AggStep (csr2, xInverse)
 **       Next (csr2)
 **     }
 **
 ** ROWS BETWEEN <expr> FOLLOWING    AND <expr> FOLLOWING
 **
-**   regFollow = regFollow - regPrec
+**   regEnd = regEnd - regStart
 **   Rewind (csr,csr2,csr3)       // if EOF goto flush_partition_done
 **     Aggstep (csr3)
 **     Next(csr3)                 // if EOF fall-through
-**     if( (regFollow--)<=0 ){
+**     if( (regEnd--)<=0 ){
 **       AggStep (csr2, xInverse)
 **       Next (csr2)
-**       if( (regPrec--)<=0 ){
+**       if( (regStart--)<=0 ){
 **         AggFinal (xValue)
 **         Gosub addrGosub
 **         Next(csr)              // if EOF goto flush_partition_done
@@ -536,7 +581,7 @@ static void windowCodeDefaultStep(
 ** ROWS BETWEEN UNBOUNDED PRECEDING AND <expr> PRECEDING
 ** ROWS BETWEEN <expr> FOLLOWING    AND UNBOUNDED FOLLOWING
 **
-**   Similar to the above, except with regPrec or regFollow set to infinity,
+**   Similar to the above, except with regStart or regEnd set to infinity,
 **   as appropriate.
 **
 **
@@ -552,10 +597,8 @@ void sqlite3WindowCodeStep(
 ){
   Window *pMWin = p->pWin;
 
-  if( pMWin->eType==TK_ROWS 
-   && (pMWin->eStart==TK_PRECEDING || pMWin->eEnd==TK_FOLLOWING)
-   && (pMWin->eStart!=TK_FOLLOWING || pMWin->eEnd==TK_PRECEDING)
-  ){
+  if( pMWin->pStart || pMWin->pEnd ){
+    assert( pMWin->eType==TK_ROWS );
     *pbLoop = 0;
     windowCodeRowExprStep(pParse, p, pWInfo, regGosub, addrGosub);
     return;
index 44a13cbf54c78ea0fea14fa3a72e647ebe2a88a0..c32249cc8bb40f15ce884e14c2d92d88b33a0b92 100644 (file)
@@ -219,6 +219,45 @@ execsql_test 2.13 {
   ) FROM t1
 }
 
+execsql_test 2.14 {
+  SELECT a, sum(d) OVER (
+    ORDER BY d 
+    ROWS BETWEEN 3 PRECEDING AND 1 PRECEDING
+  ) FROM t1
+}
+
+execsql_test 2.15 {
+  SELECT a, sum(d) OVER (
+    PARTITION BY b
+    ORDER BY d 
+    ROWS BETWEEN 1 PRECEDING AND 0 PRECEDING
+  ) FROM t1
+}
+
+execsql_test 2.16 {
+  SELECT a, sum(d) OVER (
+    PARTITION BY b
+    ORDER BY d 
+    ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING
+  ) FROM t1
+}
+
+execsql_test 2.17 {
+  SELECT a, sum(d) OVER (
+    PARTITION BY b
+    ORDER BY d 
+    ROWS BETWEEN 1 PRECEDING AND 2 PRECEDING
+  ) FROM t1
+}
+
+execsql_test 2.18 {
+  SELECT a, sum(d) OVER (
+    PARTITION BY b
+    ORDER BY d 
+    ROWS BETWEEN UNBOUNDED PRECEDING AND 2 PRECEDING
+  ) FROM t1
+}
+
 
 ==========
 
index a5d4ee853e0c98574f32126cc281915b8dd4eb53..bc6c44923539b6b3e6d9d7caf4d28e79b5afe517 100644 (file)
@@ -130,6 +130,45 @@ do_execsql_test 2.13 {
   ) FROM t1
 } {1 21   2 21   3 21   4 20   5 18   6 15}
 
+do_execsql_test 2.14 {
+  SELECT a, sum(d) OVER (
+    ORDER BY d 
+    ROWS BETWEEN 3 PRECEDING AND 1 PRECEDING
+  ) FROM t1
+} {1 {}   2 1   3 3   4 6   5 9   6 12}
+
+do_execsql_test 2.15 {
+  SELECT a, sum(d) OVER (
+    PARTITION BY b
+    ORDER BY d 
+    ROWS BETWEEN 1 PRECEDING AND 0 PRECEDING
+  ) FROM t1
+} {2 2   4 6   6 10   1 1   3 4   5 8}
+
+do_execsql_test 2.16 {
+  SELECT a, sum(d) OVER (
+    PARTITION BY b
+    ORDER BY d 
+    ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING
+  ) FROM t1
+} {2 {}   4 2   6 4   1 {}   3 1   5 3}
+
+do_execsql_test 2.17 {
+  SELECT a, sum(d) OVER (
+    PARTITION BY b
+    ORDER BY d 
+    ROWS BETWEEN 1 PRECEDING AND 2 PRECEDING
+  ) FROM t1
+} {2 {}   4 {}   6 {}   1 {}   3 {}   5 {}}
+
+do_execsql_test 2.18 {
+  SELECT a, sum(d) OVER (
+    PARTITION BY b
+    ORDER BY d 
+    ROWS BETWEEN UNBOUNDED PRECEDING AND 2 PRECEDING
+  ) FROM t1
+} {2 {}   4 {}   6 2   1 {}   3 {}   5 1}
+
 #==========================================================================
 
 finish_test