return (pList ? pList->nExpr : 0);
}
+typedef struct WindowCodeArg WindowCodeArg;
+typedef struct WindowCsrAndReg WindowCsrAndReg;
+
+/*
+** See comments above struct WindowCodeArg.
+*/
+struct WindowCsrAndReg {
+ int csr; /* Cursor number */
+ int reg; /* First in array of peer values */
+};
+
+/*
+** A single instance of this structure is allocated on the stack by
+** sqlite3WindowCodeStep() and a pointer to it passed to the various helper
+** routines. This is to reduce the number of arguments required by each
+** helper function.
+**
+** regArg:
+** Each window function requires an accumulator register (just as an
+** ordinary aggregate function does). This variable is set to the first
+** in an array of accumulator registers - one for each window function
+** in the WindowCodeArg.pMWin list.
+**
+** eDelete:
+** The window functions implementation sometimes caches the input rows
+** that it processes in a temporary table. If it is not zero, this
+** variable indicates when rows may be removed from the temp table (in
+** order to reduce memory requirements - it would always be safe just
+** to leave them there). Possible values for eDelete are:
+**
+** WINDOW_RETURN_ROW:
+** An input row can be discarded after it is returned to the caller.
+**
+** WINDOW_AGGINVERSE:
+** An input row can be discarded after the window functions xInverse()
+** callbacks have been invoked in it.
+**
+** WINDOW_AGGSTEP:
+** An input row can be discarded after the window functions xStep()
+** callbacks have been invoked in it.
+**
+** start,current,end
+** Consider a window-frame similar to the following:
+**
+** (ORDER BY a, b GROUPS BETWEEN 2 PRECEDING AND 2 FOLLOWING)
+**
+** The windows functions implmentation caches the input rows in a temp
+** table, sorted by "a, b" (it actually populates the cache lazily, and
+** aggressively removes rows once they are no longer required, but that's
+** a mere detail). It keeps three cursors open on the temp table. One
+** (current) that points to the next row to return to the query engine
+** once its window function values have been calculated. Another (end)
+** points to the next row to call the xStep() method of each window function
+** on (so that it is 2 groups ahead of current). And a third (start) that
+** points to the next row to call the xInverse() method of each window
+** function on.
+**
+** Each cursor (start, current and end) consists of a VDBE cursor
+** (WindowCsrAndReg.csr) and an array of registers (starting at
+** WindowCodeArg.reg) that always contains a copy of the peer values
+** read from the corresponding cursor.
+**
+** Depending on the window-frame in question, all three cursors may not
+** be required. In this case both WindowCodeArg.csr and reg are set to
+** 0.
+*/
+struct WindowCodeArg {
+ Parse *pParse; /* Parse context */
+ Window *pMWin; /* First in list of functions being processed */
+ Vdbe *pVdbe; /* VDBE object */
+ int addrGosub; /* OP_Gosub to this address to return one row */
+ int regGosub; /* Register used with OP_Gosub(addrGosub) */
+ int regArg; /* First in array of accumulator registers */
+ int eDelete; /* See above */
+ int regStart; /* Value of <expr> PRECEDING */
+ int regEnd; /* Value of <expr> FOLLOWING */
+
+ WindowCsrAndReg start;
+ WindowCsrAndReg current;
+ WindowCsrAndReg end;
+};
+
+/*
+** 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);
+ }
+ }
+}
+
/*
** Generate VM code to invoke either xStep() (if bInverse is 0) or
** xInverse (if bInverse is non-zero) for each window function in the
** number of rows in the current partition.
*/
static void windowAggStep(
- Parse *pParse,
+ WindowCodeArg *p,
Window *pMWin, /* Linked list of window functions */
int csr, /* Read arguments from this cursor */
int bInverse, /* True to invoke xInverse instead of xStep */
int reg /* Array of registers */
){
+ Parse *pParse = p->pParse;
Vdbe *v = sqlite3GetVdbe(pParse);
Window *pWin;
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1-bInverse, 1);
}else if( pFunc->xSFunc!=noopStepFunc ){
int addrIf = 0;
+ int addrIf2 = 0;
if( pWin->pFilter ){
int regTmp;
assert( pWin->bExprArgs || !nArg ||nArg==pWin->pOwner->x.pList->nExpr );
VdbeCoverage(v);
sqlite3ReleaseTempReg(pParse, regTmp);
}
+
+
+ /* If this is a (RANGE BETWEEN a PRECEDING AND b PRECEDING) or a
+ ** (RANGE BETWEEN b FOLLOWING AND a FOLLOWING) frame and (b > a),
+ ** omit the OP_AggStep or OP_AggInverse if the peer value is numeric.
+ ** A numeric peer value is one for which the following is true:
+ **
+ ** (peer IS NOT NULL AND peer < '')
+ */
+ if( pWin->eFrmType==TK_RANGE
+ && pWin->eStart==pWin->eEnd
+ && pWin->eStart==TK_PRECEDING
+ ){
+ int op = ((pMWin->eStart==TK_FOLLOWING) ? OP_Ge : OP_Le);
+ int regPeer = sqlite3GetTempReg(pParse);
+ int regString = sqlite3GetTempReg(pParse);
+ int lbl = sqlite3VdbeMakeLabel(pParse);
+ VdbeModuleComment((v, "windowAggStep \"peer is numeric?\" test"));
+ sqlite3VdbeAddOp3(v, op, p->regStart, lbl, p->regEnd);
+ VdbeCoverageNeverNullIf(v, op==OP_Ge); /* NeverNull because <expr> */
+ VdbeCoverageNeverNullIf(v, op==OP_Le); /* values previously checked */
+ windowReadPeerValues(p, csr, regPeer);
+ sqlite3VdbeAddOp2(v, OP_IsNull, regPeer, lbl);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC);
+ addrIf2 = sqlite3VdbeAddOp3(v, OP_Lt, regString, 0, regPeer);
+ sqlite3ReleaseTempReg(pParse, regPeer);
+ sqlite3ReleaseTempReg(pParse, regString);
+ sqlite3VdbeResolveLabel(v, lbl);
+ VdbeModuleComment((v, "windowAggStep end \"peer is numeric?\""));
+ assert( pWin->eStart==TK_PRECEDING || pWin->eStart==TK_FOLLOWING );
+ assert( pMWin->pOrderBy && pMWin->pOrderBy->nExpr==1 );
+ }
+
if( pWin->bExprArgs ){
int iStart = sqlite3VdbeCurrentAddr(v);
VdbeOp *pOp, *pEnd;
sqlite3ReleaseTempRange(pParse, regArg, nArg);
}
if( addrIf ) sqlite3VdbeJumpHere(v, addrIf);
+ if( addrIf2 ) sqlite3VdbeJumpHere(v, addrIf2);
}
}
}
-typedef struct WindowCodeArg WindowCodeArg;
-typedef struct WindowCsrAndReg WindowCsrAndReg;
-
-/*
-** See comments above struct WindowCodeArg.
-*/
-struct WindowCsrAndReg {
- int csr; /* Cursor number */
- int reg; /* First in array of peer values */
-};
-
-/*
-** A single instance of this structure is allocated on the stack by
-** sqlite3WindowCodeStep() and a pointer to it passed to the various helper
-** routines. This is to reduce the number of arguments required by each
-** helper function.
-**
-** regArg:
-** Each window function requires an accumulator register (just as an
-** ordinary aggregate function does). This variable is set to the first
-** in an array of accumulator registers - one for each window function
-** in the WindowCodeArg.pMWin list.
-**
-** eDelete:
-** The window functions implementation sometimes caches the input rows
-** that it processes in a temporary table. If it is not zero, this
-** variable indicates when rows may be removed from the temp table (in
-** order to reduce memory requirements - it would always be safe just
-** to leave them there). Possible values for eDelete are:
-**
-** WINDOW_RETURN_ROW:
-** An input row can be discarded after it is returned to the caller.
-**
-** WINDOW_AGGINVERSE:
-** An input row can be discarded after the window functions xInverse()
-** callbacks have been invoked in it.
-**
-** WINDOW_AGGSTEP:
-** An input row can be discarded after the window functions xStep()
-** callbacks have been invoked in it.
-**
-** start,current,end
-** Consider a window-frame similar to the following:
-**
-** (ORDER BY a, b GROUPS BETWEEN 2 PRECEDING AND 2 FOLLOWING)
-**
-** The windows functions implmentation caches the input rows in a temp
-** table, sorted by "a, b" (it actually populates the cache lazily, and
-** aggressively removes rows once they are no longer required, but that's
-** a mere detail). It keeps three cursors open on the temp table. One
-** (current) that points to the next row to return to the query engine
-** once its window function values have been calculated. Another (end)
-** points to the next row to call the xStep() method of each window function
-** on (so that it is 2 groups ahead of current). And a third (start) that
-** points to the next row to call the xInverse() method of each window
-** function on.
-**
-** Each cursor (start, current and end) consists of a VDBE cursor
-** (WindowCsrAndReg.csr) and an array of registers (starting at
-** WindowCodeArg.reg) that always contains a copy of the peer values
-** read from the corresponding cursor.
-**
-** Depending on the window-frame in question, all three cursors may not
-** be required. In this case both WindowCodeArg.csr and reg are set to
-** 0.
-*/
-struct WindowCodeArg {
- Parse *pParse; /* Parse context */
- Window *pMWin; /* First in list of functions being processed */
- Vdbe *pVdbe; /* VDBE object */
- int addrGosub; /* OP_Gosub to this address to return one row */
- int regGosub; /* Register used with OP_Gosub(addrGosub) */
- int regArg; /* First in array of accumulator registers */
- int eDelete; /* See above */
-
- WindowCsrAndReg start;
- WindowCsrAndReg current;
- WindowCsrAndReg end;
-};
-
/*
** Values that may be passed as the second argument to windowCodeOp().
*/
#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);
- }
- }
-}
-
/*
** Generate VM code to invoke either xValue() (bFin==0) or xFinalize()
** (bFin==1) for each window function in the linked list starting at
int addrNext;
int csr;
+ VdbeModuleComment((v, "windowFullScan begin"));
+
assert( pMWin!=0 );
csr = pMWin->csrApp;
nPeer = (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0);
if( addrEq ) sqlite3VdbeJumpHere(v, addrEq);
}
- windowAggStep(pParse, pMWin, csr, 0, p->regArg);
+ windowAggStep(p, pMWin, csr, 0, p->regArg);
sqlite3VdbeResolveLabel(v, lblNext);
sqlite3VdbeAddOp2(v, OP_Next, csr, addrNext);
}
windowAggFinal(p, 1);
+ VdbeModuleComment((v, "windowFullScan end"));
}
/*
Window *pMWin = p->pMWin;
int ret = 0;
Vdbe *v = p->pVdbe;
- int addrIf = 0;
int addrContinue = 0;
- int addrGoto = 0;
int bPeer = (pMWin->eFrmType!=TK_ROWS);
int lblDone = sqlite3VdbeMakeLabel(pParse);
);
}
}else{
- addrIf = sqlite3VdbeAddOp3(v, OP_IfPos, regCountdown, 0, 1);
+ sqlite3VdbeAddOp3(v, OP_IfPos, regCountdown, lblDone, 1);
VdbeCoverage(v);
}
}
assert( pMWin->regEndRowid );
sqlite3VdbeAddOp2(v, OP_AddImm, pMWin->regStartRowid, 1);
}else{
- windowAggStep(pParse, pMWin, csr, 1, p->regArg);
+ windowAggStep(p, pMWin, csr, 1, p->regArg);
}
break;
assert( pMWin->regEndRowid );
sqlite3VdbeAddOp2(v, OP_AddImm, pMWin->regEndRowid, 1);
}else{
- windowAggStep(pParse, pMWin, csr, 0, p->regArg);
+ windowAggStep(p, pMWin, csr, 0, p->regArg);
}
break;
}
sqlite3VdbeAddOp2(v, OP_Next, csr, sqlite3VdbeCurrentAddr(v)+1+bPeer);
VdbeCoverage(v);
if( bPeer ){
- addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, lblDone);
}
}
if( bPeer ){
int nReg = (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0);
- int regTmp = (nReg ? sqlite3GetTempRange(pParse, nReg) : 0);
+ int regTmp;
+
+ /* If this is a (RANGE BETWEEN a FOLLOWING AND b FOLLOWING), ensure
+ ** the start cursor does not advance past the end cursor within the
+ ** temporary table. It otherwise might, if (a>b). */
+ if( pMWin->eStart==TK_FOLLOWING && pMWin->eEnd==TK_FOLLOWING
+ && pMWin->eFrmType==TK_RANGE && op==WINDOW_AGGINVERSE
+ ){
+ int regRowid1 = sqlite3GetTempReg(pParse);
+ int regRowid2 = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp2(v, OP_Rowid, p->start.csr, regRowid1);
+ sqlite3VdbeAddOp2(v, OP_Rowid, p->end.csr, regRowid2);
+ sqlite3VdbeAddOp3(v, OP_Ge, regRowid2, lblDone, regRowid1);
+ sqlite3ReleaseTempReg(pParse, regRowid1);
+ sqlite3ReleaseTempReg(pParse, regRowid2);
+ }
+ regTmp = (nReg ? sqlite3GetTempRange(pParse, nReg) : 0);
windowReadPeerValues(p, csr, regTmp);
windowIfNewPeer(pParse, pMWin->pOrderBy, regTmp, reg, addrContinue);
sqlite3ReleaseTempRange(pParse, regTmp, nReg);
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNextRange);
}
sqlite3VdbeResolveLabel(v, lblDone);
- if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto);
- if( addrIf ) sqlite3VdbeJumpHere(v, addrIf);
return ret;
}
** regEnd = <expr2>
** regStart = <expr1>
** }else{
-** if( (csrEnd.key + regEnd) <= csrCurrent.key ){
+** while( (csrEnd.key + regEnd) <= csrCurrent.key ){
** AGGSTEP
** }
+** RETURN_ROW
** while( (csrStart.key + regStart) < csrCurrent.key ){
** AGGINVERSE
** }
-** RETURN_ROW
** }
** }
** flush:
** while( (csrEnd.key + regEnd) <= csrCurrent.key ){
** AGGSTEP
** }
+** RETURN_ROW
** while( (csrStart.key + regStart) < csrCurrent.key ){
** AGGINVERSE
** }
-** RETURN_ROW
**
** RANGE BETWEEN <expr1> FOLLOWING AND <expr2> FOLLOWING
**
int addrGosubFlush = 0; /* Address of OP_Gosub to flush: */
int addrInteger = 0; /* Address of OP_Integer */
int addrEmpty; /* Address of OP_Rewind in flush: */
- int regStart = 0; /* Value of <expr> PRECEDING */
- int regEnd = 0; /* Value of <expr> FOLLOWING */
int regNew; /* Array of registers holding new input row */
int regRecord; /* regNew array in record form */
int regRowid; /* Rowid for regRecord in eph table */
** clause, allocate registers to store the results of evaluating each
** <expr>. */
if( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_FOLLOWING ){
- regStart = ++pParse->nMem;
+ s.regStart = ++pParse->nMem;
}
if( pMWin->eEnd==TK_PRECEDING || pMWin->eEnd==TK_FOLLOWING ){
- regEnd = ++pParse->nMem;
+ s.regEnd = ++pParse->nMem;
}
/* If this is not a "ROWS BETWEEN ..." frame, then allocate arrays of
/* This block is run for the first row of each partition */
s.regArg = windowInitAccum(pParse, pMWin);
- if( regStart ){
- sqlite3ExprCode(pParse, pMWin->pStart, regStart);
- windowCheckValue(pParse, regStart, 0 + (pMWin->eFrmType==TK_RANGE ? 3 : 0));
+ if( s.regStart ){
+ sqlite3ExprCode(pParse, pMWin->pStart, s.regStart);
+ windowCheckValue(pParse, s.regStart, 0 + (pMWin->eFrmType==TK_RANGE?3:0));
}
- if( regEnd ){
- sqlite3ExprCode(pParse, pMWin->pEnd, regEnd);
- windowCheckValue(pParse, regEnd, 1 + (pMWin->eFrmType==TK_RANGE ? 3 : 0));
+ if( s.regEnd ){
+ sqlite3ExprCode(pParse, pMWin->pEnd, s.regEnd);
+ windowCheckValue(pParse, s.regEnd, 1 + (pMWin->eFrmType==TK_RANGE?3:0));
}
- if( pMWin->eFrmType!=TK_RANGE && pMWin->eStart==pMWin->eEnd && regStart ){
+ if( pMWin->eFrmType!=TK_RANGE && pMWin->eStart==pMWin->eEnd && s.regStart ){
int op = ((pMWin->eStart==TK_FOLLOWING) ? OP_Ge : OP_Le);
- int addrGe = sqlite3VdbeAddOp3(v, op, regStart, 0, regEnd);
+ int addrGe = sqlite3VdbeAddOp3(v, op, s.regStart, 0, s.regEnd);
VdbeCoverageNeverNullIf(v, op==OP_Ge); /* NeverNull because bound <expr> */
VdbeCoverageNeverNullIf(v, op==OP_Le); /* values previously checked */
windowAggFinal(&s, 0);
sqlite3VdbeAddOp2(v, OP_Goto, 0, lblWhereEnd);
sqlite3VdbeJumpHere(v, addrGe);
}
- if( pMWin->eStart==TK_FOLLOWING && pMWin->eFrmType!=TK_RANGE && regEnd ){
+ if( pMWin->eStart==TK_FOLLOWING && pMWin->eFrmType!=TK_RANGE && s.regEnd ){
assert( pMWin->eEnd==TK_FOLLOWING );
- sqlite3VdbeAddOp3(v, OP_Subtract, regStart, regEnd, regStart);
+ sqlite3VdbeAddOp3(v, OP_Subtract, s.regStart, s.regEnd, s.regStart);
}
if( pMWin->eStart!=TK_UNBOUNDED ){
if( pMWin->eFrmType==TK_RANGE ){
int lbl = sqlite3VdbeMakeLabel(pParse);
int addrNext = sqlite3VdbeCurrentAddr(v);
- windowCodeRangeTest(&s, OP_Ge, s.current.csr, regEnd, s.end.csr, lbl);
- windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ windowCodeRangeTest(&s, OP_Ge, s.current.csr, s.regEnd, s.end.csr, lbl);
+ windowCodeOp(&s, WINDOW_AGGINVERSE, s.regStart, 0);
windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNext);
sqlite3VdbeResolveLabel(v, lbl);
}else{
- windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 0);
- windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ windowCodeOp(&s, WINDOW_RETURN_ROW, s.regEnd, 0);
+ windowCodeOp(&s, WINDOW_AGGINVERSE, s.regStart, 0);
}
}
}else
if( pMWin->eEnd==TK_PRECEDING ){
int bRPS = (pMWin->eStart==TK_PRECEDING && pMWin->eFrmType==TK_RANGE);
- windowCodeOp(&s, WINDOW_AGGSTEP, regEnd, 0);
- if( bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ windowCodeOp(&s, WINDOW_AGGSTEP, s.regEnd, 0);
+ if( bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, s.regStart, 0);
windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
- if( !bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ if( !bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, s.regStart, 0);
}else{
int addr = 0;
windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
if( pMWin->eFrmType==TK_RANGE ){
int lbl = 0;
addr = sqlite3VdbeCurrentAddr(v);
- if( regEnd ){
+ if( s.regEnd ){
lbl = sqlite3VdbeMakeLabel(pParse);
- windowCodeRangeTest(&s, OP_Ge, s.current.csr, regEnd, s.end.csr, lbl);
+ windowCodeRangeTest(&s, OP_Ge, s.current.csr,s.regEnd,s.end.csr,lbl);
}
windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
- windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
- if( regEnd ){
+ windowCodeOp(&s, WINDOW_AGGINVERSE, s.regStart, 0);
+ if( s.regEnd ){
sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
sqlite3VdbeResolveLabel(v, lbl);
}
}else{
- if( regEnd ){
- addr = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0, 1);
+ if( s.regEnd ){
+ addr = sqlite3VdbeAddOp3(v, OP_IfPos, s.regEnd, 0, 1);
VdbeCoverage(v);
}
windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
- windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
- if( regEnd ) sqlite3VdbeJumpHere(v, addr);
+ windowCodeOp(&s, WINDOW_AGGINVERSE, s.regStart, 0);
+ if( s.regEnd ) sqlite3VdbeJumpHere(v, addr);
}
}
}
VdbeCoverage(v);
if( pMWin->eEnd==TK_PRECEDING ){
int bRPS = (pMWin->eStart==TK_PRECEDING && pMWin->eFrmType==TK_RANGE);
- windowCodeOp(&s, WINDOW_AGGSTEP, regEnd, 0);
- if( bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ windowCodeOp(&s, WINDOW_AGGSTEP, s.regEnd, 0);
+ if( bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, s.regStart, 0);
windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
}else if( pMWin->eStart==TK_FOLLOWING ){
int addrStart;
windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
if( pMWin->eFrmType==TK_RANGE ){
addrStart = sqlite3VdbeCurrentAddr(v);
- addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 1);
+ addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, s.regStart, 1);
addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1);
}else
if( pMWin->eEnd==TK_UNBOUNDED ){
addrStart = sqlite3VdbeCurrentAddr(v);
- addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regStart, 1);
+ addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, s.regStart, 1);
addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, 0, 1);
}else{
assert( pMWin->eEnd==TK_FOLLOWING );
addrStart = sqlite3VdbeCurrentAddr(v);
- addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 1);
- addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 1);
+ addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, s.regEnd, 1);
+ addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, s.regStart, 1);
}
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart);
sqlite3VdbeJumpHere(v, addrBreak2);
windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
addrStart = sqlite3VdbeCurrentAddr(v);
addrBreak = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1);
- windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ windowCodeOp(&s, WINDOW_AGGINVERSE, s.regStart, 0);
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart);
sqlite3VdbeJumpHere(v, addrBreak);
}