]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add support for "ROWS BETWEEN <expr> PRECEDING AND <expr> FOLLOWING" window
authordan <dan@noemail.net>
Wed, 23 May 2018 20:55:37 +0000 (20:55 +0000)
committerdan <dan@noemail.net>
Wed, 23 May 2018 20:55:37 +0000 (20:55 +0000)
frames.

FossilOrigin-Name: 3a203660f1e4da3b8d2d605c494f4843f6e00752f28042b49e11d7d6550dd406

manifest
manifest.uuid
src/btree.c
src/btree.h
src/func.c
src/select.c
src/sqliteInt.h
src/vdbe.c
src/window.c
test/window2.tcl
test/window2.test

index 1220173c3762b710ec8ae7a65ea391d5f5b5d533..4b502112030833bcd20bf02d9a7efa22b238e10a 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Merge\slatest\strunk\schanges\sinto\sthis\sbranch.
-D 2018-05-22T20:36:12.808
+C Add\ssupport\sfor\s"ROWS\sBETWEEN\s<expr>\sPRECEDING\sAND\s<expr>\sFOLLOWING"\swindow\nframes.
+D 2018-05-23T20:55:37.621
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F Makefile.in bfc40f350586923e0419d2ea4b559c37ec10ee4b6e210e08c14401f8e340f0da
@@ -435,8 +435,8 @@ F src/auth.c 6277d63837357549fe14e723490d6dc1a38768d71c795c5eb5c0f8a99f918f73
 F src/backup.c faf17e60b43233c214aae6a8179d24503a61e83b
 F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
 F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
-F src/btree.c 8270813c8f0ca91b2802e88ded3755d04ee962a923d431c13bcb6cf3e0c18f63
-F src/btree.h 448f15b98ea85dcf7e4eb76f731cadb89636c676ad25dfaac6de77cd66556598
+F src/btree.c b8fc4fcf851316fc0b84d4aa46899d127df952c39cfeb067bc97036060df1138
+F src/btree.h d46a8e31a4bd15572cdb6f2c940966f57b605b865628028c5eccf7d1bed83bac
 F src/btreeInt.h 620ab4c7235f43572cf3ac2ac8723cbdf68073be4d29da24897c7b77dda5fd96
 F src/build.c 50ff3e0fa07646b4d797aae0f773efcdb7602f6a5e2f5da27856503f35200889
 F src/callback.c fe677cb5f5abb02f7a772a62a98c2f516426081df68856e8f2d5f950929b966a
@@ -449,7 +449,7 @@ F src/delete.c b0f90749e22d5e41a12dbf940f4811138cf97da54b46b737089b93eb64a2896f
 F src/expr.c bb57b0b5ba1351335091ce4ec43b40968746f03afd65c9e2920d7cbe4dc98133
 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
 F src/fkey.c d617daf66b5515e2b42c1405b2b4984c30ca50fb705ab164271a9bf66c69e331
-F src/func.c b1a5122c69ef13c7bf0100e792ca539a36034c1b50476233ded6d2f72afcfbfc
+F src/func.c eff9c15696cda3485df3ae52ce3663692a2cd506fba63a5f49f56cb204831021
 F src/global.c 9bf034fd560bdd514715170ed8460bb7f823cec113f0569ef3f18a20c7ccd128
 F src/hash.c a12580e143f10301ed5166ea4964ae2853d3905a511d4e0c44497245c7ce1f7a
 F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4
@@ -494,12 +494,12 @@ F src/printf.c 1d1b4a568a58d0f32a5ff26c6b98db8a6e1883467f343a6406263cacd2e60c21
 F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
 F src/resolve.c 446f60b2e0d2440bb233d6a69a4ed0f2ad030a4e63ac4b3cfc0e98cf73d9c5a3
 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
-F src/select.c 1ebe775c0651bf357ab83b4c9b9139194149cfe1277dfa3e16f3ed73669b6b04
+F src/select.c 8a7f842a049a3407079e0b0748de916dcd91c00377394b2e8b1aefc5972a0b2f
 F src/shell.c.in 51c100206f4b7f86cd9affd80b764825e0edc36ca0190c442e4ca7994611bfe2
 F src/sqlite.h.in 34be2d0d18bf4726538793bdc9854cd87f689fda4b3789515134cdbd68188cf4
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7
-F src/sqliteInt.h a3f0edb26ebfee6107fa99f3c300e71018ad23addeeba0746a4ac62425e36f3f
+F src/sqliteInt.h 735b04170551a899e8703421c376f98c19503b8210ad4cd2e0f35b85b6af595d
 F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
 F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
 F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
@@ -564,7 +564,7 @@ F src/upsert.c ae4a4823b45c4daf87e8aea8c0f582a8844763271f5ed54ee5956c4c612734f4
 F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5
 F src/util.c d9eb0a6c4aae1b00a7369eadd7ca0bbe946cb4c953b6751aa20d357c2f482157
 F src/vacuum.c 37730af7540033135909ecaee3667dddec043293428d8718546d0d64ba4a5025
-F src/vdbe.c d83cfec9ebf523d5b2a8a3756ba8f23e39723725334a2e2e947e602ef6e6b278
+F src/vdbe.c 89c76c95a24e2561f5f94ef8530bd0127d512ed56b62932047ef89076d58fa91
 F src/vdbe.h d970d9738efdd09cb2df73e3a40856e7df13e88a3486789c49fcdd322c9eb8a2
 F src/vdbeInt.h 3878856fab3a8e64d27d472909e391db9d82f4f8b902a1737a1f7f351299ff52
 F src/vdbeapi.c 29d2baf9c1233131ec467d7bed1b7c8a03c27579048d768c4b04acf427838858
@@ -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 37eb02c2af935f207ba902ef25ec27d635b68bb1567f9e5994a6720bac1c093e
+F src/window.c 1313e941d1e50a44594e6f3e12bc7d0fe6f092ea35c1f3884c31bd224ba66d29
 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 7e0b692974a18ae0992dd2e76be83d8e1c6c5cac3190d84fa62911ab0e5c7896
-F test/window2.test e1453371b605e54eeb2264fc3a4a23c5eba93e95f6c7f3230fce9d34b4b5e8a4
+F test/window2.tcl 29e9bb16a52eb1e9e8f376519185af5c64eed88a8e6f0bee54237ba2971803a7
+F test/window2.test f580e1cc96d1ccb6bb220d1e338525ee5541e45e2206ed9ca74417ba862d8a62
 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 16168146b202915252f9375aef38e65ca20c5d4aa906e851d4d3a484db57562d c6071ac99cfa4b6272ac4d739fc61a85acb544f6c1c2ae67b31e92aadcc995bd
-R a9163894154abe7467dbbc7b918577f5
+P cdb68d2c64e453fdcd29437d5915c5c5ab6fbc7b5ffac52f4cb393f35b4a0124
+R 20d653691e285483f3e3bdb0b082aa4b
 U dan
-Z 671adb87a45ec21df0f125088181e31d
+Z e628ea8085262c078f67c7957c5a3c95
index 1fe351141444bbdfc36524faa65a99ed76cdcc92..8f6226467bbc021f08188e66f7e7c56c566ac8ce 100644 (file)
@@ -1 +1 @@
-cdb68d2c64e453fdcd29437d5915c5c5ab6fbc7b5ffac52f4cb393f35b4a0124
\ No newline at end of file
+3a203660f1e4da3b8d2d605c494f4843f6e00752f28042b49e11d7d6550dd406
\ No newline at end of file
index a73896e1610e48a28d10e8b1c6c66676a2423869..316fa701d66d2f5e0d7510525525d193f8f0bdf5 100644 (file)
@@ -5176,6 +5176,13 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
   return rc;
 }
 
