]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Allow an entire partition to be cached in a temp table for all types of window
authordan <dan@noemail.net>
Fri, 1 Jun 2018 21:00:08 +0000 (21:00 +0000)
committerdan <dan@noemail.net>
Fri, 1 Jun 2018 21:00:08 +0000 (21:00 +0000)
frames. This is required by nth_value() and others.

FossilOrigin-Name: b5b18f661341d8d450147e62d321791c706f16c0550bcd98eec3e0220c039189

manifest
manifest.uuid
src/window.c

index e6173ed2cebb5f9c2bbefd2824960b2e12204a7e..34429f047a5fad35b8687540b4a36b2b1f413760 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Allow\smin()\sand\smax()\sto\sbe\sused\sas\swindow\sfunctions.
-D 2018-05-30T20:44:58.014
+C Allow\san\sentire\spartition\sto\sbe\scached\sin\sa\stemp\stable\sfor\sall\stypes\sof\swindow\nframes.\sThis\sis\srequired\sby\snth_value()\sand\sothers.
+D 2018-06-01T21:00:08.284
 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 1f2b1590db468bde0ae2c53fe8b89f7a47afd8fd66a65a1385ffe6ce6f292279
+F src/window.c 59f519e2090a930b73f0789e44e481bf5a27d256d22ed74dee100ea14ad3bb02
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
 F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
@@ -1736,7 +1736,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 e74c6e91859ec395c12ba2742542ff176a1d8364dcfb66b862240746bef54efc
-R c4e13ad00b1be186d1a741c62965ae01
+P c16125a884a9131b707ac20033968c4c3177ea79625a15efb64d754568c6c7a0
+R 3ad4cd9edcd57b310020dff4b3e91b5b
 U dan
-Z 73f6b911c1a6e43e389c2921969fdc8b
+Z 0764619e321410e8611ab0fa83a4bd79
index adf1a6034dc3d199e0549248ee556400dc133c3b..b17b1d112372a9477fdb989c4e14848db3fc2277 100644 (file)
@@ -1 +1 @@
-c16125a884a9131b707ac20033968c4c3177ea79625a15efb64d754568c6c7a0
\ No newline at end of file
+b5b18f661341d8d450147e62d321791c706f16c0550bcd98eec3e0220c039189
\ No newline at end of file
index 2745a0d07739abda0daf18070e5282ede5798cde..69efa05330dee9c0f3bb9cc6c437fbbf36f40254 100644 (file)
@@ -193,6 +193,59 @@ static void windowAggFinal(Parse *pParse, Window *pMWin, int bFinal){
   }
 }
 
+static void windowPartitionCache(
+  Parse *pParse,
+  Select *p,
+  WhereInfo *pWInfo,
+  int regFlushPart,
+  int lblFlushPart
+){
+  Window *pMWin = p->pWin;
+  Vdbe *v = sqlite3GetVdbe(pParse);
+  Window *pWin;
+  int iSubCsr = p->pSrc->a[0].iCursor;
+  int nSub = p->pSrc->a[0].pTab->nCol;
+  int k;
+
+  int reg = pParse->nMem+1;
+  int regRecord = reg+nSub;
+  int regRowid = regRecord+1;
+
+  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);
+  }
+  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 ){
+    int addr;
+    ExprList *pPart = pMWin->pPartition;
+    int nPart = (pPart ? pPart->nExpr : 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);
+    sqlite3VdbeAddOp3(v, OP_Jump, addr+2, addr+4, addr+2);
+    sqlite3VdbeAddOp3(v, OP_Copy, regNewPart, pMWin->regPart, nPart-1);
+    sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart);
+  }
+
+  /* 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, lblFlushPart);
+}
 
 /*
 ** ROWS BETWEEN <expr1> PRECEDING AND <expr2> FOLLOWING
@@ -329,15 +382,13 @@ static void windowCodeRowExprStep(
   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 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 regArg;
+  int nArg;
   int addr;
   int csrStart = pParse->nTab++;
   int csrEnd = pParse->nTab++;
@@ -373,8 +424,6 @@ static void windowCodeRowExprStep(
     bRange = 1;
   }
 
-  pParse->nMem += nSub + 2;
-
   /* Allocate register and label for the "flush_partition" sub-routine. */
   regFlushPart = ++pParse->nMem;
   lblFlushPart = sqlite3VdbeMakeLabel(v);
@@ -383,37 +432,8 @@ static void windowCodeRowExprStep(
   regStart = ++pParse->nMem;
   regEnd = ++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);
+  windowPartitionCache(pParse, p, pWInfo, regFlushPart, lblFlushPart);
 
-  /* 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 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);
-    sqlite3VdbeAddOp3(v, OP_Jump, addr+2, addr+4, addr+2);
-    sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart);
-    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, lblFlushPart);
   addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
 
   /* Start of "flush_partition" */
@@ -443,9 +463,13 @@ static void windowCodeRowExprStep(
   }
 
   /* Initialize the accumulator register for each window function to NULL */
+  nArg = 0;
   for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
     sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum);
+    nArg = MAX(nArg, pWin->nArg);
   }
+  regArg = pParse->nMem+1;
+  pParse->nMem += nArg;
 
   sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, lblFlushDone);
   sqlite3VdbeAddOp2(v, OP_Rewind, csrStart, lblFlushDone);
@@ -462,7 +486,7 @@ static void windowCodeRowExprStep(
   }
   sqlite3VdbeAddOp2(v, OP_Next, csrEnd, sqlite3VdbeCurrentAddr(v)+2);
   addr = sqlite3VdbeAddOp0(v, OP_Goto);
-  windowAggStep(pParse, pMWin, csrEnd, 0, reg);
+  windowAggStep(pParse, pMWin, csrEnd, 0, regArg);
   if( pMWin->eEnd==TK_UNBOUNDED ){
     sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop);
     sqlite3VdbeJumpHere(v, addr);
@@ -528,7 +552,7 @@ static void windowCodeRowExprStep(
       addrJumpHere = sqlite3VdbeAddOp0(v, OP_Goto);
     }
     sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+1);
-    windowAggStep(pParse, pMWin, csrStart, 1, reg);
+    windowAggStep(pParse, pMWin, csrStart, 1, regArg);
     if( bRange ){
       sqlite3VdbeAddOp2(v, OP_Goto, 0, addrJumpHere-1);
     }
@@ -550,6 +574,146 @@ static void windowCodeRowExprStep(
   sqlite3VdbeJumpHere(v, addrGoto);
 }
 
