1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-07 02:42:48 +03:00

Expand on header comment for sqlite3WindowCodeStep(). Further simplify the implementation of the same.

FossilOrigin-Name: 5129bcc996b3c9f78ab6b674a4364787e7b353b90f15f027cad4431012022c30
This commit is contained in:
dan
2019-03-12 15:21:51 +00:00
parent cc7a850f2e
commit 935d9d8260
3 changed files with 179 additions and 39 deletions

View File

@@ -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. C Expand\son\sheader\scomment\sfor\ssqlite3WindowCodeStep().\sFurther\ssimplify\sthe\simplementation\sof\sthe\ssame.
D 2019-03-11T19:50:54.722 D 2019-03-12T15:21:51.703
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F Makefile.in 236d2739dc3e823c3c909bca2d6cef93009bafbefd7018a8f3281074ecb92954 F Makefile.in 236d2739dc3e823c3c909bca2d6cef93009bafbefd7018a8f3281074ecb92954
@@ -604,7 +604,7 @@ F src/where.c 8a207cb2ca6b99e1edb1e4bbff9b0504385a759cbf66180d1deb34d80ca4b799
F src/whereInt.h 5f14db426ca46a83eabab1ae9aa6d4b8f27504ad35b64c290916289b1ddb2e88 F src/whereInt.h 5f14db426ca46a83eabab1ae9aa6d4b8f27504ad35b64c290916289b1ddb2e88
F src/wherecode.c ce7b21e1be2b981d62683fc59c4ca73a04a7ff2f1ebec23d41baf2da2349afd6 F src/wherecode.c ce7b21e1be2b981d62683fc59c4ca73a04a7ff2f1ebec23d41baf2da2349afd6
F src/whereexpr.c 36b47f7261d6b6f1a72d774c113b74beddf6745aba1018e64b196e29db233442 F src/whereexpr.c 36b47f7261d6b6f1a72d774c113b74beddf6745aba1018e64b196e29db233442
F src/window.c f41e0b36e6c26aa8b858f488498aa8152d6441a09c0e96baa5979269e1f0d199 F src/window.c 4763c2e81e8bb5d79fc378ba650cfcf076653d78065bd85790d592d441c9ae7a
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
@@ -1812,7 +1812,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 6bd1a07949ff3d394056bfcc813444401ef00806e3f0e0423ff6962541e84bdb P 081263538332bb9c07e62630629007ccbba31bef5dc890f60b4ba58a355f70ac
R 895a04edfc289d379068084b0a252ef5 R b359af1c195991083b8f00df3e4dfe08
U dan U dan
Z ab8b6521d2f2f661581dab04391f3c9a Z 98fef6b18eded687d4fe3a47cb60a7a7

View File

@@ -1 +1 @@
081263538332bb9c07e62630629007ccbba31bef5dc890f60b4ba58a355f70ac 5129bcc996b3c9f78ab6b674a4364787e7b353b90f15f027cad4431012022c30

View File

@@ -1494,28 +1494,27 @@ static int windowCacheFrame(Window *pMWin){
** regOld and control falls through. Otherwise, if the contents of the arrays ** 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. ** are equal, an OP_Goto is executed. The address of the OP_Goto is returned.
*/ */
static int windowIfNewPeer( static void windowIfNewPeer(
Parse *pParse, Parse *pParse,
ExprList *pOrderBy, ExprList *pOrderBy,
int regNew, /* First in array of new values */ 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); Vdbe *v = sqlite3GetVdbe(pParse);
int addr;
if( pOrderBy ){ if( pOrderBy ){
int nVal = pOrderBy->nExpr; int nVal = pOrderBy->nExpr;
KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0); KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0);
sqlite3VdbeAddOp3(v, OP_Compare, regOld, regNew, nVal); sqlite3VdbeAddOp3(v, OP_Compare, regOld, regNew, nVal);
sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO); sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
addr = sqlite3VdbeAddOp3( sqlite3VdbeAddOp3(v, OP_Jump,
v, OP_Jump, sqlite3VdbeCurrentAddr(v)+1, 0, sqlite3VdbeCurrentAddr(v)+1 sqlite3VdbeCurrentAddr(v)+1, addr, sqlite3VdbeCurrentAddr(v)+1
); );
VdbeCoverageEqNe(v); VdbeCoverageEqNe(v);
sqlite3VdbeAddOp3(v, OP_Copy, regNew, regOld, nVal-1); sqlite3VdbeAddOp3(v, OP_Copy, regNew, regOld, nVal-1);
}else{ }else{
addr = sqlite3VdbeAddOp0(v, OP_Goto); sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
} }
return addr;
} }
typedef struct WindowCodeArg WindowCodeArg; typedef struct WindowCodeArg WindowCodeArg;
@@ -1708,8 +1707,7 @@ static int windowCodeOp(
int nReg = (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0); int nReg = (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0);
int regTmp = (nReg ? sqlite3GetTempRange(pParse, nReg) : 0); int regTmp = (nReg ? sqlite3GetTempRange(pParse, nReg) : 0);
windowReadPeerValues(p, csr, regTmp); windowReadPeerValues(p, csr, regTmp);
addr = windowIfNewPeer(pParse, pMWin->pOrderBy, regTmp, reg); windowIfNewPeer(pParse, pMWin->pOrderBy, regTmp, reg, addrContinue);
sqlite3VdbeChangeP2(v, addr, addrContinue);
sqlite3ReleaseTempRange(pParse, regTmp, nReg); sqlite3ReleaseTempRange(pParse, regTmp, nReg);
} }
@@ -1850,9 +1848,7 @@ Window *sqlite3WindowListDup(sqlite3 *db, Window *p){
** } ** }
** Insert new row into eph table. ** Insert new row into eph table.
** if( first row of partition ){ ** if( first row of partition ){
** Rewind(csrEnd) ** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent)
** Rewind(csrStart)
** Rewind(csrCurrent)
** regEnd = <expr2> ** regEnd = <expr2>
** regStart = <expr1> ** regStart = <expr1>
** }else{ ** }else{
@@ -1880,9 +1876,7 @@ Window *sqlite3WindowListDup(sqlite3 *db, Window *p){
** } ** }
** Insert new row into eph table. ** Insert new row into eph table.
** if( first row of partition ){ ** if( first row of partition ){
** Rewind(csrEnd) ** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent)
** Rewind(csrStart)
** Rewind(csrCurrent)
** regEnd = <expr2> ** regEnd = <expr2>
** regStart = regEnd - <expr1> ** regStart = regEnd - <expr1>
** }else{ ** }else{
@@ -1926,9 +1920,7 @@ Window *sqlite3WindowListDup(sqlite3 *db, Window *p){
** } ** }
** Insert new row into eph table. ** Insert new row into eph table.
** if( first row of partition ){ ** if( first row of partition ){
** Rewind(csrEnd) ** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent)
** Rewind(csrStart)
** Rewind(csrCurrent)
** regStart = <expr1> ** regStart = <expr1>
** }else{ ** }else{
** AGGSTEP ** AGGSTEP
@@ -1946,6 +1938,162 @@ Window *sqlite3WindowListDup(sqlite3 *db, Window *p){
** while( !eof csrCurrent ){ ** while( !eof csrCurrent ){
** RETURN_ROW ** RETURN_ROW
** } ** }
**
** Also requiring special handling are the cases:
**
** ROWS BETWEEN <expr1> PRECEDING AND <expr2> PRECEDING
** ROWS BETWEEN <expr1> FOLLOWING AND <expr2> 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 = <expr2>
** regStart = <expr1>
** 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 = <expr2>
** regStart = <expr1>
** }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 <expr1> PRECEDING AND <expr2> 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 = <expr2>
** regStart = <expr1>
** }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 <expr> FOLLOWING
** or <expr PRECEDING) read from cursor csr.
**
** RANGE BETWEEN <expr1> PRECEDING AND <expr2> 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 = <expr2>
** regStart = <expr1>
** }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 <expr1> FOLLOWING AND <expr2> 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 = <expr2>
** regStart = <expr1>
** }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( void sqlite3WindowCodeStep(
Parse *pParse, /* Parse context */ Parse *pParse, /* Parse context */
@@ -1962,13 +2110,10 @@ void sqlite3WindowCodeStep(
int csrInput = p->pSrc->a[0].iCursor; /* Cursor of sub-select */ 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 nInput = p->pSrc->a[0].pTab->nCol; /* Number of cols returned by sub */
int iInput; /* To iterate through sub cols */ int iInput; /* To iterate through sub cols */
int addrGoto; /* Address of OP_Goto */
int addrIfNot; /* Address of OP_IfNot */ int addrIfNot; /* Address of OP_IfNot */
int addrGosubFlush; /* Address of OP_Gosub to flush: */ int addrGosubFlush; /* Address of OP_Gosub to flush: */
int addrInteger; /* Address of OP_Integer */ int addrInteger; /* Address of OP_Integer */
int addrShortcut = 0;
int addrEmpty = 0; /* Address of OP_Rewind in flush: */ 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 <expr> PRECEDING */ int regStart = 0; /* Value of <expr> PRECEDING */
int regEnd = 0; /* Value of <expr> FOLLOWING */ int regEnd = 0; /* Value of <expr> FOLLOWING */
int regNew; /* Array of registers holding new input row */ 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 regNewPeer = 0; /* Peer values for new row (part of regNew) */
int regPeer = 0; /* Peer values for current row */ int regPeer = 0; /* Peer values for current row */
WindowCodeArg s; /* Context object for sub-routines */ WindowCodeArg s; /* Context object for sub-routines */
int lblWhereEnd; /* Label just before sqlite3WhereEnd() code */
assert( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_CURRENT assert( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_CURRENT
|| pMWin->eStart==TK_FOLLOWING || pMWin->eStart==TK_UNBOUNDED || pMWin->eStart==TK_FOLLOWING || pMWin->eStart==TK_UNBOUNDED
@@ -1985,8 +2131,7 @@ void sqlite3WindowCodeStep(
|| pMWin->eEnd==TK_UNBOUNDED || pMWin->eEnd==TK_PRECEDING || pMWin->eEnd==TK_UNBOUNDED || pMWin->eEnd==TK_PRECEDING
); );
/* Determine whether or not each partition will be cached before beginning lblWhereEnd = sqlite3VdbeMakeLabel(pParse);
** to process rows within it. */
/* Fill in the context object */ /* Fill in the context object */
memset(&s, 0, sizeof(WindowCodeArg)); memset(&s, 0, sizeof(WindowCodeArg));
@@ -2083,7 +2228,7 @@ void sqlite3WindowCodeStep(
sqlite3VdbeAddOp2(v, OP_Rewind, s.current.csr, 1); sqlite3VdbeAddOp2(v, OP_Rewind, s.current.csr, 1);
windowReturnOneRow(pParse, pMWin, regGosub, addrGosub); windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr); sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr);
addrShortcut = sqlite3VdbeAddOp0(v, OP_Goto); sqlite3VdbeAddOp2(v, OP_Goto, 0, lblWhereEnd);
sqlite3VdbeJumpHere(v, addrGe); sqlite3VdbeJumpHere(v, addrGe);
} }
if( pMWin->eStart==TK_FOLLOWING && pMWin->eType!=TK_RANGE && regEnd ){ if( pMWin->eStart==TK_FOLLOWING && pMWin->eType!=TK_RANGE && regEnd ){
@@ -2104,13 +2249,13 @@ void sqlite3WindowCodeStep(
} }
sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regFirst); sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regFirst);
addrGoto = sqlite3VdbeAddOp0(v, OP_Goto); sqlite3VdbeAddOp2(v, OP_Goto, 0, lblWhereEnd);
/* Begin generating SECOND_ROW_CODE */ /* Begin generating SECOND_ROW_CODE */
VdbeModuleComment((pParse->pVdbe, "Begin WindowCodeStep.SECOND_ROW")); VdbeModuleComment((pParse->pVdbe, "Begin WindowCodeStep.SECOND_ROW"));
sqlite3VdbeJumpHere(v, addrIfNot); sqlite3VdbeJumpHere(v, addrIfNot);
if( regPeer ){ if( regPeer ){
addrPeerJump = windowIfNewPeer(pParse, pOrderBy, regNewPeer, regPeer); windowIfNewPeer(pParse, pOrderBy, regNewPeer, regPeer, lblWhereEnd);
} }
if( pMWin->eStart==TK_FOLLOWING ){ if( pMWin->eStart==TK_FOLLOWING ){
windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0); windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
@@ -2158,14 +2303,10 @@ void sqlite3WindowCodeStep(
} }
} }
} }
if( addrPeerJump ){
sqlite3VdbeJumpHere(v, addrPeerJump);
}
VdbeModuleComment((pParse->pVdbe, "End WindowCodeStep.SECOND_ROW")); VdbeModuleComment((pParse->pVdbe, "End WindowCodeStep.SECOND_ROW"));
/* End of the main input loop */ /* End of the main input loop */
sqlite3VdbeJumpHere(v, addrGoto); sqlite3VdbeResolveLabel(v, lblWhereEnd);
if( addrShortcut>0 ) sqlite3VdbeJumpHere(v, addrShortcut);
sqlite3WhereEnd(pWInfo); sqlite3WhereEnd(pWInfo);
/* Fall through */ /* Fall through */
@@ -2217,7 +2358,6 @@ void sqlite3WindowCodeStep(
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart); sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart);
sqlite3VdbeJumpHere(v, addrBreak); sqlite3VdbeJumpHere(v, addrBreak);
} }
sqlite3VdbeJumpHere(v, addrEmpty); sqlite3VdbeJumpHere(v, addrEmpty);
sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr); sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr);