mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-07 02:42:48 +03:00
Bug fixes for window frames of the form (... RANGE BETWEEN b PRECEDING AND a PRECEDING) or (... RANGE a FOLLOWING AND b FOLLOWING) where (a > b).
FossilOrigin-Name: 040e196a8be3ca41b9365310ab88c2a3cc84b918a6511c77a6d95d4b4e0da3ed
This commit is contained in:
16
manifest
16
manifest
@@ -1,5 +1,5 @@
|
|||||||
C Omit\sthe\ssqlite3IntTokens\sarray\sconstant\sfor\sa\scode\ssimplification.
|
C Bug\sfixes\sfor\swindow\sframes\sof\sthe\sform\s(...\sRANGE\sBETWEEN\sb\sPRECEDING\sAND\sa\sPRECEDING)\sor\s(...\sRANGE\sa\sFOLLOWING\sAND\sb\sFOLLOWING)\swhere\s(a\s>\sb).
|
||||||
D 2019-09-23T12:38:10.519
|
D 2019-09-24T20:20:05.843
|
||||||
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 LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||||
@@ -615,7 +615,7 @@ F src/where.c 9f3f23efc45934e7b7ea6c0c1042420b73053e7c3264feef6faf9ce6fbd5df61
|
|||||||
F src/whereInt.h 2c6bae136a7c0be6ff75dc36950d1968c67d005c8e51d7a9d77cb996bb4843d9
|
F src/whereInt.h 2c6bae136a7c0be6ff75dc36950d1968c67d005c8e51d7a9d77cb996bb4843d9
|
||||||
F src/wherecode.c 535c8e228478fd971b9a5b6cb6773995b0fbf7020d5989508a5094ce5b8cd95b
|
F src/wherecode.c 535c8e228478fd971b9a5b6cb6773995b0fbf7020d5989508a5094ce5b8cd95b
|
||||||
F src/whereexpr.c 05c283d26aa9c3f5d1bf13a5f6a573b43295b9db280eff18e26f97d7d7f119b4
|
F src/whereexpr.c 05c283d26aa9c3f5d1bf13a5f6a573b43295b9db280eff18e26f97d7d7f119b4
|
||||||
F src/window.c af649dd0627e925b4ad0d6ae9fd12c3d4ececdb8b872a1785a28da41fcc06148
|
F src/window.c 1dcacaeee25487eb222f9c5e226eb6821265a22a71432f570b3c41d9754717ba
|
||||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||||
F test/affinity2.test da465d3d490ab24ef64f7715b5953343a4967762b9350b29eb1462879ff3fb9e
|
F test/affinity2.test da465d3d490ab24ef64f7715b5953343a4967762b9350b29eb1462879ff3fb9e
|
||||||
F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
|
F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
|
||||||
@@ -1721,7 +1721,7 @@ F test/window8.tcl f2711aa3571e4e6b0dad98db8d95fd6cb8d9db0c92bbdf535f153b07606a1
|
|||||||
F test/window8.test c4331b27a6f66d69fa8f8bab10cc731db1a81d293ae108a68f7c3487fa94e65b
|
F test/window8.test c4331b27a6f66d69fa8f8bab10cc731db1a81d293ae108a68f7c3487fa94e65b
|
||||||
F test/window9.test 20a6b590be718b6bc98a5356d4396d6cdf19329c547da084fa225b92d68e1693
|
F test/window9.test 20a6b590be718b6bc98a5356d4396d6cdf19329c547da084fa225b92d68e1693
|
||||||
F test/windowA.test 6d63dc1260daa17141a55007600581778523a8b420629f1282d2acfc36af23be
|
F test/windowA.test 6d63dc1260daa17141a55007600581778523a8b420629f1282d2acfc36af23be
|
||||||
F test/windowB.test be2ecc1298021e87eef375af35906288cc929fc21f2651139f7dd50a5844b120
|
F test/windowB.test f9a79e1bd669d513c6cb2a7b448773f7954b8e35e2b843b6c371475402b412b0
|
||||||
F test/windowerr.tcl f5acd6fbc210d7b5546c0e879d157888455cd4a17a1d3f28f07c1c8a387019e0
|
F test/windowerr.tcl f5acd6fbc210d7b5546c0e879d157888455cd4a17a1d3f28f07c1c8a387019e0
|
||||||
F test/windowerr.test a8b752402109c15aa1c5efe1b93ccb0ce1ef84fa964ae1cd6684dd0b3cc1819b
|
F test/windowerr.test a8b752402109c15aa1c5efe1b93ccb0ce1ef84fa964ae1cd6684dd0b3cc1819b
|
||||||
F test/windowfault.test a90b397837209f15e54afa62e8be39b2759a0101fae04e05a08bcc50e243a452
|
F test/windowfault.test a90b397837209f15e54afa62e8be39b2759a0101fae04e05a08bcc50e243a452
|
||||||
@@ -1845,7 +1845,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 82e5dcf5c1d500ed82f398b38fdae0f30033804e897fbab3c10f1e15e2abedef
|
P f907395ef5a2dc1d084b6a286af00de4c742cf12d4f347c21e1b757786508f57
|
||||||
R 6576418b2448b3d64f3c208d7dfcd054
|
R 0b002338bf10d899e34ef318ab4f702e
|
||||||
U drh
|
U dan
|
||||||
Z be0d84726c6176673fd11b7076b1f111
|
Z a41a183eaebde05b792c6177f74029dd
|
||||||
|
@@ -1 +1 @@
|
|||||||
f907395ef5a2dc1d084b6a286af00de4c742cf12d4f347c21e1b757786508f57
|
040e196a8be3ca41b9365310ab88c2a3cc84b918a6511c77a6d95d4b4e0da3ed
|
391
src/window.c
391
src/window.c
@@ -1408,120 +1408,6 @@ static int windowArgCount(Window *pWin){
|
|||||||
return (pList ? pList->nExpr : 0);
|
return (pList ? pList->nExpr : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
** Generate VM code to invoke either xStep() (if bInverse is 0) or
|
|
||||||
** xInverse (if bInverse is non-zero) for each window function in the
|
|
||||||
** linked list starting at pMWin. Or, for built-in window functions
|
|
||||||
** that do not use the standard function API, generate the required
|
|
||||||
** inline VM code.
|
|
||||||
**
|
|
||||||
** If argument csr is greater than or equal to 0, then argument reg is
|
|
||||||
** the first register in an array of registers guaranteed to be large
|
|
||||||
** enough to hold the array of arguments for each function. In this case
|
|
||||||
** the arguments are extracted from the current row of csr into the
|
|
||||||
** array of registers before invoking OP_AggStep or OP_AggInverse
|
|
||||||
**
|
|
||||||
** Or, if csr is less than zero, then the array of registers at reg is
|
|
||||||
** already populated with all columns from the current row of the sub-query.
|
|
||||||
**
|
|
||||||
** If argument regPartSize is non-zero, then it is a register containing the
|
|
||||||
** number of rows in the current partition.
|
|
||||||
*/
|
|
||||||
static void windowAggStep(
|
|
||||||
Parse *pParse,
|
|
||||||
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 */
|
|
||||||
){
|
|
||||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
|
||||||
Window *pWin;
|
|
||||||
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
|
||||||
FuncDef *pFunc = pWin->pFunc;
|
|
||||||
int regArg;
|
|
||||||
int nArg = pWin->bExprArgs ? 0 : windowArgCount(pWin);
|
|
||||||
int i;
|
|
||||||
|
|
||||||
assert( bInverse==0 || pWin->eStart!=TK_UNBOUNDED );
|
|
||||||
|
|
||||||
for(i=0; i<nArg; i++){
|
|
||||||
if( i!=1 || pFunc->zName!=nth_valueName ){
|
|
||||||
sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+i, reg+i);
|
|
||||||
}else{
|
|
||||||
sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, pWin->iArgCol+i, reg+i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
regArg = reg;
|
|
||||||
|
|
||||||
if( pMWin->regStartRowid==0
|
|
||||||
&& (pFunc->funcFlags & SQLITE_FUNC_MINMAX)
|
|
||||||
&& (pWin->eStart!=TK_UNBOUNDED)
|
|
||||||
){
|
|
||||||
int addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regArg);
|
|
||||||
VdbeCoverage(v);
|
|
||||||
if( bInverse==0 ){
|
|
||||||
sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1, 1);
|
|
||||||
sqlite3VdbeAddOp2(v, OP_SCopy, regArg, pWin->regApp);
|
|
||||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, pWin->regApp, 2, pWin->regApp+2);
|
|
||||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, pWin->csrApp, pWin->regApp+2);
|
|
||||||
}else{
|
|
||||||
sqlite3VdbeAddOp4Int(v, OP_SeekGE, pWin->csrApp, 0, regArg, 1);
|
|
||||||
VdbeCoverageNeverTaken(v);
|
|
||||||
sqlite3VdbeAddOp1(v, OP_Delete, pWin->csrApp);
|
|
||||||
sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
|
|
||||||
}
|
|
||||||
sqlite3VdbeJumpHere(v, addrIsNull);
|
|
||||||
}else if( pWin->regApp ){
|
|
||||||
assert( pFunc->zName==nth_valueName
|
|
||||||
|| pFunc->zName==first_valueName
|
|
||||||
);
|
|
||||||
assert( bInverse==0 || bInverse==1 );
|
|
||||||
sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1-bInverse, 1);
|
|
||||||
}else if( pFunc->xSFunc!=noopStepFunc ){
|
|
||||||
int addrIf = 0;
|
|
||||||
if( pWin->pFilter ){
|
|
||||||
int regTmp;
|
|
||||||
assert( pWin->bExprArgs || !nArg ||nArg==pWin->pOwner->x.pList->nExpr );
|
|
||||||
assert( pWin->bExprArgs || nArg ||pWin->pOwner->x.pList==0 );
|
|
||||||
regTmp = sqlite3GetTempReg(pParse);
|
|
||||||
sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp);
|
|
||||||
addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1);
|
|
||||||
VdbeCoverage(v);
|
|
||||||
sqlite3ReleaseTempReg(pParse, regTmp);
|
|
||||||
}
|
|
||||||
if( pWin->bExprArgs ){
|
|
||||||
int iStart = sqlite3VdbeCurrentAddr(v);
|
|
||||||
VdbeOp *pOp, *pEnd;
|
|
||||||
|
|
||||||
nArg = pWin->pOwner->x.pList->nExpr;
|
|
||||||
regArg = sqlite3GetTempRange(pParse, nArg);
|
|
||||||
sqlite3ExprCodeExprList(pParse, pWin->pOwner->x.pList, regArg, 0, 0);
|
|
||||||
|
|
||||||
pEnd = sqlite3VdbeGetOp(v, -1);
|
|
||||||
for(pOp=sqlite3VdbeGetOp(v, iStart); pOp<=pEnd; pOp++){
|
|
||||||
if( pOp->opcode==OP_Column && pOp->p1==pWin->iEphCsr ){
|
|
||||||
pOp->p1 = csr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
|
|
||||||
CollSeq *pColl;
|
|
||||||
assert( nArg>0 );
|
|
||||||
pColl = sqlite3ExprNNCollSeq(pParse, pWin->pOwner->x.pList->a[0].pExpr);
|
|
||||||
sqlite3VdbeAddOp4(v, OP_CollSeq, 0,0,0, (const char*)pColl, P4_COLLSEQ);
|
|
||||||
}
|
|
||||||
sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep,
|
|
||||||
bInverse, regArg, pWin->regAccum);
|
|
||||||
sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF);
|
|
||||||
sqlite3VdbeChangeP5(v, (u8)nArg);
|
|
||||||
if( pWin->bExprArgs ){
|
|
||||||
sqlite3ReleaseTempRange(pParse, regArg, nArg);
|
|
||||||
}
|
|
||||||
if( addrIf ) sqlite3VdbeJumpHere(v, addrIf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct WindowCodeArg WindowCodeArg;
|
typedef struct WindowCodeArg WindowCodeArg;
|
||||||
typedef struct WindowCsrAndReg WindowCsrAndReg;
|
typedef struct WindowCsrAndReg WindowCsrAndReg;
|
||||||
|
|
||||||
@@ -1596,19 +1482,14 @@ struct WindowCodeArg {
|
|||||||
int regGosub; /* Register used with OP_Gosub(addrGosub) */
|
int regGosub; /* Register used with OP_Gosub(addrGosub) */
|
||||||
int regArg; /* First in array of accumulator registers */
|
int regArg; /* First in array of accumulator registers */
|
||||||
int eDelete; /* See above */
|
int eDelete; /* See above */
|
||||||
|
int regStart; /* Value of <expr> PRECEDING */
|
||||||
|
int regEnd; /* Value of <expr> FOLLOWING */
|
||||||
|
|
||||||
WindowCsrAndReg start;
|
WindowCsrAndReg start;
|
||||||
WindowCsrAndReg current;
|
WindowCsrAndReg current;
|
||||||
WindowCsrAndReg end;
|
WindowCsrAndReg end;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
** Values that may be passed as the second argument to windowCodeOp().
|
|
||||||
*/
|
|
||||||
#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
|
** Generate VM code to read the window frames peer values from cursor csr into
|
||||||
** an array of registers starting at reg.
|
** an array of registers starting at reg.
|
||||||
@@ -1631,6 +1512,163 @@ static void windowReadPeerValues(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Generate VM code to invoke either xStep() (if bInverse is 0) or
|
||||||
|
** xInverse (if bInverse is non-zero) for each window function in the
|
||||||
|
** linked list starting at pMWin. Or, for built-in window functions
|
||||||
|
** that do not use the standard function API, generate the required
|
||||||
|
** inline VM code.
|
||||||
|
**
|
||||||
|
** If argument csr is greater than or equal to 0, then argument reg is
|
||||||
|
** the first register in an array of registers guaranteed to be large
|
||||||
|
** enough to hold the array of arguments for each function. In this case
|
||||||
|
** the arguments are extracted from the current row of csr into the
|
||||||
|
** array of registers before invoking OP_AggStep or OP_AggInverse
|
||||||
|
**
|
||||||
|
** Or, if csr is less than zero, then the array of registers at reg is
|
||||||
|
** already populated with all columns from the current row of the sub-query.
|
||||||
|
**
|
||||||
|
** If argument regPartSize is non-zero, then it is a register containing the
|
||||||
|
** number of rows in the current partition.
|
||||||
|
*/
|
||||||
|
static void windowAggStep(
|
||||||
|
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){
|
||||||
|
FuncDef *pFunc = pWin->pFunc;
|
||||||
|
int regArg;
|
||||||
|
int nArg = pWin->bExprArgs ? 0 : windowArgCount(pWin);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
assert( bInverse==0 || pWin->eStart!=TK_UNBOUNDED );
|
||||||
|
|
||||||
|
for(i=0; i<nArg; i++){
|
||||||
|
if( i!=1 || pFunc->zName!=nth_valueName ){
|
||||||
|
sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+i, reg+i);
|
||||||
|
}else{
|
||||||
|
sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, pWin->iArgCol+i, reg+i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
regArg = reg;
|
||||||
|
|
||||||
|
if( pMWin->regStartRowid==0
|
||||||
|
&& (pFunc->funcFlags & SQLITE_FUNC_MINMAX)
|
||||||
|
&& (pWin->eStart!=TK_UNBOUNDED)
|
||||||
|
){
|
||||||
|
int addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regArg);
|
||||||
|
VdbeCoverage(v);
|
||||||
|
if( bInverse==0 ){
|
||||||
|
sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1, 1);
|
||||||
|
sqlite3VdbeAddOp2(v, OP_SCopy, regArg, pWin->regApp);
|
||||||
|
sqlite3VdbeAddOp3(v, OP_MakeRecord, pWin->regApp, 2, pWin->regApp+2);
|
||||||
|
sqlite3VdbeAddOp2(v, OP_IdxInsert, pWin->csrApp, pWin->regApp+2);
|
||||||
|
}else{
|
||||||
|
sqlite3VdbeAddOp4Int(v, OP_SeekGE, pWin->csrApp, 0, regArg, 1);
|
||||||
|
VdbeCoverageNeverTaken(v);
|
||||||
|
sqlite3VdbeAddOp1(v, OP_Delete, pWin->csrApp);
|
||||||
|
sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
|
||||||
|
}
|
||||||
|
sqlite3VdbeJumpHere(v, addrIsNull);
|
||||||
|
}else if( pWin->regApp ){
|
||||||
|
assert( pFunc->zName==nth_valueName
|
||||||
|
|| pFunc->zName==first_valueName
|
||||||
|
);
|
||||||
|
assert( bInverse==0 || bInverse==1 );
|
||||||
|
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 );
|
||||||
|
assert( pWin->bExprArgs || nArg ||pWin->pOwner->x.pList==0 );
|
||||||
|
regTmp = sqlite3GetTempReg(pParse);
|
||||||
|
sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp);
|
||||||
|
addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1);
|
||||||
|
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;
|
||||||
|
|
||||||
|
nArg = pWin->pOwner->x.pList->nExpr;
|
||||||
|
regArg = sqlite3GetTempRange(pParse, nArg);
|
||||||
|
sqlite3ExprCodeExprList(pParse, pWin->pOwner->x.pList, regArg, 0, 0);
|
||||||
|
|
||||||
|
pEnd = sqlite3VdbeGetOp(v, -1);
|
||||||
|
for(pOp=sqlite3VdbeGetOp(v, iStart); pOp<=pEnd; pOp++){
|
||||||
|
if( pOp->opcode==OP_Column && pOp->p1==pWin->iEphCsr ){
|
||||||
|
pOp->p1 = csr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
|
||||||
|
CollSeq *pColl;
|
||||||
|
assert( nArg>0 );
|
||||||
|
pColl = sqlite3ExprNNCollSeq(pParse, pWin->pOwner->x.pList->a[0].pExpr);
|
||||||
|
sqlite3VdbeAddOp4(v, OP_CollSeq, 0,0,0, (const char*)pColl, P4_COLLSEQ);
|
||||||
|
}
|
||||||
|
sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep,
|
||||||
|
bInverse, regArg, pWin->regAccum);
|
||||||
|
sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF);
|
||||||
|
sqlite3VdbeChangeP5(v, (u8)nArg);
|
||||||
|
if( pWin->bExprArgs ){
|
||||||
|
sqlite3ReleaseTempRange(pParse, regArg, nArg);
|
||||||
|
}
|
||||||
|
if( addrIf ) sqlite3VdbeJumpHere(v, addrIf);
|
||||||
|
if( addrIf2 ) sqlite3VdbeJumpHere(v, addrIf2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Values that may be passed as the second argument to windowCodeOp().
|
||||||
|
*/
|
||||||
|
#define WINDOW_RETURN_ROW 1
|
||||||
|
#define WINDOW_AGGINVERSE 2
|
||||||
|
#define WINDOW_AGGSTEP 3
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Generate VM code to invoke either xValue() (bFin==0) or xFinalize()
|
** Generate VM code to invoke either xValue() (bFin==0) or xFinalize()
|
||||||
** (bFin==1) for each window function in the linked list starting at
|
** (bFin==1) for each window function in the linked list starting at
|
||||||
@@ -1693,6 +1731,8 @@ static void windowFullScan(WindowCodeArg *p){
|
|||||||
int addrNext;
|
int addrNext;
|
||||||
int csr;
|
int csr;
|
||||||
|
|
||||||
|
VdbeModuleComment((v, "windowFullScan begin"));
|
||||||
|
|
||||||
assert( pMWin!=0 );
|
assert( pMWin!=0 );
|
||||||
csr = pMWin->csrApp;
|
csr = pMWin->csrApp;
|
||||||
nPeer = (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0);
|
nPeer = (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0);
|
||||||
@@ -1749,7 +1789,7 @@ static void windowFullScan(WindowCodeArg *p){
|
|||||||
if( addrEq ) sqlite3VdbeJumpHere(v, addrEq);
|
if( addrEq ) sqlite3VdbeJumpHere(v, addrEq);
|
||||||
}
|
}
|
||||||
|
|
||||||
windowAggStep(pParse, pMWin, csr, 0, p->regArg);
|
windowAggStep(p, pMWin, csr, 0, p->regArg);
|
||||||
|
|
||||||
sqlite3VdbeResolveLabel(v, lblNext);
|
sqlite3VdbeResolveLabel(v, lblNext);
|
||||||
sqlite3VdbeAddOp2(v, OP_Next, csr, addrNext);
|
sqlite3VdbeAddOp2(v, OP_Next, csr, addrNext);
|
||||||
@@ -1764,6 +1804,7 @@ static void windowFullScan(WindowCodeArg *p){
|
|||||||
}
|
}
|
||||||
|
|
||||||
windowAggFinal(p, 1);
|
windowAggFinal(p, 1);
|
||||||
|
VdbeModuleComment((v, "windowFullScan end"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -2094,9 +2135,7 @@ static int windowCodeOp(
|
|||||||
Window *pMWin = p->pMWin;
|
Window *pMWin = p->pMWin;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
Vdbe *v = p->pVdbe;
|
Vdbe *v = p->pVdbe;
|
||||||
int addrIf = 0;
|
|
||||||
int addrContinue = 0;
|
int addrContinue = 0;
|
||||||
int addrGoto = 0;
|
|
||||||
int bPeer = (pMWin->eFrmType!=TK_ROWS);
|
int bPeer = (pMWin->eFrmType!=TK_ROWS);
|
||||||
|
|
||||||
int lblDone = sqlite3VdbeMakeLabel(pParse);
|
int lblDone = sqlite3VdbeMakeLabel(pParse);
|
||||||
@@ -2129,7 +2168,7 @@ static int windowCodeOp(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
addrIf = sqlite3VdbeAddOp3(v, OP_IfPos, regCountdown, 0, 1);
|
sqlite3VdbeAddOp3(v, OP_IfPos, regCountdown, lblDone, 1);
|
||||||
VdbeCoverage(v);
|
VdbeCoverage(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2152,7 +2191,7 @@ static int windowCodeOp(
|
|||||||
assert( pMWin->regEndRowid );
|
assert( pMWin->regEndRowid );
|
||||||
sqlite3VdbeAddOp2(v, OP_AddImm, pMWin->regStartRowid, 1);
|
sqlite3VdbeAddOp2(v, OP_AddImm, pMWin->regStartRowid, 1);
|
||||||
}else{
|
}else{
|
||||||
windowAggStep(pParse, pMWin, csr, 1, p->regArg);
|
windowAggStep(p, pMWin, csr, 1, p->regArg);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -2164,7 +2203,7 @@ static int windowCodeOp(
|
|||||||
assert( pMWin->regEndRowid );
|
assert( pMWin->regEndRowid );
|
||||||
sqlite3VdbeAddOp2(v, OP_AddImm, pMWin->regEndRowid, 1);
|
sqlite3VdbeAddOp2(v, OP_AddImm, pMWin->regEndRowid, 1);
|
||||||
}else{
|
}else{
|
||||||
windowAggStep(pParse, pMWin, csr, 0, p->regArg);
|
windowAggStep(p, pMWin, csr, 0, p->regArg);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2182,13 +2221,29 @@ static int windowCodeOp(
|
|||||||
sqlite3VdbeAddOp2(v, OP_Next, csr, sqlite3VdbeCurrentAddr(v)+1+bPeer);
|
sqlite3VdbeAddOp2(v, OP_Next, csr, sqlite3VdbeCurrentAddr(v)+1+bPeer);
|
||||||
VdbeCoverage(v);
|
VdbeCoverage(v);
|
||||||
if( bPeer ){
|
if( bPeer ){
|
||||||
addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
|
sqlite3VdbeAddOp2(v, OP_Goto, 0, lblDone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( bPeer ){
|
if( bPeer ){
|
||||||
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;
|
||||||
|
|
||||||
|
/* 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);
|
windowReadPeerValues(p, csr, regTmp);
|
||||||
windowIfNewPeer(pParse, pMWin->pOrderBy, regTmp, reg, addrContinue);
|
windowIfNewPeer(pParse, pMWin->pOrderBy, regTmp, reg, addrContinue);
|
||||||
sqlite3ReleaseTempRange(pParse, regTmp, nReg);
|
sqlite3ReleaseTempRange(pParse, regTmp, nReg);
|
||||||
@@ -2198,8 +2253,6 @@ static int windowCodeOp(
|
|||||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNextRange);
|
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNextRange);
|
||||||
}
|
}
|
||||||
sqlite3VdbeResolveLabel(v, lblDone);
|
sqlite3VdbeResolveLabel(v, lblDone);
|
||||||
if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto);
|
|
||||||
if( addrIf ) sqlite3VdbeJumpHere(v, addrIf);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2552,23 +2605,23 @@ static int windowExprGtZero(Parse *pParse, Expr *pExpr){
|
|||||||
** regEnd = <expr2>
|
** regEnd = <expr2>
|
||||||
** regStart = <expr1>
|
** regStart = <expr1>
|
||||||
** }else{
|
** }else{
|
||||||
** if( (csrEnd.key + regEnd) <= csrCurrent.key ){
|
** while( (csrEnd.key + regEnd) <= csrCurrent.key ){
|
||||||
** AGGSTEP
|
** AGGSTEP
|
||||||
** }
|
** }
|
||||||
|
** RETURN_ROW
|
||||||
** while( (csrStart.key + regStart) < csrCurrent.key ){
|
** while( (csrStart.key + regStart) < csrCurrent.key ){
|
||||||
** AGGINVERSE
|
** AGGINVERSE
|
||||||
** }
|
** }
|
||||||
** RETURN_ROW
|
|
||||||
** }
|
** }
|
||||||
** }
|
** }
|
||||||
** flush:
|
** flush:
|
||||||
** while( (csrEnd.key + regEnd) <= csrCurrent.key ){
|
** while( (csrEnd.key + regEnd) <= csrCurrent.key ){
|
||||||
** AGGSTEP
|
** AGGSTEP
|
||||||
** }
|
** }
|
||||||
|
** RETURN_ROW
|
||||||
** while( (csrStart.key + regStart) < csrCurrent.key ){
|
** while( (csrStart.key + regStart) < csrCurrent.key ){
|
||||||
** AGGINVERSE
|
** AGGINVERSE
|
||||||
** }
|
** }
|
||||||
** RETURN_ROW
|
|
||||||
**
|
**
|
||||||
** RANGE BETWEEN <expr1> FOLLOWING AND <expr2> FOLLOWING
|
** RANGE BETWEEN <expr1> FOLLOWING AND <expr2> FOLLOWING
|
||||||
**
|
**
|
||||||
@@ -2625,8 +2678,6 @@ void sqlite3WindowCodeStep(
|
|||||||
int addrGosubFlush = 0; /* Address of OP_Gosub to flush: */
|
int addrGosubFlush = 0; /* Address of OP_Gosub to flush: */
|
||||||
int addrInteger = 0; /* Address of OP_Integer */
|
int addrInteger = 0; /* Address of OP_Integer */
|
||||||
int addrEmpty; /* Address of OP_Rewind in flush: */
|
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 regNew; /* Array of registers holding new input row */
|
||||||
int regRecord; /* regNew array in record form */
|
int regRecord; /* regNew array in record form */
|
||||||
int regRowid; /* Rowid for regRecord in eph table */
|
int regRowid; /* Rowid for regRecord in eph table */
|
||||||
@@ -2705,10 +2756,10 @@ void sqlite3WindowCodeStep(
|
|||||||
** clause, allocate registers to store the results of evaluating each
|
** clause, allocate registers to store the results of evaluating each
|
||||||
** <expr>. */
|
** <expr>. */
|
||||||
if( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_FOLLOWING ){
|
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 ){
|
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
|
/* If this is not a "ROWS BETWEEN ..." frame, then allocate arrays of
|
||||||
@@ -2763,18 +2814,18 @@ void sqlite3WindowCodeStep(
|
|||||||
/* This block is run for the first row of each partition */
|
/* This block is run for the first row of each partition */
|
||||||
s.regArg = windowInitAccum(pParse, pMWin);
|
s.regArg = windowInitAccum(pParse, pMWin);
|
||||||
|
|
||||||
if( regStart ){
|
if( s.regStart ){
|
||||||
sqlite3ExprCode(pParse, pMWin->pStart, regStart);
|
sqlite3ExprCode(pParse, pMWin->pStart, s.regStart);
|
||||||
windowCheckValue(pParse, regStart, 0 + (pMWin->eFrmType==TK_RANGE ? 3 : 0));
|
windowCheckValue(pParse, s.regStart, 0 + (pMWin->eFrmType==TK_RANGE?3:0));
|
||||||
}
|
}
|
||||||
if( regEnd ){
|
if( s.regEnd ){
|
||||||
sqlite3ExprCode(pParse, pMWin->pEnd, regEnd);
|
sqlite3ExprCode(pParse, pMWin->pEnd, s.regEnd);
|
||||||
windowCheckValue(pParse, regEnd, 1 + (pMWin->eFrmType==TK_RANGE ? 3 : 0));
|
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 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_Ge); /* NeverNull because bound <expr> */
|
||||||
VdbeCoverageNeverNullIf(v, op==OP_Le); /* values previously checked */
|
VdbeCoverageNeverNullIf(v, op==OP_Le); /* values previously checked */
|
||||||
windowAggFinal(&s, 0);
|
windowAggFinal(&s, 0);
|
||||||
@@ -2785,9 +2836,9 @@ void sqlite3WindowCodeStep(
|
|||||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, lblWhereEnd);
|
sqlite3VdbeAddOp2(v, OP_Goto, 0, lblWhereEnd);
|
||||||
sqlite3VdbeJumpHere(v, addrGe);
|
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 );
|
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->eStart!=TK_UNBOUNDED ){
|
||||||
@@ -2819,23 +2870,23 @@ void sqlite3WindowCodeStep(
|
|||||||
if( pMWin->eFrmType==TK_RANGE ){
|
if( pMWin->eFrmType==TK_RANGE ){
|
||||||
int lbl = sqlite3VdbeMakeLabel(pParse);
|
int lbl = sqlite3VdbeMakeLabel(pParse);
|
||||||
int addrNext = sqlite3VdbeCurrentAddr(v);
|
int addrNext = sqlite3VdbeCurrentAddr(v);
|
||||||
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_AGGINVERSE, regStart, 0);
|
windowCodeOp(&s, WINDOW_AGGINVERSE, s.regStart, 0);
|
||||||
windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
|
windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
|
||||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNext);
|
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNext);
|
||||||
sqlite3VdbeResolveLabel(v, lbl);
|
sqlite3VdbeResolveLabel(v, lbl);
|
||||||
}else{
|
}else{
|
||||||
windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 0);
|
windowCodeOp(&s, WINDOW_RETURN_ROW, s.regEnd, 0);
|
||||||
windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
|
windowCodeOp(&s, WINDOW_AGGINVERSE, s.regStart, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else
|
}else
|
||||||
if( pMWin->eEnd==TK_PRECEDING ){
|
if( pMWin->eEnd==TK_PRECEDING ){
|
||||||
int bRPS = (pMWin->eStart==TK_PRECEDING && pMWin->eFrmType==TK_RANGE);
|
int bRPS = (pMWin->eStart==TK_PRECEDING && pMWin->eFrmType==TK_RANGE);
|
||||||
windowCodeOp(&s, WINDOW_AGGSTEP, regEnd, 0);
|
windowCodeOp(&s, WINDOW_AGGSTEP, s.regEnd, 0);
|
||||||
if( bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
|
if( bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, s.regStart, 0);
|
||||||
windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 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{
|
}else{
|
||||||
int addr = 0;
|
int addr = 0;
|
||||||
windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
|
windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
|
||||||
@@ -2843,24 +2894,24 @@ void sqlite3WindowCodeStep(
|
|||||||
if( pMWin->eFrmType==TK_RANGE ){
|
if( pMWin->eFrmType==TK_RANGE ){
|
||||||
int lbl = 0;
|
int lbl = 0;
|
||||||
addr = sqlite3VdbeCurrentAddr(v);
|
addr = sqlite3VdbeCurrentAddr(v);
|
||||||
if( regEnd ){
|
if( s.regEnd ){
|
||||||
lbl = sqlite3VdbeMakeLabel(pParse);
|
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_RETURN_ROW, 0, 0);
|
||||||
windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
|
windowCodeOp(&s, WINDOW_AGGINVERSE, s.regStart, 0);
|
||||||
if( regEnd ){
|
if( s.regEnd ){
|
||||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
|
sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
|
||||||
sqlite3VdbeResolveLabel(v, lbl);
|
sqlite3VdbeResolveLabel(v, lbl);
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
if( regEnd ){
|
if( s.regEnd ){
|
||||||
addr = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0, 1);
|
addr = sqlite3VdbeAddOp3(v, OP_IfPos, s.regEnd, 0, 1);
|
||||||
VdbeCoverage(v);
|
VdbeCoverage(v);
|
||||||
}
|
}
|
||||||
windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
|
windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
|
||||||
windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
|
windowCodeOp(&s, WINDOW_AGGINVERSE, s.regStart, 0);
|
||||||
if( regEnd ) sqlite3VdbeJumpHere(v, addr);
|
if( s.regEnd ) sqlite3VdbeJumpHere(v, addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2879,8 +2930,8 @@ void sqlite3WindowCodeStep(
|
|||||||
VdbeCoverage(v);
|
VdbeCoverage(v);
|
||||||
if( pMWin->eEnd==TK_PRECEDING ){
|
if( pMWin->eEnd==TK_PRECEDING ){
|
||||||
int bRPS = (pMWin->eStart==TK_PRECEDING && pMWin->eFrmType==TK_RANGE);
|
int bRPS = (pMWin->eStart==TK_PRECEDING && pMWin->eFrmType==TK_RANGE);
|
||||||
windowCodeOp(&s, WINDOW_AGGSTEP, regEnd, 0);
|
windowCodeOp(&s, WINDOW_AGGSTEP, s.regEnd, 0);
|
||||||
if( bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
|
if( bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, s.regStart, 0);
|
||||||
windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
|
windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
|
||||||
}else if( pMWin->eStart==TK_FOLLOWING ){
|
}else if( pMWin->eStart==TK_FOLLOWING ){
|
||||||
int addrStart;
|
int addrStart;
|
||||||
@@ -2890,18 +2941,18 @@ void sqlite3WindowCodeStep(
|
|||||||
windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
|
windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
|
||||||
if( pMWin->eFrmType==TK_RANGE ){
|
if( pMWin->eFrmType==TK_RANGE ){
|
||||||
addrStart = sqlite3VdbeCurrentAddr(v);
|
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);
|
addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1);
|
||||||
}else
|
}else
|
||||||
if( pMWin->eEnd==TK_UNBOUNDED ){
|
if( pMWin->eEnd==TK_UNBOUNDED ){
|
||||||
addrStart = sqlite3VdbeCurrentAddr(v);
|
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);
|
addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, 0, 1);
|
||||||
}else{
|
}else{
|
||||||
assert( pMWin->eEnd==TK_FOLLOWING );
|
assert( pMWin->eEnd==TK_FOLLOWING );
|
||||||
addrStart = sqlite3VdbeCurrentAddr(v);
|
addrStart = sqlite3VdbeCurrentAddr(v);
|
||||||
addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 1);
|
addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, s.regEnd, 1);
|
||||||
addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 1);
|
addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, s.regStart, 1);
|
||||||
}
|
}
|
||||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart);
|
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart);
|
||||||
sqlite3VdbeJumpHere(v, addrBreak2);
|
sqlite3VdbeJumpHere(v, addrBreak2);
|
||||||
@@ -2916,7 +2967,7 @@ void sqlite3WindowCodeStep(
|
|||||||
windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
|
windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
|
||||||
addrStart = sqlite3VdbeCurrentAddr(v);
|
addrStart = sqlite3VdbeCurrentAddr(v);
|
||||||
addrBreak = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1);
|
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);
|
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart);
|
||||||
sqlite3VdbeJumpHere(v, addrBreak);
|
sqlite3VdbeJumpHere(v, addrBreak);
|
||||||
}
|
}
|
||||||
|
@@ -230,4 +230,71 @@ do_catchsql_test 4.3 {
|
|||||||
SELECT 1 WINDOW win AS (PARTITION BY fake_column);
|
SELECT 1 WINDOW win AS (PARTITION BY fake_column);
|
||||||
} {0 1}
|
} {0 1}
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
reset_db
|
||||||
|
do_execsql_test 5.0 {
|
||||||
|
CREATE TABLE t1(a, c);
|
||||||
|
CREATE INDEX i1 ON t1(a);
|
||||||
|
|
||||||
|
INSERT INTO t1 VALUES(0, 421);
|
||||||
|
INSERT INTO t1 VALUES(1, 844);
|
||||||
|
INSERT INTO t1 VALUES(2, 1001);
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test 5.1 {
|
||||||
|
SELECT a, sum(c) OVER (
|
||||||
|
ORDER BY a RANGE BETWEEN 0 PRECEDING AND 3 PRECEDING
|
||||||
|
) FROM t1;
|
||||||
|
} {0 {} 1 {} 2 {}}
|
||||||
|
|
||||||
|
do_execsql_test 5.2 {
|
||||||
|
INSERT INTO t1 VALUES(NULL, 123);
|
||||||
|
INSERT INTO t1 VALUES(NULL, 111);
|
||||||
|
INSERT INTO t1 VALUES('xyz', 222);
|
||||||
|
INSERT INTO t1 VALUES('xyz', 333);
|
||||||
|
|
||||||
|
SELECT a, sum(c) OVER (
|
||||||
|
ORDER BY a RANGE BETWEEN 0 PRECEDING AND 3 PRECEDING
|
||||||
|
) FROM t1;
|
||||||
|
} {{} 234 {} 234 0 {} 1 {} 2 {} xyz 555 xyz 555}
|
||||||
|
|
||||||
|
do_execsql_test 5.3 {
|
||||||
|
SELECT a, sum(c) OVER (
|
||||||
|
ORDER BY a RANGE BETWEEN 2 FOLLOWING AND 0 FOLLOWING
|
||||||
|
) FROM t1;
|
||||||
|
} {{} 234 {} 234 0 {} 1 {} 2 {} xyz 555 xyz 555}
|
||||||
|
|
||||||
|
do_execsql_test 5.4 {
|
||||||
|
SELECT a, sum(c) OVER (
|
||||||
|
ORDER BY a RANGE BETWEEN 0 PRECEDING AND 3 PRECEDING EXCLUDE NO OTHERS
|
||||||
|
) FROM t1;
|
||||||
|
} {{} 234 {} 234 0 {} 1 {} 2 {} xyz 555 xyz 555}
|
||||||
|
|
||||||
|
do_execsql_test 5.5 {
|
||||||
|
SELECT a, sum(c) OVER (
|
||||||
|
ORDER BY a RANGE BETWEEN 2 FOLLOWING AND 0 FOLLOWING EXCLUDE NO OTHERS
|
||||||
|
) FROM t1;
|
||||||
|
} {{} 234 {} 234 0 {} 1 {} 2 {} xyz 555 xyz 555}
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
reset_db
|
||||||
|
do_execsql_test 6.0 {
|
||||||
|
CREATE TABLE t1(a, c);
|
||||||
|
CREATE INDEX i1 ON t1(a);
|
||||||
|
|
||||||
|
INSERT INTO t1 VALUES(7, 997);
|
||||||
|
INSERT INTO t1 VALUES(8, 997);
|
||||||
|
INSERT INTO t1 VALUES('abc', 1001);
|
||||||
|
}
|
||||||
|
do_execsql_test 6.1 {
|
||||||
|
SELECT a, sum(c) OVER (
|
||||||
|
ORDER BY a RANGE BETWEEN 2 FOLLOWING AND 0 FOLLOWING
|
||||||
|
) FROM t1;
|
||||||
|
} {7 {} 8 {} abc 1001}
|
||||||
|
do_execsql_test 6.2 {
|
||||||
|
SELECT a, sum(c) OVER (
|
||||||
|
ORDER BY a RANGE BETWEEN 2 FOLLOWING AND 0 FOLLOWING EXCLUDE NO OTHERS
|
||||||
|
) FROM t1;
|
||||||
|
} {7 {} 8 {} abc 1001}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
Reference in New Issue
Block a user