]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Simplify the window function code generator some more.
authordan <dan@noemail.net>
Wed, 6 Mar 2019 21:04:11 +0000 (21:04 +0000)
committerdan <dan@noemail.net>
Wed, 6 Mar 2019 21:04:11 +0000 (21:04 +0000)
FossilOrigin-Name: 45cbd3b4498cea8856f189e9d0a192556d4f15212055b8328a1beca6083fc47a

manifest
manifest.uuid
src/window.c

index 1aba9518db2a7354f5776784f678fe0ec56f0ab9..2a46b037e71065ae38b87ea9f11fc3a072fe030a 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Improvements\sto\sthe\sway\sbuilt-in\swindow\sfunctions\sare\shandled.
-D 2019-03-06T17:12:32.566
+C Simplify\sthe\swindow\sfunction\scode\sgenerator\ssome\smore.
+D 2019-03-06T21:04:11.725
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F Makefile.in 1ad7263f38329c0ecea543c80f30af839ee714ea77fc391bf1a3fbb919a5b6b5
@@ -604,7 +604,7 @@ F src/where.c 8a207cb2ca6b99e1edb1e4bbff9b0504385a759cbf66180d1deb34d80ca4b799
 F src/whereInt.h 5f14db426ca46a83eabab1ae9aa6d4b8f27504ad35b64c290916289b1ddb2e88
 F src/wherecode.c ce7b21e1be2b981d62683fc59c4ca73a04a7ff2f1ebec23d41baf2da2349afd6
 F src/whereexpr.c 36b47f7261d6b6f1a72d774c113b74beddf6745aba1018e64b196e29db233442
-F src/window.c 607664ea225c3a9c2a7f55795741444bd6a95be37d1681ecf962cd5cb1618dba
+F src/window.c 06257715857cb9c6fe51b5a6f9558701821d431b85313d27e1b96d1a81e1f8eb
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
 F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
@@ -1809,7 +1809,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 af0ea1363548461b2aad8fd54ee3f2f616111dcae2d6480f5294da44c87a0a5d
-R 43bbe25e5bcdc3ad86ad389fa7d11dcc
+P e8eee566dfca6f4c8af074731dfe91f7fbcd9ca72f0303235b52e4e2e80d5b71
+R 10fe6da0769422f36eeeb42d0ed4992d
 U dan