+void sqlite3BtreeSkipNext(BtCursor *pCur){
+  if( pCur->eState==CURSOR_VALID ){
+    pCur->eState = CURSOR_SKIPNEXT;
+    pCur->skipNext = 1;
+  }
+}
+
 /* Move the cursor to the last entry in the table.  Return SQLITE_OK
 ** on success.  Set *pRes to 0 if the cursor actually points to something
 ** or set *pRes to 1 if the table is empty.
index b5bf9f7564c11bf08ee90f2d22c52c1a1a19b606..20fdbdae2ee2d8043b144eca6863d102810a41f9 100644 (file)
@@ -301,6 +301,7 @@ struct BtreePayload {
 int sqlite3BtreeInsert(BtCursor*, const BtreePayload *pPayload,
                        int flags, int seekResult);
 int sqlite3BtreeFirst(BtCursor*, int *pRes);
+void sqlite3BtreeSkipNext(BtCursor*);
 int sqlite3BtreeLast(BtCursor*, int *pRes);
 int sqlite3BtreeNext(BtCursor*, int flags);
 int sqlite3BtreeEof(BtCursor*);
index ea1fffaf0b67be36ac9cbb0bb60cf17954d526cb..c5836f55f1ee25671c9464cfde11facbff708c2a 100644 (file)
@@ -1513,6 +1513,27 @@ static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){
     }
   }
 }
+static void sumInverse(sqlite3_context *context, int argc, sqlite3_value**argv){
+  SumCtx *p;
+  int type;
+  assert( argc==1 );
+  UNUSED_PARAMETER(argc);
+  p = sqlite3_aggregate_context(context, sizeof(*p));
+  type = sqlite3_value_numeric_type(argv[0]);
+  if( p && type!=SQLITE_NULL ){
+    p->cnt--;
+    if( type==SQLITE_INTEGER ){
+      i64 v = sqlite3_value_int64(argv[0]);
+      p->rSum -= v;
+      if( (p->approx|p->overflow)==0 && sqlite3AddInt64(&p->iSum, -1*v) ){
+        p->overflow = 1;
+      }
+    }else{
+      p->rSum += sqlite3_value_double(argv[0]);
+      p->approx = 1;
+    }
+  }
+}
 static void sumFinalize(sqlite3_context *context){
   SumCtx *p;
   p = sqlite3_aggregate_context(context, 0);
@@ -1873,12 +1894,12 @@ void sqlite3RegisterBuiltinFunctions(void){
     FUNCTION(zeroblob,           1, 0, 0, zeroblobFunc     ),
     FUNCTION(substr,             2, 0, 0, substrFunc       ),
     FUNCTION(substr,             3, 0, 0, substrFunc       ),
-    WAGGREGATE(sum,               1, 0, 0, sumStep,         sumFinalize),
-    WAGGREGATE(total,             1, 0, 0, sumStep,         totalFinalize    ),
-    WAGGREGATE(avg,               1, 0, 0, sumStep,         avgFinalize    ),
+    WAGGREGATE(sum,        1, 0, 0, sumStep, sumInverse,   sumFinalize),
+    WAGGREGATE(total,      1, 0, 0, sumStep, sumInverse,   totalFinalize    ),
+    WAGGREGATE(avg,        1, 0, 0, sumStep, sumInverse,   avgFinalize    ),
     AGGREGATE2(count,            0, 0, 0, countStep,       countFinalize,
                SQLITE_FUNC_COUNT  ),
-    WAGGREGATE(count,             1, 0, 0, countStep,       countFinalize  ),
+    WAGGREGATE(count,             1, 0, 0, countStep, 0,    countFinalize  ),
     AGGREGATE(group_concat,      1, 0, 0, groupConcatStep, groupConcatFinalize,
         groupConcatValue),
     AGGREGATE(group_concat,      2, 0, 0, groupConcatStep, groupConcatFinalize,
index 12d6e5ff39f0e17f76b513d4cc12d4e92ea81c5b..bff5d36b54cfa18eb4d5fe28366e0caefb43dd93 100644 (file)
@@ -5576,7 +5576,7 @@ static int selectWindowRewrite(Parse *pParse, Select *p){
     }
 #endif
 
-    sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pMWin->iEphCsr, pMWin->nBufferCol);
+    sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pMWin->iEphCsr, pSublist->nExpr);
   }
 
   return rc;
@@ -6087,16 +6087,23 @@ int sqlite3Select(
     if( pWin ){
       int addrGosub = sqlite3VdbeMakeLabel(v);
       int regGosub = ++pParse->nMem;
-      int addr;
+      int addr = 0;
+      int bLoop = 0;
 
-      sqlite3WindowCodeStep(pParse, p, pWInfo, regGosub, addrGosub);
+      sqlite3WindowCodeStep(pParse, p, pWInfo, regGosub, addrGosub, &bLoop);
 
       sqlite3VdbeAddOp0(v, OP_Goto);
       sqlite3VdbeResolveLabel(v, addrGosub);
-      addr = sqlite3VdbeAddOp1(v, OP_Rewind, pWin->iEphCsr);
+      if( bLoop ){
+        addr = sqlite3VdbeAddOp1(v, OP_Rewind, pWin->iEphCsr);
+      }else{
+        addr = sqlite3VdbeCurrentAddr(v);
+      }
       selectInnerLoop(pParse, p, -1, &sSort, &sDistinct, pDest, addr+1, 0);
-      sqlite3VdbeAddOp2(v, OP_Next, pWin->iEphCsr, addr+1);
-      sqlite3VdbeJumpHere(v, addr);
+      if( bLoop ){
+        sqlite3VdbeAddOp2(v, OP_Next, pWin->iEphCsr, addr+1);
+        sqlite3VdbeJumpHere(v, addr);
+      }
       sqlite3VdbeAddOp1(v, OP_Return, regGosub);
       sqlite3VdbeJumpHere(v, addr-1);       /* OP_Goto jumps here */
 
