mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-07 02:42:48 +03:00
Finish consolidation of window frame code. Add untested support for GROUPS frames.
FossilOrigin-Name: 954bf369935083c188c3b14e77ed89fc5ec4323cc5b0c67e4a2e48fcc278df45
This commit is contained in:
760
src/window.c
760
src/window.c
@@ -914,7 +914,7 @@ Window *sqlite3WindowAlloc(
|
||||
int bImplicitFrame = 0;
|
||||
|
||||
/* Parser assures the following: */
|
||||
assert( eType==0 || eType==TK_RANGE || eType==TK_ROWS );
|
||||
assert( eType==0 || eType==TK_RANGE || eType==TK_ROWS || eType==TK_GROUPS );
|
||||
assert( eStart==TK_CURRENT || eStart==TK_PRECEDING
|
||||
|| eStart==TK_UNBOUNDED || eStart==TK_FOLLOWING );
|
||||
assert( eEnd==TK_CURRENT || eEnd==TK_FOLLOWING
|
||||
@@ -1327,69 +1327,6 @@ static void windowAggFinal(Parse *pParse, Window *pMWin, int bFinal){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This function generates VM code to invoke the sub-routine at address
|
||||
** lblFlushPart once for each partition with the entire partition cached in
|
||||
** the Window.iEphCsr temp table.
|
||||
*/
|
||||
static void windowPartitionCache(
|
||||
Parse *pParse,
|
||||
Select *p, /* The rewritten SELECT statement */
|
||||
WhereInfo *pWInfo, /* WhereInfo to call WhereEnd() on */
|
||||
int regFlushPart, /* Register to use with Gosub lblFlushPart */
|
||||
int lblFlushPart, /* Subroutine to Gosub to */
|
||||
int *pRegSize /* OUT: Register containing partition size */
|
||||
){
|
||||
Window *pMWin = p->pWin;
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
int iSubCsr = p->pSrc->a[0].iCursor;
|
||||
int nSub = p->pSrc->a[0].pTab->nCol;
|
||||
int k;
|
||||
|
||||
int reg = pParse->nMem+1;
|
||||
int regRecord = reg+nSub;
|
||||
int regRowid = regRecord+1;
|
||||
|
||||
*pRegSize = regRowid;
|
||||
pParse->nMem += nSub + 2;
|
||||
|
||||
/* Load the column values for the row returned by the sub-select
|
||||
** into an array of registers starting at reg. */
|
||||
for(k=0; k<nSub; k++){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iSubCsr, k, reg+k);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, nSub, regRecord);
|
||||
|
||||
/* Check if this is the start of a new partition. If so, call the
|
||||
** flush_partition sub-routine. */
|
||||
if( pMWin->pPartition ){
|
||||
int addr;
|
||||
ExprList *pPart = pMWin->pPartition;
|
||||
int nPart = pPart->nExpr;
|
||||
int regNewPart = reg + pMWin->nBufferCol;
|
||||
KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0);
|
||||
|
||||
addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart,nPart);
|
||||
sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
|
||||
sqlite3VdbeAddOp3(v, OP_Jump, addr+2, addr+4, addr+2);
|
||||
VdbeCoverageEqNe(v);
|
||||
sqlite3VdbeAddOp3(v, OP_Copy, regNewPart, pMWin->regPart, nPart-1);
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart);
|
||||
VdbeComment((v, "call flush_partition"));
|
||||
}
|
||||
|
||||
/* Buffer the current row in the ephemeral table. */
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, pMWin->iEphCsr, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, pMWin->iEphCsr, regRecord, regRowid);
|
||||
|
||||
/* End of the input loop */
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
|
||||
/* Invoke "flush_partition" to deal with the final (or only) partition */
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart);
|
||||
VdbeComment((v, "call flush_partition"));
|
||||
}
|
||||
|
||||
/*
|
||||
** Invoke the sub-routine at regGosub (generated by code in select.c) to
|
||||
** return the current row of Window.iEphCsr. If all window functions are
|
||||
@@ -1470,45 +1407,6 @@ static void windowReturnOneRow(
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
|
||||
}
|
||||
|
||||
/*
|
||||
** Invoke the code generated by windowReturnOneRow() and, optionally, the
|
||||
** xInverse() function for each window function, for one or more rows
|
||||
** from the Window.iEphCsr temp table. This routine generates VM code
|
||||
** similar to:
|
||||
**
|
||||
** while( regCtr>0 ){
|
||||
** regCtr--;
|
||||
** windowReturnOneRow()
|
||||
** if( bInverse ){
|
||||
** AggInverse
|
||||
** }
|
||||
** Next (Window.iEphCsr)
|
||||
** }
|
||||
*/
|
||||
static void windowReturnRows(
|
||||
Parse *pParse,
|
||||
Window *pMWin, /* List of window functions */
|
||||
int regCtr, /* Register containing number of rows */
|
||||
int regGosub, /* Register for Gosub addrGosub */
|
||||
int addrGosub, /* Address of sub-routine for ReturnOneRow */
|
||||
int regInvArg, /* Array of registers for xInverse args */
|
||||
int regInvSize /* Register containing size of partition */
|
||||
){
|
||||
int addr;
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
windowAggFinal(pParse, pMWin, 0);
|
||||
addr = sqlite3VdbeAddOp3(v, OP_IfPos, regCtr, sqlite3VdbeCurrentAddr(v)+2 ,1);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
|
||||
windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
|
||||
if( regInvArg ){
|
||||
windowAggStep(pParse, pMWin, pMWin->iEphCsr, 1, regInvArg, regInvSize);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, addr);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeJumpHere(v, addr+1); /* The OP_Goto */
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code to set the accumulator register for each window function
|
||||
** in the linked list passed as the second argument to NULL. And perform
|
||||
@@ -1562,7 +1460,46 @@ static int windowCachePartition(Window *pMWin){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** regOld and regNew are each the first register in an array of size
|
||||
** pOrderBy->nExpr. This function generates code to compare the two
|
||||
** arrays of registers using the collation sequences and other comparison
|
||||
** parameters specified by pOrderBy.
|
||||
**
|
||||
** If the two arrays are not equal, the contents of regNew is copied to
|
||||
** 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(
|
||||
Parse *pParse,
|
||||
ExprList *pOrderBy,
|
||||
int regNew, /* First in array of new values */
|
||||
int regOld /* First in array of old values */
|
||||
){
|
||||
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
|
||||
);
|
||||
VdbeCoverageEqNe(v);
|
||||
sqlite3VdbeAddOp3(v, OP_Copy, regNew, regOld, nVal-1);
|
||||
}else{
|
||||
addr = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
typedef struct WindowCodeArg WindowCodeArg;
|
||||
typedef struct WindowCsrAndReg WindowCsrAndReg;
|
||||
struct WindowCsrAndReg {
|
||||
int csr;
|
||||
int reg;
|
||||
};
|
||||
struct WindowCodeArg {
|
||||
Parse *pParse;
|
||||
Window *pMWin;
|
||||
@@ -1570,23 +1507,53 @@ struct WindowCodeArg {
|
||||
int regGosub;
|
||||
int addrGosub;
|
||||
int regArg;
|
||||
|
||||
WindowCsrAndReg start;
|
||||
WindowCsrAndReg current;
|
||||
WindowCsrAndReg end;
|
||||
};
|
||||
|
||||
#define WINDOW_RETURN_ROW 1
|
||||
#define WINDOW_AGGINVERSE 2
|
||||
#define WINDOW_AGGSTEP 3
|
||||
|
||||
/*
|
||||
** Generate VM code to read the window frames peer values from cursor csr into
|
||||
** an array of registers starting at reg.
|
||||
*/
|
||||
static void windowReadPeerValues(
|
||||
WindowCodeArg *p,
|
||||
int csr,
|
||||
int reg
|
||||
){
|
||||
Window *pMWin = p->pMWin;
|
||||
ExprList *pOrderBy = pMWin->pOrderBy;
|
||||
if( pOrderBy ){
|
||||
Vdbe *v = sqlite3GetVdbe(p->pParse);
|
||||
ExprList *pPart = pMWin->pPartition;
|
||||
int iColOff = pMWin->nBufferCol + (pPart ? pPart->nExpr : 0);
|
||||
int i;
|
||||
for(i=0; i<pOrderBy->nExpr; i++){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, csr, iColOff+i, reg+i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int windowCodeOp(
|
||||
WindowCodeArg *p,
|
||||
int op,
|
||||
int csr,
|
||||
int regCountdown,
|
||||
int jumpOnEof
|
||||
){
|
||||
int csr, reg;
|
||||
Parse *pParse = p->pParse;
|
||||
Window *pMWin = p->pMWin;
|
||||
int ret = 0;
|
||||
Vdbe *v = p->pVdbe;
|
||||
int addrIf = 0;
|
||||
int addrContinue = 0;
|
||||
int addrGoto = 0;
|
||||
int bPeer = (pMWin->eType!=TK_ROWS);
|
||||
|
||||
/* Special case - WINDOW_AGGINVERSE is always a no-op if the frame
|
||||
** starts with UNBOUNDED PRECEDING. */
|
||||
@@ -1599,18 +1566,27 @@ static int windowCodeOp(
|
||||
addrIf = sqlite3VdbeAddOp3(v, OP_IfPos, regCountdown, 0, 1);
|
||||
}
|
||||
|
||||
if( op==WINDOW_RETURN_ROW ){
|
||||
windowAggFinal(pParse, pMWin, 0);
|
||||
}
|
||||
addrContinue = sqlite3VdbeCurrentAddr(v);
|
||||
switch( op ){
|
||||
case WINDOW_RETURN_ROW:
|
||||
windowAggFinal(p->pParse, pMWin, 0);
|
||||
windowReturnOneRow(p->pParse, pMWin, p->regGosub, p->addrGosub);
|
||||
csr = p->current.csr;
|
||||
reg = p->current.reg;
|
||||
windowReturnOneRow(pParse, pMWin, p->regGosub, p->addrGosub);
|
||||
break;
|
||||
|
||||
case WINDOW_AGGINVERSE:
|
||||
windowAggStep(p->pParse, pMWin, csr, 1, p->regArg, pMWin->regSize);
|
||||
csr = p->start.csr;
|
||||
reg = p->start.reg;
|
||||
windowAggStep(pParse, pMWin, csr, 1, p->regArg, pMWin->regSize);
|
||||
break;
|
||||
|
||||
case WINDOW_AGGSTEP:
|
||||
windowAggStep(p->pParse, pMWin, csr, 0, p->regArg, pMWin->regSize);
|
||||
csr = p->end.csr;
|
||||
reg = p->end.reg;
|
||||
windowAggStep(pParse, pMWin, csr, 0, p->regArg, pMWin->regSize);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1618,17 +1594,27 @@ static int windowCodeOp(
|
||||
sqlite3VdbeAddOp2(v, OP_Next, csr, sqlite3VdbeCurrentAddr(v)+2);
|
||||
ret = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Next, csr, sqlite3VdbeCurrentAddr(v)+1);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, csr, sqlite3VdbeCurrentAddr(v)+1+bPeer);
|
||||
if( bPeer ){
|
||||
addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
}
|
||||
}
|
||||
|
||||
if( regCountdown>0 ){
|
||||
sqlite3VdbeJumpHere(v, addrIf);
|
||||
if( bPeer ){
|
||||
int addr;
|
||||
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);
|
||||
sqlite3ReleaseTempRange(pParse, regTmp, nReg);
|
||||
}
|
||||
|
||||
if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto);
|
||||
if( addrIf ) 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
|
||||
@@ -1646,7 +1632,7 @@ static int windowCodeOp(
|
||||
**
|
||||
** if( first row of partition ){
|
||||
** Rewind(csrEnd, skipNext=1)
|
||||
** Rewind(csrStart, skipNext=1)
|
||||
** Rewind(start.csr, skipNext=1)
|
||||
** Rewind(csrCurrent, skipNext=1)
|
||||
**
|
||||
** regEnd = <expr2> // FOLLOWING expression
|
||||
@@ -1656,8 +1642,8 @@ static int windowCodeOp(
|
||||
** Next(csrCurrent)
|
||||
** Return one row.
|
||||
** if( (regStart--)<0 ){
|
||||
** Next(csrStart)
|
||||
** AggInverse(csrStart)
|
||||
** Next(start.csr)
|
||||
** AggInverse(start.csr)
|
||||
** }
|
||||
** }
|
||||
** }
|
||||
@@ -1671,8 +1657,8 @@ static int windowCodeOp(
|
||||
** if( eof ) break
|
||||
** Return one row.
|
||||
** if( (regStart--)<0 ){
|
||||
** Next(csrStart)
|
||||
** AggInverse(csrStart)
|
||||
** Next(start.csr)
|
||||
** AggInverse(start.csr)
|
||||
** }
|
||||
** }
|
||||
** Empty eph table.
|
||||
@@ -1704,15 +1690,12 @@ static void windowCodeStep(
|
||||
int addrGosub
|
||||
){
|
||||
Window *pMWin = p->pWin;
|
||||
ExprList *pOrderBy = pMWin->pOrderBy;
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
int regFlushPart; /* Register for "Gosub flush_partition" */
|
||||
|
||||
int regArg;
|
||||
int csrCurrent = pMWin->iEphCsr;
|
||||
int csrWrite = csrCurrent+1;
|
||||
int csrStart = csrCurrent+2;
|
||||
int csrEnd = csrCurrent+3;
|
||||
|
||||
int csrWrite = pMWin->iEphCsr+1;
|
||||
|
||||
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 */
|
||||
@@ -1727,6 +1710,7 @@ static void windowCodeStep(
|
||||
|
||||
int addrShortcut = 0;
|
||||
int addrEmpty = 0;
|
||||
int addrPeerJump = 0;
|
||||
|
||||
int bCache = windowCachePartition(pMWin);
|
||||
|
||||
@@ -1736,6 +1720,8 @@ static void windowCodeStep(
|
||||
int reg = pParse->nMem+1;
|
||||
int regRecord = reg+nSub;
|
||||
int regRowid = regRecord+1;
|
||||
int regPeer = 0;
|
||||
int regNewPeer = 0;
|
||||
WindowCodeArg s;
|
||||
|
||||
memset(&s, 0, sizeof(WindowCodeArg));
|
||||
@@ -1744,6 +1730,9 @@ static void windowCodeStep(
|
||||
s.pVdbe = v;
|
||||
s.regGosub = regGosub;
|
||||
s.addrGosub = addrGosub;
|
||||
s.current.csr = pMWin->iEphCsr;
|
||||
s.start.csr = s.current.csr+2;
|
||||
s.end.csr = s.current.csr+3;
|
||||
|
||||
pParse->nMem += 1 + nSub + 1;
|
||||
|
||||
@@ -1756,6 +1745,19 @@ static void windowCodeStep(
|
||||
regEnd = ++pParse->nMem;
|
||||
}
|
||||
|
||||
/* If this is not a "ROWS BETWEEN ..." frame, then allocate registers to
|
||||
** store a copy of the current ORDER BY expressions. */
|
||||
if( pMWin->eType!=TK_ROWS ){
|
||||
int nPeer = (pOrderBy ? pOrderBy->nExpr : 0);
|
||||
regNewPeer = reg + pMWin->nBufferCol;
|
||||
if( pMWin->pPartition ) regNewPeer += pMWin->pPartition->nExpr;
|
||||
|
||||
regPeer = 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.end.reg = pParse->nMem+1; pParse->nMem += nPeer;
|
||||
}
|
||||
|
||||
assert( pMWin->eStart==TK_PRECEDING
|
||||
|| pMWin->eStart==TK_CURRENT
|
||||
|| pMWin->eStart==TK_FOLLOWING
|
||||
@@ -1835,13 +1837,13 @@ static void windowCodeStep(
|
||||
windowAggFinal(pParse, pMWin, 0);
|
||||
if( bCache ){
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, csrWrite, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, csrCurrent, 0, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, s.current.csr, 0, regRowid);
|
||||
windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, csrWrite, addrCacheRewind+1);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, csrCurrent, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, s.current.csr, 1);
|
||||
windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
|
||||
sqlite3VdbeAddOp1(v, OP_ResetSorter, csrCurrent);
|
||||
sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr);
|
||||
}
|
||||
addrShortcut = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
sqlite3VdbeJumpHere(v, addrGe);
|
||||
@@ -1852,10 +1854,20 @@ static void windowCodeStep(
|
||||
}
|
||||
|
||||
if( pMWin->eStart!=TK_UNBOUNDED ){
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, csrStart, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, s.start.csr, 1);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, s.current.csr, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, s.end.csr, 1);
|
||||
if( regPeer && pOrderBy ){
|
||||
if( bCache ){
|
||||
windowReadPeerValues(&s, csrWrite, regPeer);
|
||||
}else{
|
||||
sqlite3VdbeAddOp3(v, OP_Copy, regNewPeer, regPeer, pOrderBy->nExpr-1);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_Copy, regPeer, s.start.reg, pOrderBy->nExpr-1);
|
||||
sqlite3VdbeAddOp3(v, OP_Copy, regPeer, s.current.reg, pOrderBy->nExpr-1);
|
||||
sqlite3VdbeAddOp3(v, OP_Copy, regPeer, s.end.reg, pOrderBy->nExpr-1);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, csrCurrent, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, csrEnd, 1);
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regFirst);
|
||||
addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
@@ -1864,30 +1876,39 @@ static void windowCodeStep(
|
||||
VdbeModuleComment((pParse->pVdbe, "Begin windowCodeStep.SECOND_ROW_CODE"));
|
||||
if( bCache ){
|
||||
addrCacheNext = sqlite3VdbeCurrentAddr(v);
|
||||
if( pMWin->eType!=TK_ROWS ){
|
||||
windowReadPeerValues(&s, csrWrite, regNewPeer);
|
||||
}
|
||||
}else{
|
||||
sqlite3VdbeJumpHere(v, addrIf);
|
||||
}
|
||||
if( regPeer ){
|
||||
addrPeerJump = windowIfNewPeer(pParse, pOrderBy, regNewPeer, regPeer);
|
||||
}
|
||||
if( pMWin->eStart==TK_FOLLOWING ){
|
||||
windowCodeOp(&s, WINDOW_AGGSTEP, csrEnd, 0, 0);
|
||||
windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
|
||||
if( pMWin->eEnd!=TK_UNBOUNDED ){
|
||||
windowCodeOp(&s, WINDOW_RETURN_ROW, csrCurrent, regEnd, 0);
|
||||
windowCodeOp(&s, WINDOW_AGGINVERSE, csrStart, regStart, 0);
|
||||
windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 0);
|
||||
windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
|
||||
}
|
||||
}else
|
||||
if( pMWin->eEnd==TK_PRECEDING ){
|
||||
windowCodeOp(&s, WINDOW_AGGSTEP, csrEnd, regEnd, 0);
|
||||
windowCodeOp(&s, WINDOW_RETURN_ROW, csrCurrent, 0, 0);
|
||||
windowCodeOp(&s, WINDOW_AGGINVERSE, csrStart, regStart, 0);
|
||||
windowCodeOp(&s, WINDOW_AGGSTEP, regEnd, 0);
|
||||
windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
|
||||
windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
|
||||
}else{
|
||||
int addr;
|
||||
windowCodeOp(&s, WINDOW_AGGSTEP, csrEnd, 0, 0);
|
||||
windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
|
||||
if( pMWin->eEnd!=TK_UNBOUNDED ){
|
||||
if( regEnd ) addr = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0, 1);
|
||||
windowCodeOp(&s, WINDOW_RETURN_ROW, csrCurrent, 0, 0);
|
||||
windowCodeOp(&s, WINDOW_AGGINVERSE, csrStart, regStart, 0);
|
||||
windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
|
||||
windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
|
||||
if( regEnd ) sqlite3VdbeJumpHere(v, addr);
|
||||
}
|
||||
}
|
||||
if( addrPeerJump ){
|
||||
sqlite3VdbeJumpHere(v, addrPeerJump);
|
||||
}
|
||||
VdbeModuleComment((pParse->pVdbe, "End windowCodeStep.SECOND_ROW_CODE"));
|
||||
|
||||
/* End of the main input loop */
|
||||
@@ -1909,38 +1930,38 @@ static void windowCodeStep(
|
||||
VdbeModuleComment((pParse->pVdbe, "Begin windowCodeStep.FLUSH_CODE"));
|
||||
addrEmpty = sqlite3VdbeAddOp1(v, OP_Rewind, csrWrite);
|
||||
if( pMWin->eEnd==TK_PRECEDING ){
|
||||
windowCodeOp(&s, WINDOW_AGGSTEP, csrEnd, regEnd, 0);
|
||||
windowCodeOp(&s, WINDOW_RETURN_ROW, csrCurrent, 0, 0);
|
||||
windowCodeOp(&s, WINDOW_AGGSTEP, regEnd, 0);
|
||||
windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
|
||||
}else if( pMWin->eStart==TK_FOLLOWING ){
|
||||
int addrStart;
|
||||
int addrBreak1;
|
||||
int addrBreak2;
|
||||
int addrBreak3;
|
||||
windowCodeOp(&s, WINDOW_AGGSTEP, csrEnd, 0, 0);
|
||||
windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
|
||||
if( pMWin->eEnd==TK_UNBOUNDED ){
|
||||
addrStart = sqlite3VdbeCurrentAddr(v);
|
||||
addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, csrCurrent, regStart, 1);
|
||||
addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, csrStart, 0, 1);
|
||||
addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regStart, 1);
|
||||
addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, 0, 1);
|
||||
}else{
|
||||
assert( pMWin->eEnd==TK_FOLLOWING );
|
||||
addrStart = sqlite3VdbeCurrentAddr(v);
|
||||
addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, csrCurrent, regEnd, 1);
|
||||
addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, csrStart, regStart, 1);
|
||||
addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 1);
|
||||
addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 1);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart);
|
||||
sqlite3VdbeJumpHere(v, addrBreak2);
|
||||
addrStart = sqlite3VdbeCurrentAddr(v);
|
||||
addrBreak3 = windowCodeOp(&s, WINDOW_RETURN_ROW, csrCurrent, 0, 1);
|
||||
addrBreak3 = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart);
|
||||
sqlite3VdbeJumpHere(v, addrBreak1);
|
||||
sqlite3VdbeJumpHere(v, addrBreak3);
|
||||
}else{
|
||||
int addrBreak;
|
||||
int addrStart;
|
||||
windowCodeOp(&s, WINDOW_AGGSTEP, csrEnd, 0, 0);
|
||||
windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
|
||||
addrStart = sqlite3VdbeCurrentAddr(v);
|
||||
addrBreak = windowCodeOp(&s, WINDOW_RETURN_ROW, csrCurrent, 0, 1);
|
||||
windowCodeOp(&s, WINDOW_AGGINVERSE, csrStart, regStart, 0);
|
||||
addrBreak = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1);
|
||||
windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart);
|
||||
sqlite3VdbeJumpHere(v, addrBreak);
|
||||
}
|
||||
@@ -1948,7 +1969,7 @@ static void windowCodeStep(
|
||||
sqlite3VdbeJumpHere(v, addrEmpty);
|
||||
|
||||
if( bCache && addrShortcut>0 ) sqlite3VdbeJumpHere(v, addrShortcut);
|
||||
sqlite3VdbeAddOp1(v, OP_ResetSorter, csrCurrent);
|
||||
sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regSize);
|
||||
if( bCache==0 ) sqlite3VdbeAddOp2(v, OP_Integer, 1, pMWin->regFirst);
|
||||
VdbeModuleComment((pParse->pVdbe, "End windowCodeStep.FLUSH_CODE"));
|
||||
@@ -1958,370 +1979,6 @@ static void windowCodeStep(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This function does the work of sqlite3WindowCodeStep() for cases that
|
||||
** would normally be handled by windowCodeDefaultStep() when there are
|
||||
** one or more built-in window-functions that require the entire partition
|
||||
** to be cached in a temp table before any rows can be returned. Additionally.
|
||||
** "RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING" is always handled by
|
||||
** this function.
|
||||
**
|
||||
** Pseudo-code corresponding to the VM code generated by this function
|
||||
** for each type of window follows.
|
||||
**
|
||||
** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||||
**
|
||||
** flush_partition:
|
||||
** Once {
|
||||
** OpenDup (iEphCsr -> csrLead)
|
||||
** }
|
||||
** Integer ctr 0
|
||||
** foreach row (csrLead){
|
||||
** if( new peer ){
|
||||
** AggFinal (xValue)
|
||||
** for(i=0; i<ctr; i++){
|
||||
** Gosub addrGosub
|
||||
** Next iEphCsr
|
||||
** }
|
||||
** Integer ctr 0
|
||||
** }
|
||||
** AggStep (csrLead)
|
||||
** Incr ctr
|
||||
** }
|
||||
**
|
||||
** AggFinal (xFinalize)
|
||||
** for(i=0; i<ctr; i++){
|
||||
** Gosub addrGosub
|
||||
** Next iEphCsr
|
||||
** }
|
||||
**
|
||||
** ResetSorter (csr)
|
||||
** Return
|
||||
**
|
||||
** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||||
**
|
||||
** As above, except that the "if( new peer )" branch is always taken.
|
||||
**
|
||||
** RANGE BETWEEN CURRENT ROW AND CURRENT ROW
|
||||
**
|
||||
** As above, except that each of the for() loops becomes:
|
||||
**
|
||||
** for(i=0; i<ctr; i++){
|
||||
** Gosub addrGosub
|
||||
** AggInverse (iEphCsr)
|
||||
** Next iEphCsr
|
||||
** }
|
||||
**
|
||||
** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
|
||||
**
|
||||
** flush_partition:
|
||||
** Once {
|
||||
** OpenDup (iEphCsr -> csrLead)
|
||||
** }
|
||||
** foreach row (csrLead) {
|
||||
** AggStep (csrLead)
|
||||
** }
|
||||
** foreach row (iEphCsr) {
|
||||
** Gosub addrGosub
|
||||
** }
|
||||
**
|
||||
** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
|
||||
**
|
||||
** flush_partition:
|
||||
** Once {
|
||||
** OpenDup (iEphCsr -> csrLead)
|
||||
** }
|
||||
** foreach row (csrLead){
|
||||
** AggStep (csrLead)
|
||||
** }
|
||||
** Rewind (csrLead)
|
||||
** Integer ctr 0
|
||||
** foreach row (csrLead){
|
||||
** if( new peer ){
|
||||
** AggFinal (xValue)
|
||||
** for(i=0; i<ctr; i++){
|
||||
** Gosub addrGosub
|
||||
** AggInverse (iEphCsr)
|
||||
** Next iEphCsr
|
||||
** }
|
||||
** Integer ctr 0
|
||||
** }
|
||||
** Incr ctr
|
||||
** }
|
||||
**
|
||||
** AggFinal (xFinalize)
|
||||
** for(i=0; i<ctr; i++){
|
||||
** Gosub addrGosub
|
||||
** Next iEphCsr
|
||||
** }
|
||||
**
|
||||
** ResetSorter (csr)
|
||||
** Return
|
||||
*/
|
||||
static void windowCodeCacheStep(
|
||||
Parse *pParse,
|
||||
Select *p,
|
||||
WhereInfo *pWInfo,
|
||||
int regGosub,
|
||||
int addrGosub
|
||||
){
|
||||
Window *pMWin = p->pWin;
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
int k;
|
||||
int addr;
|
||||
ExprList *pPart = pMWin->pPartition;
|
||||
ExprList *pOrderBy = pMWin->pOrderBy;
|
||||
int nPeer = pOrderBy ? pOrderBy->nExpr : 0;
|
||||
int regNewPeer;
|
||||
|
||||
int addrGoto; /* Address of Goto used to jump flush_par.. */
|
||||
int addrNext; /* Jump here for next iteration of loop */
|
||||
int regFlushPart;
|
||||
int lblFlushPart;
|
||||
int csrLead;
|
||||
int regCtr;
|
||||
int regArg; /* Register array to martial function args */
|
||||
int regSize;
|
||||
int lblEmpty;
|
||||
int bReverse = pMWin->pOrderBy && pMWin->eStart==TK_CURRENT
|
||||
&& pMWin->eEnd==TK_UNBOUNDED;
|
||||
|
||||
assert( (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT)
|
||||
|| (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_UNBOUNDED)
|
||||
|| (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_CURRENT)
|
||||
|| (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED)
|
||||
);
|
||||
|
||||
lblEmpty = sqlite3VdbeMakeLabel(pParse);
|
||||
regNewPeer = pParse->nMem+1;
|
||||
pParse->nMem += nPeer;
|
||||
|
||||
/* Allocate register and label for the "flush_partition" sub-routine. */
|
||||
regFlushPart = ++pParse->nMem;
|
||||
lblFlushPart = sqlite3VdbeMakeLabel(pParse);
|
||||
|
||||
csrLead = pParse->nTab++;
|
||||
regCtr = ++pParse->nMem;
|
||||
|
||||
windowPartitionCache(pParse, p, pWInfo, regFlushPart, lblFlushPart, ®Size);
|
||||
addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
|
||||
/* Start of "flush_partition" */
|
||||
sqlite3VdbeResolveLabel(v, lblFlushPart);
|
||||
sqlite3VdbeAddOp2(v, OP_Once, 0, sqlite3VdbeCurrentAddr(v)+2);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_OpenDup, csrLead, pMWin->iEphCsr);
|
||||
|
||||
/* Initialize the accumulator register for each window function to NULL */
|
||||
regArg = windowInitAccum(pParse, pMWin);
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regCtr);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, csrLead, lblEmpty);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, lblEmpty);
|
||||
VdbeCoverageNeverTaken(v);
|
||||
|
||||
if( bReverse ){
|
||||
int addr2 = sqlite3VdbeCurrentAddr(v);
|
||||
windowAggStep(pParse, pMWin, csrLead, 0, regArg, regSize);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, csrLead, addr2);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, csrLead, lblEmpty);
|
||||
VdbeCoverageNeverTaken(v);
|
||||
}
|
||||
addrNext = sqlite3VdbeCurrentAddr(v);
|
||||
|
||||
if( pOrderBy && (pMWin->eEnd==TK_CURRENT || pMWin->eStart==TK_CURRENT) ){
|
||||
int bCurrent = (pMWin->eStart==TK_CURRENT);
|
||||
int addrJump = 0; /* Address of OP_Jump below */
|
||||
if( pMWin->eType==TK_RANGE ){
|
||||
int iOff = pMWin->nBufferCol + (pPart ? pPart->nExpr : 0);
|
||||
int regPeer = pMWin->regPart + (pPart ? pPart->nExpr : 0);
|
||||
KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0);
|
||||
for(k=0; k<nPeer; k++){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, csrLead, iOff+k, regNewPeer+k);
|
||||
}
|
||||
addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer, nPeer);
|
||||
sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
|
||||
addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_Copy, regNewPeer, regPeer, nPeer-1);
|
||||
}
|
||||
|
||||
windowReturnRows(pParse, pMWin, regCtr, regGosub, addrGosub,
|
||||
(bCurrent ? regArg : 0), (bCurrent ? regSize : 0)
|
||||
);
|
||||
if( addrJump ) sqlite3VdbeJumpHere(v, addrJump);
|
||||
}
|
||||
|
||||
if( bReverse==0 ){
|
||||
windowAggStep(pParse, pMWin, csrLead, 0, regArg, regSize);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regCtr, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, csrLead, addrNext);
|
||||
VdbeCoverage(v);
|
||||
|
||||
windowReturnRows(pParse, pMWin, regCtr, regGosub, addrGosub, 0, 0);
|
||||
|
||||
sqlite3VdbeResolveLabel(v, lblEmpty);
|
||||
sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
|
||||
sqlite3VdbeAddOp1(v, OP_Return, regFlushPart);
|
||||
|
||||
/* Jump to here to skip over flush_partition */
|
||||
sqlite3VdbeJumpHere(v, addrGoto);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||||
**
|
||||
** ...
|
||||
** if( new partition ){
|
||||
** AggFinal (xFinalize)
|
||||
** Gosub addrGosub
|
||||
** ResetSorter eph-table
|
||||
** }
|
||||
** else if( new peer ){
|
||||
** AggFinal (xValue)
|
||||
** Gosub addrGosub
|
||||
** ResetSorter eph-table
|
||||
** }
|
||||
** AggStep
|
||||
** Insert (record into eph-table)
|
||||
** sqlite3WhereEnd()
|
||||
** AggFinal (xFinalize)
|
||||
** Gosub addrGosub
|
||||
**
|
||||
** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
|
||||
**
|
||||
** As above, except take no action for a "new peer". Invoke
|
||||
** the sub-routine once only for each partition.
|
||||
**
|
||||
** RANGE BETWEEN CURRENT ROW AND CURRENT ROW
|
||||
**
|
||||
** As above, except that the "new peer" condition is handled in the
|
||||
** same way as "new partition" (so there is no "else if" block).
|
||||
**
|
||||
** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||||
**
|
||||
** As above, except assume every row is a "new peer".
|
||||
*/
|
||||
static void windowCodeDefaultStep(
|
||||
Parse *pParse,
|
||||
Select *p,
|
||||
WhereInfo *pWInfo,
|
||||
int regGosub,
|
||||
int addrGosub
|
||||
){
|
||||
Window *pMWin = p->pWin;
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
int k;
|
||||
int iSubCsr = p->pSrc->a[0].iCursor;
|
||||
int nSub = p->pSrc->a[0].pTab->nCol;
|
||||
int reg = pParse->nMem+1;
|
||||
int regRecord = reg+nSub;
|
||||
int regRowid = regRecord+1;
|
||||
int addr;
|
||||
ExprList *pPart = pMWin->pPartition;
|
||||
ExprList *pOrderBy = pMWin->pOrderBy;
|
||||
|
||||
assert( pMWin->eType==TK_RANGE
|
||||
|| (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT)
|
||||
);
|
||||
|
||||
assert( (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT)
|
||||
|| (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_UNBOUNDED)
|
||||
|| (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_CURRENT)
|
||||
|| (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED && !pOrderBy)
|
||||
);
|
||||
|
||||
if( pMWin->eEnd==TK_UNBOUNDED ){
|
||||
pOrderBy = 0;
|
||||
}
|
||||
|
||||
pParse->nMem += nSub + 2;
|
||||
|
||||
/* Load the individual column values of the row returned by
|
||||
** the sub-select into an array of registers. */
|
||||
for(k=0; k<nSub; k++){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iSubCsr, k, reg+k);
|
||||
}
|
||||
|
||||
/* Check if this is the start of a new partition or peer group. */
|
||||
if( pPart || pOrderBy ){
|
||||
int nPart = (pPart ? pPart->nExpr : 0);
|
||||
int addrGoto = 0;
|
||||
int addrJump = 0;
|
||||
int nPeer = (pOrderBy ? pOrderBy->nExpr : 0);
|
||||
|
||||
if( pPart ){
|
||||
int regNewPart = reg + pMWin->nBufferCol;
|
||||
KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0);
|
||||
addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart,nPart);
|
||||
sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
|
||||
addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
|
||||
VdbeCoverageEqNe(v);
|
||||
windowAggFinal(pParse, pMWin, 1);
|
||||
if( pOrderBy ){
|
||||
addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
}
|
||||
}
|
||||
|
||||
if( pOrderBy ){
|
||||
int regNewPeer = reg + pMWin->nBufferCol + nPart;
|
||||
int regPeer = pMWin->regPart + nPart;
|
||||
|
||||
if( addrJump ) sqlite3VdbeJumpHere(v, addrJump);
|
||||
if( pMWin->eType==TK_RANGE ){
|
||||
KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0);
|
||||
addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer, nPeer);
|
||||
sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
|
||||
addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
|
||||
VdbeCoverage(v);
|
||||
}else{
|
||||
addrJump = 0;
|
||||
}
|
||||
windowAggFinal(pParse, pMWin, pMWin->eStart==TK_CURRENT);
|
||||
if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto);
|
||||
}
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr,sqlite3VdbeCurrentAddr(v)+3);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)-1);
|
||||
VdbeCoverage(v);
|
||||
|
||||
sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
|
||||
sqlite3VdbeAddOp3(
|
||||
v, OP_Copy, reg+pMWin->nBufferCol, pMWin->regPart, nPart+nPeer-1
|
||||
);
|
||||
|
||||
if( addrJump ) sqlite3VdbeJumpHere(v, addrJump);
|
||||
}
|
||||
|
||||
/* Invoke step function for window functions */
|
||||
windowAggStep(pParse, pMWin, -1, 0, reg, 0);
|
||||
|
||||
/* Buffer the current row in the ephemeral table. */
|
||||
if( pMWin->nBufferCol>0 ){
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, pMWin->nBufferCol, regRecord);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Blob, 0, regRecord);
|
||||
sqlite3VdbeAppendP4(v, (void*)"", 0);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, pMWin->iEphCsr, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, pMWin->iEphCsr, regRecord, regRowid);
|
||||
|
||||
/* End the database scan loop. */
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
|
||||
windowAggFinal(pParse, pMWin, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr,sqlite3VdbeCurrentAddr(v)+3);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)-1);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate and return a duplicate of the Window object indicated by the
|
||||
@@ -2381,78 +2038,9 @@ void sqlite3WindowCodeStep(
|
||||
int regGosub, /* Register for OP_Gosub */
|
||||
int addrGosub /* OP_Gosub here to return each row */
|
||||
){
|
||||
Window *pMWin = p->pWin;
|
||||
|
||||
/* There are three different functions that may be used to do the work
|
||||
** of this one, depending on the window frame and the specific built-in
|
||||
** window functions used (if any).
|
||||
**
|
||||
** windowCodeRowExprStep() handles all "ROWS" window frames, except for:
|
||||
**
|
||||
** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||||
**
|
||||
** The exception is because windowCodeRowExprStep() implements all window
|
||||
** frame types by caching the entire partition in a temp table, and
|
||||
** "ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW" is easy enough to
|
||||
** implement without such a cache.
|
||||
**
|
||||
** windowCodeCacheStep() is used for:
|
||||
**
|
||||
** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
|
||||
**
|
||||
** It is also used for anything not handled by windowCodeRowExprStep()
|
||||
** that invokes a built-in window function that requires the entire
|
||||
** partition to be cached in a temp table before any rows are returned
|
||||
** (e.g. nth_value() or percent_rank()).
|
||||
**
|
||||
** Finally, assuming there is no built-in window function that requires
|
||||
** the partition to be cached, windowCodeDefaultStep() is used for:
|
||||
**
|
||||
** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||||
** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
|
||||
** RANGE BETWEEN CURRENT ROW AND CURRENT ROW
|
||||
** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||||
**
|
||||
** windowCodeDefaultStep() is the only one of the three functions that
|
||||
** does not cache each partition in a temp table before beginning to
|
||||
** return rows.
|
||||
*/
|
||||
if( pMWin->eType==TK_ROWS ){
|
||||
VdbeModuleComment((pParse->pVdbe, "Begin windowCodeStep()"));
|
||||
windowCodeStep(pParse, p, pWInfo, regGosub, addrGosub);
|
||||
VdbeModuleComment((pParse->pVdbe, "End windowCodeStep()"));
|
||||
}else{
|
||||
Window *pWin;
|
||||
int bCache = 0; /* True to use CacheStep() */
|
||||
|
||||
if( pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED ){
|
||||
bCache = 1;
|
||||
}else{
|
||||
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
||||
FuncDef *pFunc = pWin->pFunc;
|
||||
if( (pFunc->funcFlags & SQLITE_FUNC_WINDOW_SIZE)
|
||||
|| (pFunc->zName==nth_valueName)
|
||||
|| (pFunc->zName==first_valueName)
|
||||
|| (pFunc->zName==leadName)
|
||||
|| (pFunc->zName==lagName)
|
||||
){
|
||||
bCache = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise, call windowCodeDefaultStep(). */
|
||||
if( bCache ){
|
||||
VdbeModuleComment((pParse->pVdbe, "Begin CacheStep()"));
|
||||
windowCodeCacheStep(pParse, p, pWInfo, regGosub, addrGosub);
|
||||
VdbeModuleComment((pParse->pVdbe, "End CacheStep()"));
|
||||
}else{
|
||||
VdbeModuleComment((pParse->pVdbe, "Begin DefaultStep()"));
|
||||
windowCodeDefaultStep(pParse, p, pWInfo, regGosub, addrGosub);
|
||||
VdbeModuleComment((pParse->pVdbe, "End DefaultStep()"));
|
||||
}
|
||||
}
|
||||
VdbeModuleComment((pParse->pVdbe, "Begin windowCodeStep()"));
|
||||
windowCodeStep(pParse, p, pWInfo, regGosub, addrGosub);
|
||||
VdbeModuleComment((pParse->pVdbe, "End windowCodeStep()"));
|
||||
}
|
||||
|
||||
#endif /* SQLITE_OMIT_WINDOWFUNC */
|
||||
|
Reference in New Issue
Block a user