-Z 3a11921edb0dd283221790b71f6938eb
+Z 42de440ac1d651cb610f16495ca5b34a
index d49ce87d1bd02abe0461e9047fa673ce4094b452..dcc39ad3ae8167817dadf663c0ea0254d17c5b64 100644 (file)
@@ -1 +1 @@
-e8eee566dfca6f4c8af074731dfe91f7fbcd9ca72f0303235b52e4e2e80d5b71
\ No newline at end of file
+45cbd3b4498cea8856f189e9d0a192556d4f15212055b8328a1beca6083fc47a
\ No newline at end of file
index e139bfafcf1b3404c868d48e6e70d26927adcaa7..33831f87c62c1fc8423745ae29d84b8e9cd1ac86 100644 (file)
@@ -1844,8 +1844,8 @@ static void windowCodeRowExprStep(
 }
 
 /* 
-** Return true if the entire partition should be cached in the temp
-** table before processing.
+** Return true if the entire partition should be cached in the ephemeral
+** table before processing any rows.
 */
 static int windowCachePartition(Window *pMWin){
   Window *pWin;
@@ -1863,6 +1863,135 @@ static int windowCachePartition(Window *pMWin){
   return 0;
 }
 
+typedef struct WindowCodeArg WindowCodeArg;
+struct WindowCodeArg {
+  Parse *pParse;
+  Window *pMWin;
+  Vdbe *pVdbe;
+  int regGosub;
+  int addrGosub;
+  int regArg;
+};
+
+#define WINDOW_RETURN_ROW 1
+#define WINDOW_AGGINVERSE 2
+#define WINDOW_AGGSTEP    3
+
+static int windowCodeOp(
+ WindowCodeArg *p,
+ int op,
+ int csr,
+ int regCountdown,
+ int jumpOnEof
+){
+  int ret = 0;
+  Vdbe *v = p->pVdbe;
+  int addrIf = 0; 
+
+  if( regCountdown>0 ){
+    addrIf = sqlite3VdbeAddOp3(v, OP_IfPos, regCountdown, 0, 1);
+  }
+
+  if( jumpOnEof ){
+    sqlite3VdbeAddOp2(v, OP_Next, csr, sqlite3VdbeCurrentAddr(v)+2);
+    ret = sqlite3VdbeAddOp0(v, OP_Goto);
+  }else{
+    sqlite3VdbeAddOp2(v, OP_Next, csr, sqlite3VdbeCurrentAddr(v)+1);
+  }
+
+  switch( op ){
+    case WINDOW_RETURN_ROW:
+      windowAggFinal(p->pParse, p->pMWin, 0);
+      windowReturnOneRow(p->pParse, p->pMWin, p->regGosub, p->addrGosub);
+      break;
+
+    case WINDOW_AGGINVERSE:
+      windowAggStep(p->pParse, p->pMWin, csr, 1, p->regArg, p->pMWin->regSize);
+      break;
+
+    case WINDOW_AGGSTEP:
+      windowAggStep(p->pParse, p->pMWin, csr, 0, p->regArg, p->pMWin->regSize);
+      break;
+  }
+
+  if( ret ){
+    sqlite3VdbeJumpHere(v, ret);
+  }
+  if( regCountdown>0 ){
+    sqlite3VdbeJumpHere(v, addrIf);
+  }
+  return ret;
+}
+
+
+
+/*
+** This function - windowCodeStep() - generates the VM code that reads data
+** from the sub-select and returns rows to the consumer. For the simplest
+** case:
+**
+**     ROWS BETWEEN <expr1> PRECEDING AND <expr2> FOLLOWING
+**
+** The VM code generated is equivalent in spirit to the following:
+**
+**     while( !eof ){
+**       if( new partition ){
+**         Gosub flush
+**       }    
+**       Insert new row into eph table.
+**     
+**       if( first row of partition ){
+**         Rewind(csrEnd, skipNext=1)
+**         Rewind(csrStart, skipNext=1)
+**         Rewind(csrCurrent, skipNext=1)
+**     
+**         regEnd = <expr2>          // FOLLOWING expression
+**         regStart = <expr1>        // PRECEDING expression
+**       }else{
+**         if( (regEnd--)<=0 ){
+**           Next(csrCurrent)
+**           Return one row.
+**           if( (regStart--)<0 ){
+**             Next(csrStart)
+**             AggInverse(csrStart)
+**           }
+**         }
+**       }    
+**     
+**       Next(csrEnd)
+**       AggStep(csrEnd)
+**     }    
+**     flush:
+**       while( 1 ){ 
+**         Next(csrCurrent)
+**         if( eof ) break
+**         Return one row.
+**         if( (regStart--)<0 ){
+**           Next(csrStart)
+**           AggInverse(csrStart)
+**         }
+**       }    
+**       Empty eph table.
+**
+** More generally, the pattern used for all window types is:
+**
+**     while( !eof ){
+**       if( new partition ){
+**         Gosub flush
+**       }    
+**       Insert new row into eph table.
+**       if( first row of partition ){
+**         FIRST_ROW_CODE
+**       }else{
+**         SECOND_ROW_CODE
+**       }    
+**       ALL_ROW_CODE
+**     }    
+**     flush:
+**       FLUSH_CODE
+**       Empty eph table.
+**
+*/
 static void windowCodeStep(
   Parse *pParse, 
   Select *p,
@@ -1883,14 +2012,12 @@ static void windowCodeStep(
   int regStart;                    /* Value of <expr> PRECEDING */
   int regEnd;                      /* Value of <expr> FOLLOWING */
 
-  int iSubCsr = p->pSrc->a[0].iCursor;
-  int nSub = p->pSrc->a[0].pTab->nCol;
-  int k;
+  int iSubCsr = p->pSrc->a[0].iCursor;      /* Cursor of sub-select */
+  int nSub = p->pSrc->a[0].pTab->nCol;      /* Number of cols returned by sub */
+  int iCol;                                 /* To iterate through sub cols */
 
   int addrGoto;
   int addrIf;
-  int addrIfEnd;
-  int addrIfStart;
   int addrGosubFlush;
   int addrInteger;
   int addrCacheRewind;
@@ -1903,8 +2030,14 @@ static void windowCodeStep(
   int reg = pParse->nMem+1;
   int regRecord = reg+nSub;
   int regRowid = regRecord+1;
+  WindowCodeArg s;
 
-  bCache = 1;
+  memset(&s, 0, sizeof(WindowCodeArg));
+  s.pParse = pParse;
+  s.pMWin = pMWin;
+  s.pVdbe = v;
+  s.regGosub = regGosub;
+  s.addrGosub = addrGosub;
 
   pParse->nMem += 1 + nSub + 1;
 
@@ -1923,11 +2056,12 @@ static void windowCodeStep(
        || pMWin->eEnd==TK_PRECEDING 
   );
 
+
   /* Load the column values for the row returned by the sub-select
   ** into an array of registers starting at reg. Assemble them into
   ** a record in register regRecord. TODO: An optimization here? */
-  for(k=0; k<nSub; k++){
-    sqlite3VdbeAddOp3(v, OP_Column, iSubCsr, k, reg+k);
+  for(iCol=0; iCol<nSub; iCol++){
+    sqlite3VdbeAddOp3(v, OP_Column, iSubCsr, iCol, reg+iCol);
   }
   sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, nSub, regRecord);
 
@@ -1973,14 +2107,16 @@ static void windowCodeStep(
   }
 
   /* This block is run for the first row of each partition */
-  regArg = windowInitAccum(pParse, pMWin);
+  s.regArg = regArg = windowInitAccum(pParse, pMWin);
 
   sqlite3ExprCode(pParse, pMWin->pStart, regStart);
   windowCheckIntValue(pParse, regStart, 0);
   sqlite3ExprCode(pParse, pMWin->pEnd, regEnd);
   windowCheckIntValue(pParse, regEnd, 1);
 
-  if( pMWin->eStart==TK_FOLLOWING || pMWin->eEnd==TK_PRECEDING ){
+  if( pMWin->eStart==pMWin->eEnd 
+   && pMWin->eStart!=TK_CURRENT && pMWin->eStart!=TK_UNBOUNDED 
+  ){
     int op = ((pMWin->eStart==TK_FOLLOWING) ? OP_Ge : OP_Le);
     int addrGe = sqlite3VdbeAddOp3(v, op, regStart, 0, regEnd);
     windowAggFinal(pParse, pMWin, 0);
@@ -2008,56 +2144,35 @@ static void windowCodeStep(
   sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regFirst);
   addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
 
-  /* This block is run for the second and subsequent rows of each partition */
+  /* Begin generating SECOND_ROW_CODE */
+  VdbeModuleComment((pParse->pVdbe, "Begin windowCodeStep.SECOND_ROW_CODE"));
   if( bCache ){
     addrCacheNext = sqlite3VdbeCurrentAddr(v);
   }else{
     sqlite3VdbeJumpHere(v, addrIf);
   }
-
   if( pMWin->eStart==TK_FOLLOWING ){
-    addrIfEnd = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0, 1);
-    windowAggFinal(pParse, pMWin, 0);
-    sqlite3VdbeAddOp2(v, OP_Next, csrCurrent, sqlite3VdbeCurrentAddr(v)+1);
-    windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
-    sqlite3VdbeJumpHere(v, addrIfEnd);
-
-    addrIfStart = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0, 1);
-    sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+1);
-    windowAggStep(pParse, pMWin, csrStart, 1, regArg, pMWin->regSize);
-    sqlite3VdbeJumpHere(v, addrIfStart);
+    windowCodeOp(&s, WINDOW_RETURN_ROW, csrCurrent, regEnd, 0);
+    windowCodeOp(&s, WINDOW_AGGINVERSE, csrStart, regStart, 0);
   }else
   if( pMWin->eEnd==TK_PRECEDING ){
-    addrIfEnd = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0, 1);
-    sqlite3VdbeAddOp2(v, OP_Next, csrEnd, sqlite3VdbeCurrentAddr(v)+1);
-    windowAggStep(pParse, pMWin, csrEnd, 0, regArg, pMWin->regSize);
-    sqlite3VdbeJumpHere(v, addrIfEnd);
-
-    windowAggFinal(pParse, pMWin, 0);
-    sqlite3VdbeAddOp2(v, OP_Next, csrCurrent, sqlite3VdbeCurrentAddr(v)+1);
-    windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
-
-    addrIfStart = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0, 1);
-    sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+1);
-    windowAggStep(pParse, pMWin, csrStart, 1, regArg, pMWin->regSize);
-    sqlite3VdbeJumpHere(v, addrIfStart);
+    windowCodeOp(&s, WINDOW_AGGSTEP, csrEnd, regEnd, 0);
+    windowCodeOp(&s, WINDOW_RETURN_ROW, csrCurrent, 0, 0);
+    windowCodeOp(&s, WINDOW_AGGINVERSE, csrStart, regStart, 0);
   }else{
-    addrIfEnd = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0, 1);
-    windowAggFinal(pParse, pMWin, 0);
-    sqlite3VdbeAddOp2(v, OP_Next, csrCurrent, sqlite3VdbeCurrentAddr(v)+1);
-    windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
-    addrIfStart = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0, 1);
-    sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+1);
-    windowAggStep(pParse, pMWin, csrStart, 1, regArg, pMWin->regSize);
-    sqlite3VdbeJumpHere(v, addrIfStart);
-    sqlite3VdbeJumpHere(v, addrIfEnd);
+    int addr = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0, 1);
+    windowCodeOp(&s, WINDOW_RETURN_ROW, csrCurrent, 0, 0);
+    windowCodeOp(&s, WINDOW_AGGINVERSE, csrStart, regStart, 0);
+    sqlite3VdbeJumpHere(v, addr);
   }
