diff --git a/manifest b/manifest index 1aba9518db..2a46b037e7 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index d49ce87d1b..dcc39ad3ae 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e8eee566dfca6f4c8af074731dfe91f7fbcd9ca72f0303235b52e4e2e80d5b71 \ No newline at end of file +45cbd3b4498cea8856f189e9d0a192556d4f15212055b8328a1beca6083fc47a \ No newline at end of file diff --git a/src/window.c b/src/window.c index e139bfafcf..33831f87c6 100644 --- a/src/window.c +++ b/src/window.c @@ -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 PRECEDING AND 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 = // FOLLOWING expression +** regStart = // 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 PRECEDING */ int regEnd; /* Value of 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; kpStart, 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);