index 45e4934c2e205b9b789617c2557a7237b7c258ee..35db193975eabb3e4d7d4b8ed66113919ed161b1 100644 (file)
@@ -1723,9 +1723,9 @@ struct FuncDestructor {
   {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|extraFlags, \
    SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xFinal,0,#zName, {0}}
 
-#define WAGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \
+#define WAGGREGATE(zName, nArg, arg, nc, xStep, xInverse, xFinal) \
   {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL), \
-   SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xFinal,0,#zName, {0}}
+   SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xFinal,xInverse,#zName, {0}}
 
 /*
 ** All current savepoints are stored in a linked list starting at
@@ -3496,7 +3496,7 @@ Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*);
 void sqlite3WindowAttach(Parse*, Expr*, Window*);
 int sqlite3WindowCompare(Parse*, Window*, Window*);
 void sqlite3WindowCodeInit(Parse*, Window*);
-void sqlite3WindowCodeStep(Parse*, Select*, WhereInfo*, int, int);
+void sqlite3WindowCodeStep(Parse*, Select*, WhereInfo*, int, int, int*);
 
 /*
 ** Assuming zIn points to the first byte of a UTF-8 character,
index 7ab1044552553beff849b3b4b1f354101bbfdf75..9f8af702eda971f644efca287eb36a9abad778e1 100644 (file)
@@ -5018,6 +5018,7 @@ case OP_Rewind: {        /* jump */
     pCrsr = pC->uc.pCursor;
     assert( pCrsr );
     rc = sqlite3BtreeFirst(pCrsr, &res);
+    if( pOp->p5 ) sqlite3BtreeSkipNext(pCrsr);
     pC->deferredMoveto = 0;
     pC->cacheStatus = CACHE_STALE;
   }
@@ -6273,7 +6274,8 @@ case OP_AggStep: {
   assert( pCtx->pOut->flags==MEM_Null );
   assert( pCtx->isError==0 );
   assert( pCtx->skipFlag==0 );
-  (pCtx->pFunc->xSFunc)(pCtx,pCtx->argc,pCtx->argv); /* IMP: R-24505-23230 */
+  (pOp->p1 ? (pCtx->pFunc->xInverse) : (pCtx->pFunc->xSFunc))
+    (pCtx,pCtx->argc,pCtx->argv); /* IMP: R-24505-23230 */
   if( pCtx->isError ){
     if( pCtx->isError>0 ){
       sqlite3VdbeError(p, "%s", sqlite3_value_text(pCtx->pOut));
index 4eb04db91c3d5c6354ea6ecb2eb38052b00e48d8..6af6d4c7ce2d6ded577fd353cc2a80fd6b10496e 100644 (file)
@@ -25,8 +25,8 @@ void sqlite3WindowDelete(sqlite3 *db, Window *p){
 Window *sqlite3WindowAlloc(
   Parse *pParse, 
   int eType,
-  int eEnd, Expr *pEnd, 
-  int eStart, Expr *pStart
+  int eStart, Expr *pStart,
+  int eEnd, Expr *pEnd
 ){
   Window *pWin = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
 
@@ -77,6 +77,279 @@ void sqlite3WindowCodeInit(Parse *pParse, Window *pWin){
   }
 }
 
+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;
+
+
+  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);
+}
+
+static void windowCodeRowExprStep(
+  Parse *pParse, 
+  Select *p,
+  WhereInfo *pWInfo,
+  int regGosub, 
+  int addrGosub
+){
+  Window *pMWin = p->pWin;
+  Vdbe *v = sqlite3GetVdbe(pParse);
+  Window *pWin;
+  int k;
+  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 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 addrNext;
+  int addrGoto;
+  int addrIfPos1;
+  int addrIfPos2;
+
+  pParse->nMem += nSub + 2;
+
+  /* Allocate register and label for the "flush_partition" sub-routine. */
+  regFlushPart = ++pParse->nMem;
+  addrFlushPart = sqlite3VdbeMakeLabel(v);
+  addrDone = sqlite3VdbeMakeLabel(v);
+
+  regPrec = ++pParse->nMem;
+  regFollow = ++pParse->nMem;
+
+  /* Martial the row returned by the sub-select into an array of 
+  ** registers. */
+  for(k=0; k<nSub; k++){
+    sqlite3VdbeAddOp3(v, OP_Column, iSubCsr, k, reg+k);
+  }
+  sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, nSub, regRecord);
+
+  /* Check if this is the start of a new partition. If so, call the
+  ** flush_partition sub-routine.  */
+  if( pMWin->pPartition ){
+    ExprList *pPart = pMWin->pPartition;
+    int nPart = (pPart ? pPart->nExpr : 0);
+    int addrJump = 0;
+    int regNewPart = reg + pMWin->nBufferCol;
+    KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0);
+
+    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);
+    sqlite3VdbeAddOp3(v, OP_Copy, regNewPart, pMWin->regPart, nPart);
+  }
+
+  /* Buffer the current row in the ephemeral table. */
+  sqlite3VdbeAddOp2(v, OP_NewRowid, pMWin->iEphCsr, regRowid);
+  sqlite3VdbeAddOp3(v, OP_Insert, pMWin->iEphCsr, regRecord, regRowid);
+
+  /* End of the input loop */
+  sqlite3WhereEnd(pWInfo);
+
+  /* Invoke "flush_partition" to deal with the final (or only) partition */
+  sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, addrFlushPart);
+  addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
+
+  /* flush_partition: */
+  sqlite3VdbeResolveLabel(v, addrFlushPart);
+  sqlite3VdbeAddOp2(v, OP_Once, 0, sqlite3VdbeCurrentAddr(v)+3);
+  sqlite3VdbeAddOp2(v, OP_OpenDup, csrPrec, pMWin->iEphCsr);
+  sqlite3VdbeAddOp2(v, OP_OpenDup, csrFollow, pMWin->iEphCsr);
+
+  sqlite3ExprCode(pParse, pMWin->pStart, regPrec);
+  sqlite3ExprCode(pParse, pMWin->pEnd, regFollow);
+
+  sqlite3VdbeAddOp2(v, OP_Null, 0, pMWin->regResult);
+  sqlite3VdbeAddOp2(v, OP_Null, 0, pMWin->regAccum);
+
+  /* If either regPrec or regFollow are not non-negative integers, throw an
+  ** exception.  */
+  windowCheckFrameValue(pParse, regPrec, 0);
+  windowCheckFrameValue(pParse, regFollow, 1);
+
+  sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, addrDone);
+  sqlite3VdbeAddOp2(v, OP_Rewind, csrPrec, addrDone);
+  sqlite3VdbeChangeP5(v, 1);
+  sqlite3VdbeAddOp2(v, OP_Rewind, csrFollow, addrDone);
+  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,
+  ** 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);
+  }
+  sqlite3VdbeJumpHere(v, addrNext+1);
+
+  addrIfPos1 = sqlite3VdbeAddOp3(v, OP_IfPos, regFollow, 0 , 1);
+  for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
+    sqlite3VdbeAddOp3(v, 
+        OP_AggFinal, pWin->regAccum, pWin->nArg, pWin->regResult
+    );
+    sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
+  }
+  sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
+  sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)+2);
+  sqlite3VdbeAddOp2(v, OP_Goto, 0, addrDone);
+
+  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);
+  }
+  sqlite3VdbeJumpHere(v, addrIfPos2);
+
+  sqlite3VdbeJumpHere(v, addrIfPos1);
+  sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNext);
+
+  /* flush_partition_done: */
+  sqlite3VdbeResolveLabel(v, addrDone);
+  sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
+  sqlite3VdbeAddOp1(v, OP_Return, regFlushPart);
+
+  /* Jump to here to skip over flush_partition */
+  sqlite3VdbeJumpHere(v, addrGoto);
+}
+
+static void windowCodeDefaultStep(
+  Parse *pParse, 
+  Select *p,
+  WhereInfo *pWInfo,
+  int regGosub, 
+  int addrGosub
+){
+  Window *pMWin = p->pWin;
+  Vdbe *v = sqlite3GetVdbe(pParse);
+  Window *pWin;
+  int k;
+  int iSubCsr = p->pSrc->a[0].iCursor;
+  int nSub = p->pSrc->a[0].pTab->nCol;
+  int reg = pParse->nMem+1;
+  int regRecord = reg+nSub;
+  int regRowid = regRecord+1;
+  int addr;
+
+  pParse->nMem += nSub + 2;
+
+  /* Martial the row returned by the sub-select into an array of 
+  ** registers. */
+  for(k=0; k<nSub; k++){
+    sqlite3VdbeAddOp3(v, OP_Column, iSubCsr, k, reg+k);
+  }
+
+  /* Check if this is the start of a new partition or peer group. */
+  if( pMWin->regPart ){
+    ExprList *pPart = pMWin->pPartition;
+    int nPart = (pPart ? pPart->nExpr : 0);
+    ExprList *pOrderBy = pMWin->pOrderBy;
+    int nPeer = (pOrderBy ? pOrderBy->nExpr : 0);
+    int addrGoto = 0;
+    int addrJump = 0;
+
+    if( pPart ){
+      int regNewPart = reg + pMWin->nBufferCol;
+      KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0);
+      addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart,nPart);
+      sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
+      addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
+      for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
+        sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, pWin->nArg);
+        sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
+        sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult);
+      }
+      if( pOrderBy ){
+        addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
+      }
+    }
+
+    if( pOrderBy ){
+      int regNewPeer = reg + pMWin->nBufferCol + nPart;
+      int regPeer = pMWin->regPart + nPart;
+
+      KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0);
+      if( addrJump ) sqlite3VdbeJumpHere(v, addrJump);
+      addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer, nPeer);
+      sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
+      addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
+      for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
+        sqlite3VdbeAddOp3(v, 
+            OP_AggFinal, pWin->regAccum, pWin->nArg, pWin->regResult
+        );
+        sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
+      }
+      if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto);
+    }
+
+    sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
+    sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
+    sqlite3VdbeAddOp3(
+        v, OP_Copy, reg+pMWin->nBufferCol, pMWin->regPart, nPart+nPeer-1
+    );
+
+    sqlite3VdbeJumpHere(v, addrJump);
+  }
+
+  /* Invoke step function for window functions */
+  for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
+    sqlite3VdbeAddOp3(v, OP_AggStep0, 0, reg+pWin->iArgCol, pWin->regAccum);
+    sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
+    sqlite3VdbeChangeP5(v, (u8)pWin->nArg);
+  }
+
+  /* Buffer the current row in the ephemeral table. */
+  if( pMWin->nBufferCol>0 ){
+    sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, pMWin->nBufferCol, regRecord);
+  }else{
+    sqlite3VdbeAddOp2(v, OP_Blob, 0, regRecord);
+    sqlite3VdbeAppendP4(v, (void*)"", 0);
+  }
+  sqlite3VdbeAddOp2(v, OP_NewRowid, pMWin->iEphCsr, regRowid);
+  sqlite3VdbeAddOp3(v, OP_Insert, pMWin->iEphCsr, regRecord, regRowid);
+
+  /* End the database scan loop. */
+  sqlite3WhereEnd(pWInfo);
+
+  for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
+    sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, pWin->nArg);
+    sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
+    sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult);
+  }
+  sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
+}
+
+
 /*
 ** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
 **
@@ -243,105 +516,22 @@ void sqlite3WindowCodeStep(
   Select *p,
   WhereInfo *pWInfo,
   int regGosub, 
-  int addrGosub
+  int addrGosub,
+  int *pbLoop
 ){
-  Vdbe *v = sqlite3GetVdbe(pParse);
-  Window *pWin;
   Window *pMWin = p->pWin;
-  int k;
-  int iSubCsr = p->pSrc->a[0].iCursor;
-  int nSub = p->pSrc->a[0].pTab->nCol;
-  int reg = pParse->nMem+1;
-  int regRecord = reg+nSub;
-  int regRowid = regRecord+1;
-  int addr;
-
-  pParse->nMem += nSub + 2;
 
-  /* Martial the row returned by the sub-select into an array of 
-  ** registers. */
-  for(k=0; k<nSub; k++){
-    sqlite3VdbeAddOp3(v, OP_Column, iSubCsr, k, reg+k);
+  if( pMWin->eType==TK_ROWS 
+   && pMWin->eStart==TK_PRECEDING
+   && pMWin->eEnd==TK_FOLLOWING
+  ){
+    *pbLoop = 0;
+    windowCodeRowExprStep(pParse, p, pWInfo, regGosub, addrGosub);
+    return;
   }
 
-  /* Check if this is the start of a new partition or peer group. */
-  if( pMWin->regPart ){
-    ExprList *pPart = pMWin->pPartition;
-    int nPart = (pPart ? pPart->nExpr : 0);
-    ExprList *pOrderBy = pMWin->pOrderBy;
-    int nPeer = (pOrderBy ? pOrderBy->nExpr : 0);
-    int addrGoto = 0;
-    int addrJump = 0;
-
-    if( pPart ){
-      int regNewPart = reg + pMWin->nBufferCol;
-      KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0);
-      addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart,nPart);
-      sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
-      addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
-      for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
-        sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, pWin->nArg);
-        sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
-        sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult);
-      }
-      if( pOrderBy ){
-        addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
-      }
-    }
-
-    if( pOrderBy ){
-      int regNewPeer = reg + pMWin->nBufferCol + nPart;
-      int regPeer = pMWin->regPart + nPart;
-
-      KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0);
-      if( addrJump ) sqlite3VdbeJumpHere(v, addrJump);
-      addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer, nPeer);
-      sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
-      addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
-      for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
-        sqlite3VdbeAddOp3(v, 
-            OP_AggFinal, pWin->regAccum, pWin->nArg, pWin->regResult
-            );
-        sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
-      }
-      if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto);
-    }
-
-    sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
-    sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
-    sqlite3VdbeAddOp3(
-        v, OP_Copy, reg+pMWin->nBufferCol, pMWin->regPart, nPart+nPeer-1
-    );
-
-    sqlite3VdbeJumpHere(v, addrJump);
-  }
-
-  /* Invoke step function for window functions */
-  for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
-    sqlite3VdbeAddOp3(v, OP_AggStep0, 0, reg+pWin->iArgCol, pWin->regAccum);
-    sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
-    sqlite3VdbeChangeP5(v, (u8)pWin->nArg);
-  }
-
-  /* Buffer the current row in the ephemeral table. */
-  if( pMWin->nBufferCol>0 ){
-    sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, pMWin->nBufferCol, regRecord);
-  }else{
-    sqlite3VdbeAddOp2(v, OP_Blob, 0, regRecord);
-    sqlite3VdbeAppendP4(v, (void*)"", 0);
-  }
-  sqlite3VdbeAddOp2(v, OP_NewRowid, pMWin->iEphCsr, regRowid);
-  sqlite3VdbeAddOp3(v, OP_Insert, pMWin->iEphCsr, regRecord, regRowid);
-
-  /* End the database scan loop. */
-  sqlite3WhereEnd(pWInfo);
-
-  for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
-    sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, pWin->nArg);
-    sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
-    sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult);
-  }
-  sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
+  *pbLoop = 1;
+  windowCodeDefaultStep(pParse, p, pWInfo, regGosub, addrGosub);
 }
 
 