+/*
+** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
+**
+**   flush_partition:
+**     Once {
+**       OpenDup (iEphCsr -> csrLead)
+**     }
+**     Integer ctr 0
+**     foreach row (csrLead){
+**       if( new peer ){
+**         AggFinal (xValue)
+**         for(i=0; i<ctr; i++){
+**           Gosub addrGosub
+**           Next iEphCsr
+**         }
+**         Integer ctr 0
+**       }
+**       AggStep (csrLead)
+**       Incr ctr
+**     }
+**
+**     AggFinal (xFinalize)
+**     for(i=0; i<ctr; i++){
+**       Gosub addrGosub
+**       Next iEphCsr
+**     }
+**
+**     ResetSorter (csr)
+**     Return
+**
+** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
+** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
+** RANGE BETWEEN CURRENT ROW AND CURRENT ROW 
+**
+**   TODO.
+*/
+static void windowCodeCacheStep(
+  Parse *pParse, 
+  Select *p,
+  WhereInfo *pWInfo,
+  int regGosub, 
+  int addrGosub
+){
+  Window *pMWin = p->pWin;
+  Vdbe *v = sqlite3GetVdbe(pParse);
+  Window *pWin;
+  int k;
+  int addr;
+  ExprList *pPart = pMWin->pPartition;
+  ExprList *pOrderBy = pMWin->pOrderBy;
+  int nPeer = pOrderBy->nExpr;
+  int regNewPeer;
+
+  int addrGoto;                   /* Address of Goto used to jump flush_par.. */
+  int addrRewind;                 /* Address of Rewind that starts loop */
+  int regFlushPart;
+  int lblFlushPart;
+  int csrLead;
+  int regCtr;
+  int regArg;                     /* Register array to martial function args */
+  int nArg;
+
+  assert( (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT) 
+  );
+
+  regNewPeer = pParse->nMem+1;
+  pParse->nMem += nPeer;
+
+  /* Allocate register and label for the "flush_partition" sub-routine. */
+  regFlushPart = ++pParse->nMem;
+  lblFlushPart = sqlite3VdbeMakeLabel(v);
+
+  csrLead = pParse->nTab++;
+  regCtr = ++pParse->nMem;
+
+  windowPartitionCache(pParse, p, pWInfo, regFlushPart, lblFlushPart);
+  addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
+
+  /* Start of "flush_partition" */
+  sqlite3VdbeResolveLabel(v, lblFlushPart);
+  sqlite3VdbeAddOp2(v, OP_Once, 0, sqlite3VdbeCurrentAddr(v)+2);
+  sqlite3VdbeAddOp2(v, OP_OpenDup, csrLead, pMWin->iEphCsr);
+
+  /* Initialize the accumulator register for each window function to NULL */
+  nArg = 0;
+  for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
+    sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum);
+    nArg = MAX(nArg, pWin->nArg);
+  }
+  regArg = pParse->nMem+1;
+  pParse->nMem += nArg;
+
+  sqlite3VdbeAddOp2(v, OP_Integer, 0, regCtr);
+  addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, csrLead);
+  sqlite3VdbeAddOp1(v, OP_Rewind, pMWin->iEphCsr);
+
+  if( pOrderBy ){
+    int addrJump;                 /* Address of OP_Jump below */
+    int iOff = pMWin->nBufferCol + (pPart ? pPart->nExpr : 0);
+    int regPeer = pMWin->regPart + (pPart ? pPart->nExpr : 0);
+    KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0);
+
+    for(k=0; k<nPeer; k++){
+      sqlite3VdbeAddOp3(v, OP_Column, csrLead, iOff+k, regNewPeer+k);
+    }
+
+    addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer, nPeer);
+    sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
+    addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
+    sqlite3VdbeAddOp3(v, OP_Copy, regNewPeer, regPeer, nPeer-1);
+    windowAggFinal(pParse, pMWin, 0);
+    sqlite3VdbeAddOp3(v, OP_IfPos, regCtr, sqlite3VdbeCurrentAddr(v)+2 , 1);
+    sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+3);
+    sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
+    sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)-3);
+    sqlite3VdbeJumpHere(v, addrJump);
+  }
+
+  windowAggStep(pParse, pMWin, csrLead, 0, regArg);
+  sqlite3VdbeAddOp2(v, OP_AddImm, regCtr, 1);
+
+  sqlite3VdbeAddOp2(v, OP_Next, csrLead, addrRewind+2);
+
+  windowAggFinal(pParse, pMWin, 1);
+
+  sqlite3VdbeAddOp3(v, OP_IfPos, regCtr, sqlite3VdbeCurrentAddr(v)+2 , 1);
+  sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+3);
+  sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
+  sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)-3);
+
+  sqlite3VdbeJumpHere(v, addrRewind);
+  sqlite3VdbeJumpHere(v, addrRewind+1);
+  sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
+  sqlite3VdbeAddOp1(v, OP_Return, regFlushPart);
+
+  /* Jump to here to skip over flush_partition */
+  sqlite3VdbeJumpHere(v, addrGoto);
+}
+
+
 /*
 ** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
 **
@@ -569,6 +733,20 @@ static void windowCodeRowExprStep(
 **   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).
+**
+** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
+** 
+**   As above, except assume every row is a "new peer".
 */
 static void windowCodeDefaultStep(
   Parse *pParse, 
@@ -742,15 +920,26 @@ void sqlite3WindowCodeStep(
 ){
   Window *pMWin = p->pWin;
 
+  *pbLoop = 0;
   if( (pMWin->eType==TK_ROWS 
    && (pMWin->eStart!=TK_UNBOUNDED||pMWin->eEnd!=TK_CURRENT||!pMWin->pOrderBy))
    || (pMWin->eStart==TK_CURRENT&&pMWin->eEnd==TK_UNBOUNDED&&pMWin->pOrderBy)
   ){
-    *pbLoop = 0;
     windowCodeRowExprStep(pParse, p, pWInfo, regGosub, addrGosub);
     return;
   }
 
+#if 0
+  if( pMWin->eType==TK_RANGE
+   && pMWin->eStart==TK_UNBOUNDED
+   && pMWin->eEnd==TK_CURRENT
+   && pMWin->pOrderBy
+  ){
+    windowCodeCacheStep(pParse, p, pWInfo, regGosub, addrGosub);
+    return;
+  }
+#endif
+
   *pbLoop = 1;
   windowCodeDefaultStep(pParse, p, pWInfo, regGosub, addrGosub);
 }