]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Begin adding support for more esoteric window frames.
authordan <dan@noemail.net>
Mon, 21 May 2018 19:45:11 +0000 (19:45 +0000)
committerdan <dan@noemail.net>
Mon, 21 May 2018 19:45:11 +0000 (19:45 +0000)
FossilOrigin-Name: bc4b81d60d40583de0f929730159011c1a7696802532ebd02220de3ace94a60d

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

index b33fe88ab5c33606b1c4e4a3746c366c87f13b58..2da13c99430a6f3cb447cce145f08557313ff2c0 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sminor\sproblems\son\sthis\sbranch.
-D 2018-05-19T14:15:29.031
+C Begin\sadding\ssupport\sfor\smore\sesoteric\swindow\sframes.
+D 2018-05-21T19:45:11.880
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F Makefile.in bfc40f350586923e0419d2ea4b559c37ec10ee4b6e210e08c14401f8e340f0da
@@ -493,12 +493,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 34a5cb9e6a37d4f7e25160ba0caeb02bccc1abee462805ba5f3fde9abc266873
+F src/select.c 1ebe775c0651bf357ab83b4c9b9139194149cfe1277dfa3e16f3ed73669b6b04
 F src/shell.c.in 53affa90711f280342f7238f9c9aa9dcaed321ec6218a18043cf92154ef8a704
 F src/sqlite.h.in 34be2d0d18bf4726538793bdc9854cd87f689fda4b3789515134cdbd68188cf4
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7
-F src/sqliteInt.h 6b3400a90f179542045ed318c780bf73675b19b3de437506859adf998a41e125
+F src/sqliteInt.h a3f0edb26ebfee6107fa99f3c300e71018ad23addeeba0746a4ac62425e36f3f
 F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
 F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
 F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
@@ -581,7 +581,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 33d3751eb1442ce8a7e428e028cbc6c220359446a7ab2a9514244c3edfea1b63
+F src/window.c da24f2e57a704dd8e0ce96df18e7442145582c65b4eb1c3176367e530d665928
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
 F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
@@ -1614,8 +1614,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 f9df2fded8de70fc4252030775351b42440bc10aa2eb07d2f625e5d14a666775
-F test/window2.test 57c0500f98a08fd372f7fecf60f0d2ade58ad3373dad5031063d0a278fd79b18
+F test/window2.tcl 19a7d45c4502f00917649a171a4eb1da3670657f5cb102bce7a813e15a112b3f
+F test/window2.test 23daf252647d780f046a2e612d7f0492d4fe589b3302fd6c062b0abfff3743bf
 F test/with1.test 58475190cd8caaeebea8cfeb2a264ec97a0c492b8ffe9ad20cefbb23df462f96
 F test/with2.test e0030e2f0267a910d6c0e4f46f2dfe941c1cc0d4f659ba69b3597728e7e8f1ab
 F test/with3.test 5e8ce2c585170bbbc0544e2a01a4941fa0be173ba5265e5c92eb588cd99a232d
@@ -1732,7 +1732,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 c9f0f140941660ff368e5bb5752d54feb1964b7a9eac986d4bfb8f24a1c20d86
-R 6dddf8e93774e8d8cc7600c6ceb6a6b5
+P 19c2e4b2f164521eab84cb0a0e12984be9431eaedd001dd3671e9ea1a6212353
+R 977b49583a548e1f3f37fbf1de8b5833
 U dan
-Z 12927ac87a28c4acd3b751d346125a3b
+Z 314fa758fa6aadc36a0f111ad4e55744
index e8182e6ce4eee48e3c1da1bbf47530f59a82dfc9..2c19d62e695bf2dd231e4f384731cac1710408c7 100644 (file)
@@ -1 +1 @@
-19c2e4b2f164521eab84cb0a0e12984be9431eaedd001dd3671e9ea1a6212353
\ No newline at end of file
+bc4b81d60d40583de0f929730159011c1a7696802532ebd02220de3ace94a60d
\ No newline at end of file
index cbe3183c0f1e0b4e7577639cca327ac1df5d01c9..12d6e5ff39f0e17f76b513d4cc12d4e92ea81c5b 100644 (file)
@@ -530,14 +530,6 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
   return 0;
 }
 
-/* Forward reference */
-static KeyInfo *keyInfoFromExprList(
-  Parse *pParse,       /* Parsing context */
-  ExprList *pList,     /* Form the KeyInfo object from this ExprList */
-  int iStart,          /* Begin with this column of pList */
-  int nExtra           /* Add this many extra columns to the end */
-);
-
 /*
 ** An instance of this object holds information (beyond pParse and pSelect)
 ** needed to load the next result row that is to be added to the sorter.
@@ -679,7 +671,7 @@ static void pushOntoSorter(
     memset(pKI->aSortOrder, 0, pKI->nKeyField); /* Makes OP_Jump testable */
     sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO);
     testcase( pKI->nAllField > pKI->nKeyField+2 );