index 54fa9d6bc90cf0a495a96623884d4e8a97e7327d..4d2503715b4be90b6eef1945c81da0197ffcb7d2 100644 (file)
@@ -136,41 +136,75 @@ execsql_test 1.3 {
   SELECT sum(d) OVER (PARTITION BY b) FROM t1;
 }
 
-puts $::fd finish_test
 ==========
-
 execsql_test 2.1 {
   SELECT a, sum(d) OVER (
-    PARTITION BY b ORDER BY d
-    RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    ORDER BY d
+    ROWS BETWEEN 1000 PRECEDING AND 1 FOLLOWING
   ) FROM t1
 }
-
 execsql_test 2.2 {
   SELECT a, sum(d) OVER (
-    ORDER BY b
-    RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    ORDER BY d
+    ROWS BETWEEN 1000 PRECEDING AND 1000 FOLLOWING
   ) FROM t1
 }
-
 execsql_test 2.3 {
   SELECT a, sum(d) OVER (
     ORDER BY d
-    ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
+    ROWS BETWEEN 1 PRECEDING AND 1000 FOLLOWING
   ) FROM t1
 }
-
 execsql_test 2.4 {
   SELECT a, sum(d) OVER (
     ORDER BY d
     ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
   ) FROM t1
 }
-
 execsql_test 2.5 {
   SELECT a, sum(d) OVER (
     ORDER BY d
-    ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING
+    ROWS BETWEEN 1 PRECEDING AND 0 FOLLOWING
+  ) FROM t1
+}
+
+execsql_test 2.6 {
+  SELECT a, sum(d) OVER (
+    PARTITION BY b
+    ORDER BY d 
+    ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
+  ) FROM t1
+}
+
+execsql_test 2.7 {
+  SELECT a, sum(d) OVER (
+    PARTITION BY b
+    ORDER BY d 
+    ROWS BETWEEN 0 PRECEDING AND 0 FOLLOWING
+  ) FROM t1
+}
+
+puts $::fd finish_test
+==========
+
+execsql_test 3.1 {
+  SELECT a, sum(d) OVER (
+    PARTITION BY b ORDER BY d
+    RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+  ) FROM t1
+}
+
+execsql_test 3.2 {
+  SELECT a, sum(d) OVER (
+    ORDER BY b
+    RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+  ) FROM t1
+}
+
+execsql_test 3.3 {
+  SELECT a, sum(d) OVER (
+    ORDER BY d
+    ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
   ) FROM t1
 }
 