+  VdbeModuleComment((pParse->pVdbe, "End windowCodeStep.SECOND_ROW_CODE"));
 
+  VdbeModuleComment((pParse->pVdbe, "Begin windowCodeStep.ALL_ROW_CODE"));
   sqlite3VdbeJumpHere(v, addrGoto);
   if( pMWin->eEnd!=TK_PRECEDING ){
-    sqlite3VdbeAddOp2(v, OP_Next, csrEnd, sqlite3VdbeCurrentAddr(v)+1);
-    windowAggStep(pParse, pMWin, csrEnd, 0, regArg, pMWin->regSize);
+    windowCodeOp(&s, WINDOW_AGGSTEP, csrEnd, 0, 0);
   }
+  VdbeModuleComment((pParse->pVdbe, "End windowCodeStep.ALL_ROW_CODE"));
 
   /* End of the main input loop */
   if( bCache ){
@@ -2070,56 +2185,34 @@ static void windowCodeStep(
 
   /* Fall through */
 
+  VdbeModuleComment((pParse->pVdbe, "Begin windowCodeStep.FLUSH_CODE"));
   if( pMWin->pPartition && bCache==0 ){
     addrInteger = sqlite3VdbeAddOp2(v, OP_Integer, 0, regFlushPart);
     sqlite3VdbeJumpHere(v, addrGosubFlush);
   }
 
-  if( pMWin->eStart==TK_FOLLOWING ){
-    int addrBreak;
-    addrIfEnd = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0, 1);
-    sqlite3VdbeAddOp2(v, OP_Next, csrCurrent, sqlite3VdbeCurrentAddr(v)+2);
-    addrBreak = sqlite3VdbeAddOp0(v, OP_Goto);
-    windowAggFinal(pParse, pMWin, 0);
-    windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
-    sqlite3VdbeJumpHere(v, addrIfEnd);
-
-    addrIfStart = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0, 1);
-    sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+2);
-    sqlite3VdbeAddOp0(v, OP_Goto);
-    windowAggStep(pParse, pMWin, csrStart, 1, regArg, pMWin->regSize);
-    sqlite3VdbeJumpHere(v, addrIfStart);
-    sqlite3VdbeJumpHere(v, addrIfStart+2);
-
-    sqlite3VdbeAddOp2(v, OP_Goto, 0, addrIfEnd);
-    sqlite3VdbeJumpHere(v, addrBreak);
+  if( pMWin->eEnd==TK_PRECEDING ){
+    windowCodeOp(&s, WINDOW_AGGSTEP, csrEnd, regEnd, 1);
+    windowCodeOp(&s, WINDOW_RETURN_ROW, csrCurrent, 0, 1);
   }else{
-    sqlite3VdbeAddOp2(v, OP_Next, csrCurrent, sqlite3VdbeCurrentAddr(v)+2);
-    addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
-    if( pMWin->eEnd==TK_PRECEDING ){
-      addrIfEnd = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0, 1);
-      sqlite3VdbeAddOp2(v, OP_Next, csrEnd, sqlite3VdbeCurrentAddr(v)+1);
-      windowAggStep(pParse, pMWin, csrEnd, 0, regArg, pMWin->regSize);
-      sqlite3VdbeJumpHere(v, addrIfEnd);
-      windowAggFinal(pParse, pMWin, 0);
-      windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
+    int addrBreak;
+    int addrStart = sqlite3VdbeCurrentAddr(v);
+    if( pMWin->eStart==TK_FOLLOWING ){
+      addrBreak = windowCodeOp(&s, WINDOW_RETURN_ROW, csrCurrent, regEnd, 1);
+      windowCodeOp(&s, WINDOW_AGGINVERSE, csrStart, regStart, 1);
     }else{
-      windowAggFinal(pParse, pMWin, 0);
-      windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
-      addrIfStart = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0, 1);
-      sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+1);
-      windowAggStep(pParse, pMWin, csrStart, 1, regArg, pMWin->regSize);
-      sqlite3VdbeJumpHere(v, addrIfStart);
-      sqlite3VdbeAddOp2(v, OP_Goto, 0, addrGoto-1);
+      addrBreak = windowCodeOp(&s, WINDOW_RETURN_ROW, csrCurrent, 0, 1);
+      windowCodeOp(&s, WINDOW_AGGINVERSE, csrStart, regStart, 0);
     }
-    sqlite3VdbeJumpHere(v, addrGoto);
+    sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart);
+    sqlite3VdbeJumpHere(v, addrBreak);
   }
 
-
   if( bCache && addrShortcut>0 ) sqlite3VdbeJumpHere(v, addrShortcut);
   sqlite3VdbeAddOp1(v, OP_ResetSorter, csrCurrent);
   sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regSize);
   if( bCache==0 ) sqlite3VdbeAddOp2(v, OP_Integer, 1, pMWin->regFirst);
+  VdbeModuleComment((pParse->pVdbe, "End windowCodeStep.FLUSH_CODE"));
   if( pMWin->pPartition ){
     sqlite3VdbeChangeP1(v, addrInteger, sqlite3VdbeCurrentAddr(v));
     sqlite3VdbeAddOp1(v, OP_Return, regFlushPart);