-    pOp->p4.pKeyInfo = keyInfoFromExprList(pParse, pSort->pOrderBy, nOBSat,
+    pOp->p4.pKeyInfo = sqlite3KeyInfoFromExprList(pParse,pSort->pOrderBy,nOBSat,
                                            pKI->nAllField-pKI->nKeyField-1);
     addrJmp = sqlite3VdbeCurrentAddr(v);
     sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v);
@@ -1349,7 +1341,7 @@ int sqlite3KeyInfoIsWriteable(KeyInfo *p){ return p->nRef==1; }
 ** function is responsible for seeing that this structure is eventually
 ** freed.
 */
-static KeyInfo *keyInfoFromExprList(
+KeyInfo *sqlite3KeyInfoFromExprList(
   Parse *pParse,       /* Parsing context */
   ExprList *pList,     /* Form the KeyInfo object from this ExprList */
   int iStart,          /* Begin with this column of pList */
@@ -5088,7 +5080,7 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
            "argument");
         pFunc->iDistinct = -1;
       }else{
-        KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList, 0, 0);
+        KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pE->x.pList,0,0);
         sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0,
                           (char*)pKeyInfo, P4_KEYINFO);
       }
@@ -6008,7 +6000,8 @@ int sqlite3Select(
   */
   if( sSort.pOrderBy ){
     KeyInfo *pKeyInfo;
-    pKeyInfo = keyInfoFromExprList(pParse, sSort.pOrderBy, 0, pEList->nExpr);
+    pKeyInfo = sqlite3KeyInfoFromExprList(
+        pParse, sSort.pOrderBy, 0, pEList->nExpr);
     sSort.iECursor = pParse->nTab++;
     sSort.addrSortIndex =
       sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
@@ -6042,9 +6035,9 @@ int sqlite3Select(
   if( p->selFlags & SF_Distinct ){
     sDistinct.tabTnct = pParse->nTab++;
     sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
-                             sDistinct.tabTnct, 0, 0,
-                             (char*)keyInfoFromExprList(pParse, p->pEList,0,0),
-                             P4_KEYINFO);
+                       sDistinct.tabTnct, 0, 0,
+                       (char*)sqlite3KeyInfoFromExprList(pParse, p->pEList,0,0),
+                       P4_KEYINFO);
     sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
     sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED;
   }else{
@@ -6052,22 +6045,15 @@ int sqlite3Select(
   }
 
   if( !isAgg && pGroupBy==0 ){
-    Window *pMWin = p->pWin;      /* Master window object (or NULL) */
-    int regPart = 0;
+    Window *pWin = p->pWin;      /* Master window object (or NULL) */
 
     /* No aggregate functions and no GROUP BY clause */
     u16 wctrlFlags = (sDistinct.isTnct ? WHERE_WANT_DISTINCT : 0);
     assert( WHERE_USE_LIMIT==SF_FixedLimit );
     wctrlFlags |= p->selFlags & SF_FixedLimit;
 
-    if( pMWin ){
-      int nPart = (pMWin->pPartition ? pMWin->pPartition->nExpr : 0);
-      nPart += (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0);
-      if( nPart ){
-        regPart = pParse->nMem+1;
-        pParse->nMem += nPart;
-        sqlite3VdbeAddOp3(v, OP_Null, 0, regPart, regPart+nPart-1);
-      }
+    if( pWin ){
+      sqlite3WindowCodeInit(pParse, pWin);
     }
 
     /* Begin the database scan. */
@@ -6098,112 +6084,18 @@ int sqlite3Select(
     }
 
     assert( p->pEList==pEList );
-    if( pMWin ){
-      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 regGosub = regRowid+1;
+    if( pWin ){
+      int addrGosub = sqlite3VdbeMakeLabel(v);
+      int regGosub = ++pParse->nMem;
       int addr;
-      int addrGosub;
-
-      pParse->nMem += nSub + 3;
-
-      /* 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( 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 = keyInfoFromExprList(pParse, pPart, 0, 0);
-          addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, 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 = regPart + nPart;
-
-          KeyInfo *pKeyInfo = keyInfoFromExprList(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);
-        }
-
-        addrGosub = sqlite3VdbeAddOp1(v, OP_Gosub, regGosub);
-        sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
-        sqlite3VdbeAddOp3(
-            v, OP_Copy, reg+pMWin->nBufferCol, 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, sqlite3VdbeCurrentAddr(v)+2);
+      sqlite3WindowCodeStep(pParse, p, pWInfo, regGosub, addrGosub);
 
       sqlite3VdbeAddOp0(v, OP_Goto);
-      if( regPart ){
-        sqlite3VdbeJumpHere(v, addrGosub);
-      }
-      addr = sqlite3VdbeAddOp1(v, OP_Rewind, pMWin->iEphCsr);
+      sqlite3VdbeResolveLabel(v, addrGosub);
+      addr = sqlite3VdbeAddOp1(v, OP_Rewind, pWin->iEphCsr);
       selectInnerLoop(pParse, p, -1, &sSort, &sDistinct, pDest, addr+1, 0);
-      sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, addr+1);
+      sqlite3VdbeAddOp2(v, OP_Next, pWin->iEphCsr, addr+1);
       sqlite3VdbeJumpHere(v, addr);
       sqlite3VdbeAddOp1(v, OP_Return, regGosub);
       sqlite3VdbeJumpHere(v, addr-1);       /* OP_Goto jumps here */
@@ -6346,7 +6238,7 @@ int sqlite3Select(
       ** will be converted into a Noop.  
       */
       sAggInfo.sortingIdx = pParse->nTab++;
-      pKeyInfo = keyInfoFromExprList(pParse, pGroupBy, 0, sAggInfo.nColumn);
+      pKeyInfo = sqlite3KeyInfoFromExprList(pParse,pGroupBy,0,sAggInfo.nColumn);
       addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen, 
           sAggInfo.sortingIdx, sAggInfo.nSortingColumn, 
           0, (char*)pKeyInfo, P4_KEYINFO);
index 7f3d37ba483005ee160bfb02d0873786de64eff2..45e4934c2e205b9b789617c2557a7237b7c258ee 100644 (file)
@@ -3485,6 +3485,7 @@ struct Window {
   FuncDef *pFunc;
   int nArg;
 
+  int regPart;
   Expr *pOwner;           /* Expression object this window is attached to */
   int nBufferCol;         /* Number of columns in buffer table */
   int iArgCol;            /* Offset of first argument for this function */
@@ -3494,6 +3495,8 @@ void sqlite3WindowDelete(sqlite3*, Window*);
 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);
 
 /*
 ** Assuming zIn points to the first byte of a UTF-8 character,
@@ -4202,6 +4205,8 @@ KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int,int);
 void sqlite3KeyInfoUnref(KeyInfo*);
 KeyInfo *sqlite3KeyInfoRef(KeyInfo*);
 KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*);
+KeyInfo *sqlite3KeyInfoFromExprList(Parse*, ExprList*, int, int);
+
 #ifdef SQLITE_DEBUG
 int sqlite3KeyInfoIsWriteable(KeyInfo*);
 #endif
index d2d6f476688cf4bd06d13d17dc3c321ec4ec7d29..5b1a9bd424c34e9da704856a93505c586c09c83a 100644 (file)
@@ -66,4 +66,216 @@ int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2){
   return 0;
 }
 
+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);
+  }
+}
+
+/*
+** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
+**
+**   ...
+**     if( new partition ){
+**       AggFinal (xFinalize)
+**       Gosub addrGosub
+**       ResetSorter eph-table
+**     }
+**     else if( new peer ){
+**       AggFinal (xValue)
+**       Gosub addrGosub
+**       ResetSorter eph-table
+**     }
+**     AggStep
+**     Insert (record into eph-table)
+**   sqlite3WhereEnd()
+**   AggFinal (xFinalize)
+**   Gosub addrGosub
+**
+** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
+**
+**   As above, except take no action for a "new peer". Invoke
+**   the sub-routine once only for each partition.
+**
+** RANGE BETWEEN CURRENT ROW AND CURRENT ROW
+**
+**   As above, except that the "new peer" condition is handled in the
+**   same way as "new partition" (so there is no "else if" block).
+**
+** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+**
+**   One way is to just reverse the sort order and do as for BETWEEN 
+**   UNBOUNDED PRECEDING AND CURRENT ROW. But that is not quite the same for
+**   things like group_concat(). And perhaps other user defined aggregates 
+**   as well.
+**
+**   ...
+**     if( new partition ){
+**       Gosub flush_partition;
+**       ResetSorter eph-table
+**     }
+**     AggStep
+**     Insert (record into eph-table)
+**   sqlite3WhereEnd()
+**   Gosub flush_partition
+**
+**  flush_partition:
+**   OpenDup (csr -> csr2)
+**   foreach (record in eph-table) {
+**     if( new peer ){
+**       while( csr2!=csr ){
+**         AggStep (xInverse)
+**         Next (csr2)
+**       }
+**     }
+**     AggFinal (xValue)
+**     Gosub addrGosub
+**   }
+**
+**========================================================================
+**
+** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
+**   ...
+**     if( new partition ){
+**       AggFinal (xFinalize)
+**     }
+**     AggStep
+**     AggFinal (xValue)
+**     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 UNBOUNDED PRECEDING AND <expr> PRECEDING
+** ROWS BETWEEN <expr> PRECEDING    AND <expr> PRECEDING
+** ROWS BETWEEN <expr> PRECEDING    AND CURRENT ROW
+** ROWS BETWEEN UNBOUNDED PRECEDING AND <expr> FOLLOWING
+** ROWS BETWEEN <expr> PRECEDING    AND <expr> FOLLOWING
+** ROWS BETWEEN CURRENT ROW         AND <expr> FOLLOWING
+** ROWS BETWEEN <expr> FOLLOWING    AND <expr> FOLLOWING
+** ROWS BETWEEN <expr> PRECEDING    AND UNBOUNDED FOLLOWING
+** ROWS BETWEEN <expr> FOLLOWING    AND UNBOUNDED FOLLOWING
+**
+**   Cases that involve <expr> PRECEDING or <expr> FOLLOWING.
+**
+**   ...
+**     Insert (record in eph-table)
+**   sqlite3WhereEnd()
+**
+*/
+void sqlite3WindowCodeStep(
+  Parse *pParse, 
+  Select *p,
+  WhereInfo *pWInfo,
+  int regGosub, 
+  int addrGosub
+){
+  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);
+  }
+
+  /* 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);
+}
+
 
index 9c567f24e3e379969f532765269a8c5395fdfe9b..b81c3ae1e12d0738dcb06c9d00e3b41eb8caadc4 100644 (file)
@@ -39,13 +39,18 @@ proc execsql {sql} {
   }
   #puts $lSql
 
-  set ret [list]
+  set ret ""
   foreach stmt $lSql {
     set res [pg_exec $::db $stmt]
     set err [pg_result $res -error]
     if {$err!=""} { error $err }
     for {set i 0} {$i < [pg_result $res -numTuples]} {incr i} {
-      lappend ret {*}[pg_result $res -getTuple $i]
+      if {$i==0} {
+        set ret [pg_result $res -getTuple 0]
+      } else {
+        append ret "   [pg_result $res -getTuple $i]"
+      }
+      # lappend ret {*}[pg_result $res -getTuple $i]
     }
     pg_result $res -clear
   }
@@ -89,6 +94,15 @@ puts $::fd [string trimleft "
   puts $::fd ""
 }
 
+proc -- {args} {
+  puts $::fd "# $args"
+}
+
+proc ========== {args} {
+  puts $::fd "#[string repeat = 74]"
+  puts $::fd ""
+}
+
 proc finish_test {} {
   puts $::fd finish_test
   close $::fd
@@ -122,6 +136,30 @@ 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
+  ) FROM t1
+}
+
+execsql_test 2.2 {
+  SELECT a, sum(d) OVER (
+    ORDER BY b
+    RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+  ) FROM t1
+}
+
+execsql_test 2.3 {
+  SELECT a, sum(d) OVER (
+    ORDER BY d
+    ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
+  ) FROM t1
+}
+
 finish_test
 
 
index 02b47aee71f29463f9a4ce3fc8b7079c0f0ade9f..9db7ee0baf969334effba60c63f9df4625a2758a 100644 (file)
@@ -32,14 +32,38 @@ do_execsql_test 1.0 {
 
 do_execsql_test 1.1 {
   SELECT c, sum(d) OVER (PARTITION BY b ORDER BY c) FROM t1;
-} {four 4 six 10 two 12 five 5 one 6 three 9}
+} {four 4   six 10   two 12   five 5   one 6   three 9}
 
 do_execsql_test 1.2 {
   SELECT sum(d) OVER () FROM t1;
-} {21 21 21 21 21 21}
+} {21   21   21   21   21   21}
 
 do_execsql_test 1.3 {
   SELECT sum(d) OVER (PARTITION BY b) FROM t1;
-} {12 12 12 9 9 9}
+} {12   12   12   9   9   9}
+
+finish_test
+#==========================================================================
+
+do_execsql_test 2.1 {
+  SELECT a, sum(d) OVER (
+    PARTITION BY b ORDER BY d
+    RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+  ) FROM t1
+} {2 12   4 10   6 6   1 9   3 8   5 5}
+
+do_execsql_test 2.2 {
+  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}
+
+do_execsql_test 2.3 {
+  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}
 
 finish_test