index 40a652cf1449938c5d948044e0bd3a634e63dfc4..db4eb44dc7a88ca42abfc5160f4aee148ffc69e4 100644 (file)
@@ -42,6 +42,59 @@ do_execsql_test 1.3 {
   SELECT sum(d) OVER (PARTITION BY b) FROM t1;
 } {12   12   12   9   9   9}
 
+#==========================================================================
+
+do_execsql_test 2.1 {
+  SELECT a, sum(d) OVER (
+    ORDER BY d
+    ROWS BETWEEN 1000 PRECEDING AND 1 FOLLOWING
+  ) FROM t1
+} {1 3   2 6   3 10   4 15   5 21   6 21}
+
+do_execsql_test 2.2 {
+  SELECT a, sum(d) OVER (
+    ORDER BY d
+    ROWS BETWEEN 1000 PRECEDING AND 1000 FOLLOWING
+  ) FROM t1
+} {1 21   2 21   3 21   4 21   5 21   6 21}
+
+do_execsql_test 2.3 {
+  SELECT a, sum(d) OVER (
+    ORDER BY d
+    ROWS BETWEEN 1 PRECEDING AND 1000 FOLLOWING
+  ) FROM t1
+} {1 21   2 21   3 20   4 18   5 15   6 11}
+
+do_execsql_test 2.4 {
+  SELECT a, sum(d) OVER (
+    ORDER BY d
+    ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
+  ) FROM t1
+} {1 3   2 6   3 9   4 12   5 15   6 11}
+
+do_execsql_test 2.5 {
+  SELECT a, sum(d) OVER (
+    ORDER BY d
+    ROWS BETWEEN 1 PRECEDING AND 0 FOLLOWING
+  ) FROM t1
+} {1 1   2 3   3 5   4 7   5 9   6 11}
+
+do_execsql_test 2.6 {
+  SELECT a, sum(d) OVER (
+    PARTITION BY b
+    ORDER BY d 
+    ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
+  ) FROM t1
+} {2 6   4 12   6 10   1 4   3 9   5 8}
+
+do_execsql_test 2.7 {
+  SELECT a, sum(d) OVER (
+    PARTITION BY b
+    ORDER BY d 
+    ROWS BETWEEN 0 PRECEDING AND 0 FOLLOWING
+  ) FROM t1
+} {2 2   4 4   6 6   1 1   3 3   5 5}
+
 finish_test
 #==========================================================================