From: dan Date: Tue, 12 Mar 2019 15:21:51 +0000 (+0000) Subject: Expand on header comment for sqlite3WindowCodeStep(). Further simplify the implementa... X-Git-Tag: version-3.28.0~88^2~22 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=935d9d8260e34e01873ac51988b23f017434a5f7;p=thirdparty%2Fsqlite.git Expand on header comment for sqlite3WindowCodeStep(). Further simplify the implementation of the same. FossilOrigin-Name: 5129bcc996b3c9f78ab6b674a4364787e7b353b90f15f027cad4431012022c30 --- diff --git a/manifest b/manifest index d2f4f74e20..4691460a47 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\s"cache\smode"\sfrom\sthe\swindow\sframe\scode\sgenerator.\sHandle\sthe\ssame\scases\sby\sediting\sthe\swindow\sframe\sspecification\sitself. -D 2019-03-11T19:50:54.722 +C Expand\son\sheader\scomment\sfor\ssqlite3WindowCodeStep().\sFurther\ssimplify\sthe\simplementation\sof\sthe\ssame. +D 2019-03-12T15:21:51.703 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 236d2739dc3e823c3c909bca2d6cef93009bafbefd7018a8f3281074ecb92954 @@ -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 f41e0b36e6c26aa8b858f488498aa8152d6441a09c0e96baa5979269e1f0d199 +F src/window.c 4763c2e81e8bb5d79fc378ba650cfcf076653d78065bd85790d592d441c9ae7a F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d @@ -1812,7 +1812,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 6bd1a07949ff3d394056bfcc813444401ef00806e3f0e0423ff6962541e84bdb -R 895a04edfc289d379068084b0a252ef5 +P 081263538332bb9c07e62630629007ccbba31bef5dc890f60b4ba58a355f70ac +R b359af1c195991083b8f00df3e4dfe08 U dan -Z ab8b6521d2f2f661581dab04391f3c9a +Z 98fef6b18eded687d4fe3a47cb60a7a7 diff --git a/manifest.uuid b/manifest.uuid index 86b00a9cb0..f926fc2d89 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -081263538332bb9c07e62630629007ccbba31bef5dc890f60b4ba58a355f70ac \ No newline at end of file +5129bcc996b3c9f78ab6b674a4364787e7b353b90f15f027cad4431012022c30 \ No newline at end of file diff --git a/src/window.c b/src/window.c index 094f6eaa37..c39b7b9b4d 100644 --- a/src/window.c +++ b/src/window.c @@ -1494,28 +1494,27 @@ static int windowCacheFrame(Window *pMWin){ ** regOld and control falls through. Otherwise, if the contents of the arrays ** are equal, an OP_Goto is executed. The address of the OP_Goto is returned. */ -static int windowIfNewPeer( +static void windowIfNewPeer( Parse *pParse, ExprList *pOrderBy, int regNew, /* First in array of new values */ - int regOld /* First in array of old values */ + int regOld, /* First in array of old values */ + int addr /* Jump here */ ){ Vdbe *v = sqlite3GetVdbe(pParse); - int addr; if( pOrderBy ){ int nVal = pOrderBy->nExpr; KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0); sqlite3VdbeAddOp3(v, OP_Compare, regOld, regNew, nVal); sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO); - addr = sqlite3VdbeAddOp3( - v, OP_Jump, sqlite3VdbeCurrentAddr(v)+1, 0, sqlite3VdbeCurrentAddr(v)+1 + sqlite3VdbeAddOp3(v, OP_Jump, + sqlite3VdbeCurrentAddr(v)+1, addr, sqlite3VdbeCurrentAddr(v)+1 ); VdbeCoverageEqNe(v); sqlite3VdbeAddOp3(v, OP_Copy, regNew, regOld, nVal-1); }else{ - addr = sqlite3VdbeAddOp0(v, OP_Goto); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); } - return addr; } typedef struct WindowCodeArg WindowCodeArg; @@ -1708,8 +1707,7 @@ static int windowCodeOp( int nReg = (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0); int regTmp = (nReg ? sqlite3GetTempRange(pParse, nReg) : 0); windowReadPeerValues(p, csr, regTmp); - addr = windowIfNewPeer(pParse, pMWin->pOrderBy, regTmp, reg); - sqlite3VdbeChangeP2(v, addr, addrContinue); + windowIfNewPeer(pParse, pMWin->pOrderBy, regTmp, reg, addrContinue); sqlite3ReleaseTempRange(pParse, regTmp, nReg); } @@ -1850,9 +1848,7 @@ Window *sqlite3WindowListDup(sqlite3 *db, Window *p){ ** } ** Insert new row into eph table. ** if( first row of partition ){ -** Rewind(csrEnd) -** Rewind(csrStart) -** Rewind(csrCurrent) +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) ** regEnd = ** regStart = ** }else{ @@ -1880,9 +1876,7 @@ Window *sqlite3WindowListDup(sqlite3 *db, Window *p){ ** } ** Insert new row into eph table. ** if( first row of partition ){ -** Rewind(csrEnd) -** Rewind(csrStart) -** Rewind(csrCurrent) +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) ** regEnd = ** regStart = regEnd - ** }else{ @@ -1926,9 +1920,7 @@ Window *sqlite3WindowListDup(sqlite3 *db, Window *p){ ** } ** Insert new row into eph table. ** if( first row of partition ){ -** Rewind(csrEnd) -** Rewind(csrStart) -** Rewind(csrCurrent) +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) ** regStart = ** }else{ ** AGGSTEP @@ -1946,6 +1938,162 @@ Window *sqlite3WindowListDup(sqlite3 *db, Window *p){ ** while( !eof csrCurrent ){ ** RETURN_ROW ** } +** +** Also requiring special handling are the cases: +** +** ROWS BETWEEN PRECEDING AND PRECEDING +** ROWS BETWEEN FOLLOWING AND FOLLOWING +** +** when (expr1 < expr2). This is detected at runtime, not by this function. +** To handle this case, the pseudo-code programs depicted above are modified +** slightly to be: +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** if( regEnd < regStart ){ +** RETURN_ROW +** delete eph table contents +** continue +** } +** ... +** +** The new "continue" statement in the above jumps to the next iteration +** of the outer loop - the one started by sqlite3WhereBegin(). +** +** The various GROUPS cases are implemented using the same patterns as +** ROWS. The VM code is modified slightly so that: +** +** 1. The else branch in the main loop is only taken if the row just +** added to the ephemeral table is the start of a new group. In +** other words, it becomes: +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** }else if( new group ){ +** ... +** } +** } +** +** 2. Instead of processing a single row, each RETURN_ROW, AGGSTEP or +** AGGINVERSE step processes the current row of the relevant cursor and +** all subsequent rows belonging to the same group. +** +** RANGE window frames are a little different again. As for GROUPS, the +** main loop runs once per group only. And RETURN_ROW, AGGSTEP and AGGINVERSE +** deal in groups instead of rows. As for ROWS and GROUPS, there are three +** basic cases: +** +** RANGE BETWEEN PRECEDING AND FOLLOWING +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** }else{ +** AGGSTEP +** while( (csrCurrent.key + regEnd) < csrEnd.key ){ +** RETURN_ROW +** while( csrStart.key + regStart) < csrCurrent.key ){ +** AGGINVERSE +** } +** } +** } +** } +** flush: +** AGGSTEP +** while( 1 ){ +** RETURN ROW +** if( csrCurrent is EOF ) break; +** while( csrStart.key + regStart) < csrCurrent.key ){ +** AGGINVERSE +** } +** } +** } +** +** In the above notation, "csr.key" means the current value of the ORDER BY +** expression (there is only ever 1 for a RANGE that uses an FOLLOWING +** or PRECEDING AND PRECEDING +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** }else{ +** while( (csrEnd.key + regEnd) <= csrCurrent.key ){ +** AGGSTEP +** } +** RETURN_ROW +** while( (csrStart.key + regStart) < csrCurrent.key ){ +** AGGINVERSE +** } +** } +** } +** flush: +** while( (csrEnd.key + regEnd) <= csrCurrent.key ){ +** AGGSTEP +** } +** RETURN_ROW +** +** RANGE BETWEEN FOLLOWING AND FOLLOWING +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** }else{ +** AGGSTEP +** while( (csrCurrent.key + regEnd) < csrEnd.key ){ +** while( (csrCurrent.key + regStart) > csrStart.key ){ +** AGGINVERSE +** } +** RETURN_ROW +** } +** } +** } +** flush: +** AGGSTEP +** while( 1 ){ +** while( (csrCurrent.key + regStart) > csrStart.key ){ +** AGGINVERSE +** if( eof ) break "while( 1 )" loop. +** } +** RETURN_ROW +** } +** while( !eof csrCurrent ){ +** RETURN_ROW +** } +** */ void sqlite3WindowCodeStep( Parse *pParse, /* Parse context */ @@ -1962,13 +2110,10 @@ void sqlite3WindowCodeStep( int csrInput = p->pSrc->a[0].iCursor; /* Cursor of sub-select */ int nInput = p->pSrc->a[0].pTab->nCol; /* Number of cols returned by sub */ int iInput; /* To iterate through sub cols */ - int addrGoto; /* Address of OP_Goto */ int addrIfNot; /* Address of OP_IfNot */ int addrGosubFlush; /* Address of OP_Gosub to flush: */ int addrInteger; /* Address of OP_Integer */ - int addrShortcut = 0; int addrEmpty = 0; /* Address of OP_Rewind in flush: */ - int addrPeerJump = 0; /* Address of jump taken if not new peer */ int regStart = 0; /* Value of PRECEDING */ int regEnd = 0; /* Value of FOLLOWING */ int regNew; /* Array of registers holding new input row */ @@ -1977,6 +2122,7 @@ void sqlite3WindowCodeStep( int regNewPeer = 0; /* Peer values for new row (part of regNew) */ int regPeer = 0; /* Peer values for current row */ WindowCodeArg s; /* Context object for sub-routines */ + int lblWhereEnd; /* Label just before sqlite3WhereEnd() code */ assert( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_CURRENT || pMWin->eStart==TK_FOLLOWING || pMWin->eStart==TK_UNBOUNDED @@ -1985,8 +2131,7 @@ void sqlite3WindowCodeStep( || pMWin->eEnd==TK_UNBOUNDED || pMWin->eEnd==TK_PRECEDING ); - /* Determine whether or not each partition will be cached before beginning - ** to process rows within it. */ + lblWhereEnd = sqlite3VdbeMakeLabel(pParse); /* Fill in the context object */ memset(&s, 0, sizeof(WindowCodeArg)); @@ -2083,7 +2228,7 @@ void sqlite3WindowCodeStep( sqlite3VdbeAddOp2(v, OP_Rewind, s.current.csr, 1); windowReturnOneRow(pParse, pMWin, regGosub, addrGosub); sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr); - addrShortcut = sqlite3VdbeAddOp0(v, OP_Goto); + sqlite3VdbeAddOp2(v, OP_Goto, 0, lblWhereEnd); sqlite3VdbeJumpHere(v, addrGe); } if( pMWin->eStart==TK_FOLLOWING && pMWin->eType!=TK_RANGE && regEnd ){ @@ -2104,13 +2249,13 @@ void sqlite3WindowCodeStep( } sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regFirst); - addrGoto = sqlite3VdbeAddOp0(v, OP_Goto); + sqlite3VdbeAddOp2(v, OP_Goto, 0, lblWhereEnd); /* Begin generating SECOND_ROW_CODE */ VdbeModuleComment((pParse->pVdbe, "Begin WindowCodeStep.SECOND_ROW")); sqlite3VdbeJumpHere(v, addrIfNot); if( regPeer ){ - addrPeerJump = windowIfNewPeer(pParse, pOrderBy, regNewPeer, regPeer); + windowIfNewPeer(pParse, pOrderBy, regNewPeer, regPeer, lblWhereEnd); } if( pMWin->eStart==TK_FOLLOWING ){ windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0); @@ -2158,14 +2303,10 @@ void sqlite3WindowCodeStep( } } } - if( addrPeerJump ){ - sqlite3VdbeJumpHere(v, addrPeerJump); - } VdbeModuleComment((pParse->pVdbe, "End WindowCodeStep.SECOND_ROW")); /* End of the main input loop */ - sqlite3VdbeJumpHere(v, addrGoto); - if( addrShortcut>0 ) sqlite3VdbeJumpHere(v, addrShortcut); + sqlite3VdbeResolveLabel(v, lblWhereEnd); sqlite3WhereEnd(pWInfo); /* Fall through */ @@ -2217,7 +2358,6 @@ void sqlite3WindowCodeStep( sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart); sqlite3VdbeJumpHere(v, addrBreak); } - sqlite3VdbeJumpHere(v, addrEmpty); sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr);