1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-05 15:55:57 +03:00

Simplify the windows frame code some. Add a comment explaining some of the VM code generated by sqlite3WindowCodeStep().

FossilOrigin-Name: 6bd1a07949ff3d394056bfcc813444401ef00806e3f0e0423ff6962541e84bdb
This commit is contained in:
dan
2019-03-11 18:17:04 +00:00
parent 71fddaf195
commit a786e453a4
3 changed files with 282 additions and 167 deletions

View File

@@ -1,5 +1,5 @@
C Fix\sproblems\swith\s"RANGE\s...\sORDER\sBY\s<expr>\sDESC"\swindow\sframes. C Simplify\sthe\swindows\sframe\scode\ssome.\sAdd\sa\scomment\sexplaining\ssome\sof\sthe\sVM\scode\sgenerated\sby\ssqlite3WindowCodeStep().
D 2019-03-11T11:12:34.831 D 2019-03-11T18:17:04.702
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 199efb1a0185fe887f4b14edeff903333be3152dfa7701a98c790b44360b376a F src/window.c ca0d94d256bc52b7815b3c73b00468d0de84021f0d7be1d18588584dc96996b4
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 ffc32b246d92d53c66094afe11950b53ffab6a1c230c602eebbfedafb2eb57f4 P e7bced731aa071c95bc398cdecd53c939841bf0c52fbcd06e47ba68f8c5cc35a
R 27103c6d5c4ac21fa5559af86898ca9a R c7a0f784b83bf2fca5c90a7c55a81175
U dan U dan
Z cff247c86123ad16f7fa6838517cb329 Z ca63593942babec716fd2593022c4696

View File

@@ -1 +1 @@
e7bced731aa071c95bc398cdecd53c939841bf0c52fbcd06e47ba68f8c5cc35a 6bd1a07949ff3d394056bfcc813444401ef00806e3f0e0423ff6962541e84bdb

View File

@@ -1698,114 +1698,284 @@ static int windowCodeOp(
return ret; return ret;
} }
/* /*
** This function - windowCodeStep() - generates the VM code that reads data ** Allocate and return a duplicate of the Window object indicated by the
** from the sub-select and returns rows to the consumer. For the simplest ** third argument. Set the Window.pOwner field of the new object to
** case: ** pOwner.
*/
Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p){
Window *pNew = 0;
if( ALWAYS(p) ){
pNew = sqlite3DbMallocZero(db, sizeof(Window));
if( pNew ){
pNew->zName = sqlite3DbStrDup(db, p->zName);
pNew->pFilter = sqlite3ExprDup(db, p->pFilter, 0);
pNew->pFunc = p->pFunc;
pNew->pPartition = sqlite3ExprListDup(db, p->pPartition, 0);
pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, 0);
pNew->eType = p->eType;
pNew->eEnd = p->eEnd;
pNew->eStart = p->eStart;
pNew->pStart = sqlite3ExprDup(db, p->pStart, 0);
pNew->pEnd = sqlite3ExprDup(db, p->pEnd, 0);
pNew->pOwner = pOwner;
}
}
return pNew;
}
/*
** Return a copy of the linked list of Window objects passed as the
** second argument.
*/
Window *sqlite3WindowListDup(sqlite3 *db, Window *p){
Window *pWin;
Window *pRet = 0;
Window **pp = &pRet;
for(pWin=p; pWin; pWin=pWin->pNextWin){
*pp = sqlite3WindowDup(db, 0, pWin);
if( *pp==0 ) break;
pp = &((*pp)->pNextWin);
}
return pRet;
}
/*
** sqlite3WhereBegin() has already been called for the SELECT statement
** passed as the second argument when this function is invoked. It generates
** code to populate the Window.regResult register for each window function
** and invoke the sub-routine at instruction addrGosub once for each row.
** sqlite3WhereEnd() is always called before returning.
** **
** ROWS BETWEEN <expr1> PRECEDING AND <expr2> FOLLOWING ** This function handles several different types of window frames, which
** require slightly different processing. The following pseudo code is
** used to implement window frames of the form:
** **
** The VM code generated is equivalent in spirit to the following: ** ROWS BETWEEN <expr1> PRECEDING AND <expr2> FOLLOWING
** **
** while( !eof ){ ** Other window frame types use variants of the following:
**
** ... loop started by sqlite3WhereBegin() ...
** if( new partition ){ ** if( new partition ){
** Gosub flush ** Gosub flush
** } ** }
** Insert new row into eph table. ** Insert new row into eph table.
** **
** if( first row of partition ){ ** if( first row of partition ){
** Rewind(csrEnd, skipNext=1) ** // Rewind three cursors, all open on the eph table.
** Rewind(start.csr, skipNext=1) ** Rewind(csrEnd);
** Rewind(csrCurrent, skipNext=1) ** Rewind(csrStart);
** ** Rewind(csrCurrent);
**
** regEnd = <expr2> // FOLLOWING expression ** regEnd = <expr2> // FOLLOWING expression
** regStart = <expr1> // PRECEDING expression ** regStart = <expr1> // PRECEDING expression
** }else{ ** }else{
** // First time this branch is taken, the eph table contains two
** // rows. The first row in the partition, which all three cursors
** // currently point to, and the following row.
** AGGSTEP
** if( (regEnd--)<=0 ){ ** if( (regEnd--)<=0 ){
** Next(csrCurrent) ** RETURN_ROW
** Return one row. ** if( (regStart--)<=0 ){
** if( (regStart--)<0 ){ ** AGGINVERSE
** Next(start.csr)
** AggInverse(start.csr)
** } ** }
** } ** }
** } ** }
** ** }
** Next(csrEnd)
** AggStep(csrEnd)
** }
** flush: ** flush:
** while( 1 ){ ** AGGSTEP
** Next(csrCurrent) ** while( 1 ){
** if( eof ) break ** RETURN ROW
** Return one row. ** if( csrCurrent is EOF ) break;
** if( (regStart--)<0 ){ ** if( (regStart--)<=0 ){
** Next(start.csr) ** AggInverse(csrStart)
** AggInverse(start.csr) ** Next(csrStart)
** } ** }
** } ** }
** Empty eph table.
** **
** More generally, the pattern used for all window types is: ** The pseudo-code above uses the following shorthand:
** **
** while( !eof ){ ** AGGSTEP: invoke the aggregate xStep() function for each window function
** with arguments read from the current row of cursor csrEnd, then
** step cursor csrEnd forward one row (i.e. sqlite3BtreeNext()).
**
** RETURN_ROW: return a row to the caller based on the contents of the
** current row of csrCurrent and the current state of all
** aggregates. Then step cursor csrCurrent forward one row.
**
** AGGINVERSE: invoke the aggregate xInverse() function for each window
** functions with arguments read from the current row of cursor
** csrStart. Then step csrStart forward one row.
**
** There are two other ROWS window frames that are handled significantly
** differently from the above - "BETWEEN <expr> PRECEDING AND <expr> PRECEDING"
** and "BETWEEN <expr> FOLLOWING AND <expr> FOLLOWING". These are special
** cases because they change the order in which the three cursors (csrStart,
** csrCurrent and csrEnd) iterate through the ephemeral table. Cases that
** use UNBOUNDED or CURRENT ROW are much simpler variations on one of these
** three.
**
** ROWS BETWEEN <expr1> PRECEDING AND <expr2> PRECEDING
**
** ... loop started by sqlite3WhereBegin() ...
** if( new partition ){ ** if( new partition ){
** Gosub flush ** Gosub flush
** } ** }
** Insert new row into eph table. ** Insert new row into eph table.
** if( first row of partition ){ ** if( first row of partition ){
** FIRST_ROW_CODE ** Rewind(csrEnd)
** Rewind(csrStart)
** Rewind(csrCurrent)
** regEnd = <expr2>
** regStart = <expr1>
** }else{ ** }else{
** SECOND_ROW_CODE ** if( (regEnd--)<=0 ){
** } ** AGGSTEP
** ALL_ROW_CODE ** }
** } ** RETURN_ROW
** if( (regStart--)<=0 ){
** AGGINVERSE
** }
** }
** }
** flush: ** flush:
** FLUSH_CODE ** if( (regEnd--)<=0 ){
** Empty eph table. ** AGGSTEP
** }
** RETURN_ROW
**
**
** ROWS 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 = regEnd - <expr1>
** }else{
** AGGSTEP
** if( (regEnd--)<=0 ){
** RETURN_ROW
** }
** if( (regStart--)<=0 ){
** AGGINVERSE
** }
** }
** }
** flush:
** AGGSTEP
** while( 1 ){
** if( (regEnd--)<=0 ){
** RETURN_ROW
** if( eof ) break;
** }
** if( (regStart--)<=0 ){
** AGGINVERSE
** if( eof ) break
** }
** }
** while( !eof csrCurrent ){
** RETURN_ROW
** }
**
** For the most part, the patterns above are adapted to support UNBOUNDED by
** assuming that it is equivalent to "infinity PRECEDING/FOLLOWING" and
** CURRENT ROW by assuming that it is equivilent to "0 PRECEDING/FOLLOWING".
** This is optimized of course - branches that will never be taken and
** conditions that are always true are omitted from the VM code. The only
** exceptional case is:
**
** ROWS BETWEEN <expr1> FOLLOWING AND UNBOUNDED 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)
** regStart = <expr1>
** }else{
** AGGSTEP
** }
** }
** flush:
** AGGSTEP
** while( 1 ){
** if( (regStart--)<=0 ){
** AGGINVERSE
** if( eof ) break
** }
** RETURN_ROW
** }
** while( !eof csrCurrent ){
** RETURN_ROW
** }
**
** Sometimes, this function generates code to run in "cache mode" - meaning
** the entire partition is cached in the ephemeral table before any of its
** rows are processed, instead of processing rows as the sub-select delivers
** them. This is required by certain built-in window functions, for example
** percent_rank() or lead(). In that case, the relevant pseudo-code above
** is modified to:
**
** ... loop started by sqlite3WhereBegin() ...
** if( new partition ){
** Gosub flush
** }
** Insert new row into eph table.
** }
** flush:
** for each row in eph table {
**
** followed immediately by the code that usually follows the "Insert new row
** into eph table." line.
** **
*/ */
static void windowCodeStep( void sqlite3WindowCodeStep(
Parse *pParse, Parse *pParse, /* Parse context */
Select *p, Select *p, /* Rewritten SELECT statement */
WhereInfo *pWInfo, WhereInfo *pWInfo, /* Context returned by sqlite3WhereBegin() */
int regGosub, int regGosub, /* Register for OP_Gosub */
int addrGosub int addrGosub /* OP_Gosub here to return each row */
){ ){
Window *pMWin = p->pWin; Window *pMWin = p->pWin;
ExprList *pOrderBy = pMWin->pOrderBy; ExprList *pOrderBy = pMWin->pOrderBy;
Vdbe *v = sqlite3GetVdbe(pParse); Vdbe *v = sqlite3GetVdbe(pParse);
int bCache; /* True if generating "cache-mode" code */
int regFlushPart; /* Register for "Gosub flush_partition" */ int regFlushPart; /* Register for "Gosub flush_partition" */
int csrWrite; /* Cursor used to write to eph. table */
int regArg; int csrInput = p->pSrc->a[0].iCursor; /* Cursor of sub-select */
int csrWrite = pMWin->iEphCsr+1; int nInput = p->pSrc->a[0].pTab->nCol; /* Number of cols returned by sub */
int iInput; /* To iterate through sub cols */
int iSubCsr = p->pSrc->a[0].iCursor; /* Cursor of sub-select */ int addrGoto; /* Address of OP_Goto */
int nSub = p->pSrc->a[0].pTab->nCol; /* Number of cols returned by sub */ int addrIfNot; /* Address of OP_IfNot */
int iCol; /* To iterate through sub cols */ int addrGosubFlush; /* Address of OP_Gosub to flush: */
int addrInteger; /* Address of OP_Integer */
int addrGoto; int addrCacheRewind; /* Address of OP_Rewind used in cache-mode */
int addrIf; int addrCacheNext; /* Jump here for next row in cache-mode */
int addrGosubFlush;
int addrInteger;
int addrCacheRewind;
int addrCacheNext;
int addrShortcut = 0; int addrShortcut = 0;
int addrEmpty = 0; int addrEmpty = 0; /* Address of OP_Rewind in flush: */
int addrPeerJump = 0; int addrPeerJump = 0; /* Address of jump taken if not new peer */
int bCache = windowCachePartition(pMWin);
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 reg = pParse->nMem+1; int regRecord; /* regNew array in record form */
int regRecord = reg+nSub; int regRowid; /* Rowid for regRecord in eph table */
int regRowid = regRecord+1; int regNewPeer = 0; /* Peer values for new row (part of regNew) */
int regPeer = 0; int regPeer = 0; /* Peer values for current row */
int regNewPeer = 0; WindowCodeArg s; /* Context object for sub-routines */
WindowCodeArg s;
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
@@ -1814,6 +1984,11 @@ static void windowCodeStep(
|| 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
** to process rows within it. */
bCache = windowCachePartition(pMWin);
/* Fill in the context object */
memset(&s, 0, sizeof(WindowCodeArg)); memset(&s, 0, sizeof(WindowCodeArg));
s.pParse = pParse; s.pParse = pParse;
s.pMWin = pMWin; s.pMWin = pMWin;
@@ -1821,13 +1996,19 @@ static void windowCodeStep(
s.regGosub = regGosub; s.regGosub = regGosub;
s.addrGosub = addrGosub; s.addrGosub = addrGosub;
s.current.csr = pMWin->iEphCsr; s.current.csr = pMWin->iEphCsr;
csrWrite = s.current.csr+1;
s.start.csr = s.current.csr+2; s.start.csr = s.current.csr+2;
s.end.csr = s.current.csr+3; s.end.csr = s.current.csr+3;
pParse->nMem += 1 + nSub + 1; regNew = pParse->nMem+1;
pParse->nMem += nInput;
regRecord = ++pParse->nMem;
regRowid = ++pParse->nMem;
regFlushPart = ++pParse->nMem; regFlushPart = ++pParse->nMem;
/* If the window frame contains an "<expr> PRECEDING" or "<expr> FOLLOWING"
** clause, allocate registers to store the results of evaluating each
** <expr>. */
if( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_FOLLOWING ){ if( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_FOLLOWING ){
regStart = ++pParse->nMem; regStart = ++pParse->nMem;
} }
@@ -1836,13 +2017,12 @@ static void windowCodeStep(
} }
/* If this is not a "ROWS BETWEEN ..." frame, then allocate arrays of /* If this is not a "ROWS BETWEEN ..." frame, then allocate arrays of
** registers to store a copies of the ORDER BY expressions for the ** registers to store copies of the ORDER BY expressions (peer values)
** main loop, and for each cursor (start, current and end). */ ** for the main loop, and for each cursor (start, current and end). */
if( pMWin->eType!=TK_ROWS ){ if( pMWin->eType!=TK_ROWS ){
int nPeer = (pOrderBy ? pOrderBy->nExpr : 0); int nPeer = (pOrderBy ? pOrderBy->nExpr : 0);
regNewPeer = reg + pMWin->nBufferCol; regNewPeer = regNew + pMWin->nBufferCol;
if( pMWin->pPartition ) regNewPeer += pMWin->pPartition->nExpr; if( pMWin->pPartition ) regNewPeer += pMWin->pPartition->nExpr;
regPeer = pParse->nMem+1; pParse->nMem += nPeer; regPeer = pParse->nMem+1; pParse->nMem += nPeer;
s.start.reg = pParse->nMem+1; pParse->nMem += nPeer; s.start.reg = pParse->nMem+1; pParse->nMem += nPeer;
s.current.reg = pParse->nMem+1; pParse->nMem += nPeer; s.current.reg = pParse->nMem+1; pParse->nMem += nPeer;
@@ -1850,24 +2030,23 @@ static void windowCodeStep(
} }
/* Load the column values for the row returned by the sub-select /* Load the column values for the row returned by the sub-select
** into an array of registers starting at reg. Assemble them into ** into an array of registers starting at regNew. Assemble them into
** a record in register regRecord. TODO: An optimization here? */ ** a record in register regRecord. */
for(iCol=0; iCol<nSub; iCol++){ for(iInput=0; iInput<nInput; iInput++){
sqlite3VdbeAddOp3(v, OP_Column, iSubCsr, iCol, reg+iCol); sqlite3VdbeAddOp3(v, OP_Column, csrInput, iInput, regNew+iInput);
} }
sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, nSub, regRecord); sqlite3VdbeAddOp3(v, OP_MakeRecord, regNew, nInput, regRecord);
/* An input row has just been read into an array of registers starting /* An input row has just been read into an array of registers starting
** at reg. If the window has a PARTITION clause, this block generates ** at regNew. If the window has a PARTITION clause, this block generates
** VM code to check if the input row is the start of a new partition. ** VM code to check if the input row is the start of a new partition.
** If so, it does an OP_Gosub to an address to be filled in later. The ** If so, it does an OP_Gosub to an address to be filled in later. The
** address of the OP_Gosub is stored in local variable addrGosubFlush. ** address of the OP_Gosub is stored in local variable addrGosubFlush. */
*/
if( pMWin->pPartition ){ if( pMWin->pPartition ){
int addr; int addr;
ExprList *pPart = pMWin->pPartition; ExprList *pPart = pMWin->pPartition;
int nPart = pPart->nExpr; int nPart = pPart->nExpr;
int regNewPart = reg + pMWin->nBufferCol; int regNewPart = regNew + pMWin->nBufferCol;
KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0); KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0);
addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart, nPart); addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart, nPart);
@@ -1893,11 +2072,11 @@ static void windowCodeStep(
} }
addrCacheRewind = sqlite3VdbeAddOp1(v, OP_Rewind, csrWrite); addrCacheRewind = sqlite3VdbeAddOp1(v, OP_Rewind, csrWrite);
}else{ }else{
addrIf = sqlite3VdbeAddOp1(v, OP_IfNot, pMWin->regFirst); addrIfNot = sqlite3VdbeAddOp1(v, OP_IfNot, pMWin->regFirst);
} }
/* This block is run for the first row of each partition */ /* This block is run for the first row of each partition */
s.regArg = regArg = windowInitAccum(pParse, pMWin); s.regArg = windowInitAccum(pParse, pMWin);
if( regStart ){ if( regStart ){
sqlite3ExprCode(pParse, pMWin->pStart, regStart); sqlite3ExprCode(pParse, pMWin->pStart, regStart);
@@ -1950,14 +2129,14 @@ static void windowCodeStep(
addrGoto = sqlite3VdbeAddOp0(v, OP_Goto); addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
/* Begin generating SECOND_ROW_CODE */ /* Begin generating SECOND_ROW_CODE */
VdbeModuleComment((pParse->pVdbe, "Begin windowCodeStep.SECOND_ROW_CODE")); VdbeModuleComment((pParse->pVdbe, "Begin WindowCodeStep.SECOND_ROW"));
if( bCache ){ if( bCache ){
addrCacheNext = sqlite3VdbeCurrentAddr(v); addrCacheNext = sqlite3VdbeCurrentAddr(v);
if( pMWin->eType!=TK_ROWS ){ if( pMWin->eType!=TK_ROWS ){
windowReadPeerValues(&s, csrWrite, regNewPeer); windowReadPeerValues(&s, csrWrite, regNewPeer);
} }
}else{ }else{
sqlite3VdbeJumpHere(v, addrIf); sqlite3VdbeJumpHere(v, addrIfNot);
} }
if( regPeer ){ if( regPeer ){
addrPeerJump = windowIfNewPeer(pParse, pOrderBy, regNewPeer, regPeer); addrPeerJump = windowIfNewPeer(pParse, pOrderBy, regNewPeer, regPeer);
@@ -2011,7 +2190,7 @@ static void windowCodeStep(
if( addrPeerJump ){ if( addrPeerJump ){
sqlite3VdbeJumpHere(v, addrPeerJump); sqlite3VdbeJumpHere(v, addrPeerJump);
} }
VdbeModuleComment((pParse->pVdbe, "End windowCodeStep.SECOND_ROW_CODE")); VdbeModuleComment((pParse->pVdbe, "End WindowCodeStep.SECOND_ROW"));
/* End of the main input loop */ /* End of the main input loop */
sqlite3VdbeJumpHere(v, addrGoto); sqlite3VdbeJumpHere(v, addrGoto);
@@ -2029,7 +2208,7 @@ static void windowCodeStep(
sqlite3VdbeJumpHere(v, addrGosubFlush); sqlite3VdbeJumpHere(v, addrGosubFlush);
} }
VdbeModuleComment((pParse->pVdbe, "Begin windowCodeStep.FLUSH_CODE")); VdbeModuleComment((pParse->pVdbe, "Begin WindowCodeStep.FLUSH"));
addrEmpty = sqlite3VdbeAddOp1(v, OP_Rewind, csrWrite); addrEmpty = sqlite3VdbeAddOp1(v, OP_Rewind, csrWrite);
if( pMWin->eEnd==TK_PRECEDING ){ if( pMWin->eEnd==TK_PRECEDING ){
windowCodeOp(&s, WINDOW_AGGSTEP, regEnd, 0); windowCodeOp(&s, WINDOW_AGGSTEP, regEnd, 0);
@@ -2079,75 +2258,11 @@ static void windowCodeStep(
sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr); sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr);
sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regSize); sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regSize);
if( bCache==0 ) sqlite3VdbeAddOp2(v, OP_Integer, 1, pMWin->regFirst); if( bCache==0 ) sqlite3VdbeAddOp2(v, OP_Integer, 1, pMWin->regFirst);
VdbeModuleComment((pParse->pVdbe, "End windowCodeStep.FLUSH_CODE")); VdbeModuleComment((pParse->pVdbe, "End WindowCodeStep.FLUSH"));
if( pMWin->pPartition ){ if( pMWin->pPartition ){
sqlite3VdbeChangeP1(v, addrInteger, sqlite3VdbeCurrentAddr(v)); sqlite3VdbeChangeP1(v, addrInteger, sqlite3VdbeCurrentAddr(v));
sqlite3VdbeAddOp1(v, OP_Return, regFlushPart); sqlite3VdbeAddOp1(v, OP_Return, regFlushPart);
} }
} }
/*
** Allocate and return a duplicate of the Window object indicated by the
** third argument. Set the Window.pOwner field of the new object to
** pOwner.
*/
Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p){
Window *pNew = 0;
if( ALWAYS(p) ){
pNew = sqlite3DbMallocZero(db, sizeof(Window));
if( pNew ){
pNew->zName = sqlite3DbStrDup(db, p->zName);
pNew->pFilter = sqlite3ExprDup(db, p->pFilter, 0);
pNew->pFunc = p->pFunc;
pNew->pPartition = sqlite3ExprListDup(db, p->pPartition, 0);
pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, 0);
pNew->eType = p->eType;
pNew->eEnd = p->eEnd;
pNew->eStart = p->eStart;
pNew->pStart = sqlite3ExprDup(db, p->pStart, 0);
pNew->pEnd = sqlite3ExprDup(db, p->pEnd, 0);
pNew->pOwner = pOwner;
}
}
return pNew;
}
/*
** Return a copy of the linked list of Window objects passed as the
** second argument.
*/
Window *sqlite3WindowListDup(sqlite3 *db, Window *p){
Window *pWin;
Window *pRet = 0;
Window **pp = &pRet;
for(pWin=p; pWin; pWin=pWin->pNextWin){
*pp = sqlite3WindowDup(db, 0, pWin);
if( *pp==0 ) break;
pp = &((*pp)->pNextWin);
}
return pRet;
}
/*
** sqlite3WhereBegin() has already been called for the SELECT statement
** passed as the second argument when this function is invoked. It generates
** code to populate the Window.regResult register for each window function and
** invoke the sub-routine at instruction addrGosub once for each row.
** This function calls sqlite3WhereEnd() before returning.
*/
void sqlite3WindowCodeStep(
Parse *pParse, /* Parse context */
Select *p, /* Rewritten SELECT statement */
WhereInfo *pWInfo, /* Context returned by sqlite3WhereBegin() */
int regGosub, /* Register for OP_Gosub */
int addrGosub /* OP_Gosub here to return each row */
){
VdbeModuleComment((pParse->pVdbe, "Begin windowCodeStep()"));
windowCodeStep(pParse, p, pWInfo, regGosub, addrGosub);
VdbeModuleComment((pParse->pVdbe, "End windowCodeStep()"));
}
#endif /* SQLITE_OMIT_WINDOWFUNC */ #endif /* SQLITE_OMIT_WINDOWFUNC */