mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-07 02:42:48 +03:00
Fix problems with "RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING" window
frames. FossilOrigin-Name: c34f31dbd79891249ee9485e91f6ea558ee1db62e04fb0fff2c051612b8fa5e7
This commit is contained in:
16
manifest
16
manifest
@@ -1,5 +1,5 @@
|
||||
C Fix\sanother\sissue\sto\sdo\swith\swindow-functions\sin\saggregate\squeries.
|
||||
D 2018-06-12T20:53:38.832
|
||||
C Fix\sproblems\swith\s"RANGE\sBETWEEN\sCURRENT\sROW\sAND\sUNBOUNDED\sFOLLOWING"\swindow\nframes.
|
||||
D 2018-06-13T20:29:38.362
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F Makefile.in 498b77b89a8cb42f2ee20fcd6317f279a45c0d6ff40d27825f94b69884c09bbe
|
||||
@@ -583,7 +583,7 @@ F src/where.c fe1a6f97c12cc9472ccce86166ba3f827cf61d6ae770c036a6396b63863baac4
|
||||
F src/whereInt.h b90ef9b9707ef750eab2a7a080c48fb4900315033274689def32d0cf5a81ebe4
|
||||
F src/wherecode.c 3317f2b083a66d3e65a03edf316ade4ccb0a99c9956273282ebb579b95d4ba96
|
||||
F src/whereexpr.c 6f022d6cc9daf56495f191b199352f783aff5cf268ba136b4d8cea3fb62d8c7d
|
||||
F src/window.c 45d149fe9926b7e9c610ef5234b6eef08f22cbdff855aa3f367b6af17499e90b
|
||||
F src/window.c 4a26ff629a2207fbb766b64eec5de56a642db2ee1a58ca7f3d9bf7241ca2265d
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
|
||||
F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
|
||||
@@ -1622,8 +1622,8 @@ F test/window2.tcl 0983de5eade5eeda49469244799d5331bfe3199fca3f6c6d2a836aa08f4fb
|
||||
F test/window2.test 79747b2edde4ad424e0752b27529aedc86e91f3d8d88846fa17ff0cb67f65086
|
||||
F test/window3.tcl 654d61d73e10db089b22514d498bb23ec310f720c0f4b5f69f67fda83d672048
|
||||
F test/window3.test 41727668ee31d2ba50f78efcb5bf1bda2c5cffd889aa65243511004669d1ac25
|
||||
F test/window4.tcl a77a7ab3c60517abe06307e4204d65d11f5474c8062f30f536755dd083bf8224
|
||||
F test/window4.test 0fb98450ff5478f91b4f8c9440d4463ded30a7337029da4272894ccff0f227e2
|
||||
F test/window4.tcl 09167855f695ef94312da965532bc13f8027411de8ce442664fa74949f9df011
|
||||
F test/window4.test eb0cf5740de803a4a9373b2c30b73986a4fb1662149260ccf05458abba312ba5
|
||||
F test/with1.test 58475190cd8caaeebea8cfeb2a264ec97a0c492b8ffe9ad20cefbb23df462f96
|
||||
F test/with2.test e0030e2f0267a910d6c0e4f46f2dfe941c1cc0d4f659ba69b3597728e7e8f1ab
|
||||
F test/with3.test 5e8ce2c585170bbbc0544e2a01a4941fa0be173ba5265e5c92eb588cd99a232d
|
||||
@@ -1740,7 +1740,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P fe7081e0952950f577234fcbb58f3c1efa4579267654fd2f713dc4804e470e7e
|
||||
R 7cbc79b684a204310b1cd3eb56d614ea
|
||||
P 6413e38a174044c28fa9b8b937e6c972d144547a246e6f2882e782538300d042
|
||||
R 30609a9614da284dabc36852804237fe
|
||||
U dan
|
||||
Z 4b5b4b6d9afcbd2c7bfd885559718021
|
||||
Z 71b05cd13cd6b1243eec19b7dd0b41c2
|
||||
|
@@ -1 +1 @@
|
||||
6413e38a174044c28fa9b8b937e6c972d144547a246e6f2882e782538300d042
|
||||
c34f31dbd79891249ee9485e91f6ea558ee1db62e04fb0fff2c051612b8fa5e7
|
273
src/window.c
273
src/window.c
@@ -826,6 +826,7 @@ void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){
|
||||
|
||||
/*
|
||||
** Return 0 if the two window objects are identical, or non-zero otherwise.
|
||||
** Identical window objects can be processed in a single scan.
|
||||
*/
|
||||
int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2){
|
||||
if( p1->eType!=p2->eType ) return 1;
|
||||
@@ -838,10 +839,24 @@ int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2){
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void windowAggInit(Parse *pParse, Window *pMWin){
|
||||
|
||||
/*
|
||||
** This is called by code in select.c before it calls sqlite3WhereBegin()
|
||||
** to begin iterating through the sub-query results. It is used to allocate
|
||||
** and initialize registers and cursors used by sqlite3WindowCodeStep().
|
||||
*/
|
||||
void sqlite3WindowCodeInit(Parse *pParse, Window *pMWin){
|
||||
Window *pWin;
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
int nPart = (pMWin->pPartition ? pMWin->pPartition->nExpr : 0);
|
||||
nPart += (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0);
|
||||
if( nPart ){
|
||||
pMWin->regPart = pParse->nMem+1;
|
||||
pParse->nMem += nPart;
|
||||
sqlite3VdbeAddOp3(v, OP_Null, 0, pMWin->regPart, pMWin->regPart+nPart-1);
|
||||
}
|
||||
|
||||
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
FuncDef *p = pWin->pFunc;
|
||||
if( (p->funcFlags & SQLITE_FUNC_MINMAX) && pWin->eStart!=TK_UNBOUNDED ){
|
||||
ExprList *pList = pWin->pOwner->x.pList;
|
||||
@@ -874,33 +889,31 @@ static void windowAggInit(Parse *pParse, Window *pMWin){
|
||||
}
|
||||
}
|
||||
|
||||
void sqlite3WindowCodeInit(Parse *pParse, Window *pWin){
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
int nPart = (pWin->pPartition ? pWin->pPartition->nExpr : 0);
|
||||
nPart += (pWin->pOrderBy ? pWin->pOrderBy->nExpr : 0);
|
||||
if( nPart ){
|
||||
pWin->regPart = pParse->nMem+1;
|
||||
pParse->nMem += nPart;
|
||||
sqlite3VdbeAddOp3(v, OP_Null, 0, pWin->regPart, pWin->regPart+nPart-1);
|
||||
}
|
||||
windowAggInit(pParse, pWin);
|
||||
}
|
||||
|
||||
/*
|
||||
** A "PRECEDING <expr>" (bEnd==0) or "FOLLOWING <expr>" (bEnd==1) has just
|
||||
** been evaluated and the result left in register reg. This function generates
|
||||
** VM code to check that the value is a non-negative integer and throws
|
||||
** an exception if it is not.
|
||||
*/
|
||||
static void windowCheckFrameValue(Parse *pParse, int reg, int bEnd){
|
||||
static const char *azErr[] = {
|
||||
"frame starting offset must be a non-negative integer",
|
||||
"frame ending offset must be a non-negative integer"
|
||||
};
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
int regZero = ++pParse->nMem;
|
||||
|
||||
int regZero = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regZero);
|
||||
sqlite3VdbeAddOp2(v, OP_MustBeInt, reg, sqlite3VdbeCurrentAddr(v)+2);
|
||||
sqlite3VdbeAddOp3(v, OP_Ge, regZero, sqlite3VdbeCurrentAddr(v)+2, reg);
|
||||
sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_ERROR, OE_Abort);
|
||||
sqlite3VdbeAppendP4(v, (void*)azErr[bEnd], P4_STATIC);
|
||||
sqlite3ReleaseTempReg(pParse, regZero);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of arguments passed to the window-function associated
|
||||
** with the object passed as the only argument to this function.
|
||||
*/
|
||||
static int windowArgCount(Window *pWin){
|
||||
ExprList *pList = pWin->pOwner->x.pList;
|
||||
return (pList ? pList->nExpr : 0);
|
||||
@@ -909,14 +922,28 @@ static int windowArgCount(Window *pWin){
|
||||
/*
|
||||
** 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.
|
||||
** 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, 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,
|
||||
int csr,
|
||||
int bInverse,
|
||||
int reg,
|
||||
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 */
|
||||
int regPartSize /* Register containing size of partition */
|
||||
){
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
@@ -997,6 +1024,12 @@ static void windowAggStep(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate VM code to invoke either xValue() (bFinal==0) or xFinalize()
|
||||
** (bFinal==1) for each window function in the linked list starting at
|
||||
** pMWin. Or, for built-in window-functions that do not use the standard
|
||||
** API, generate the equivalent VM code.
|
||||
*/
|
||||
static void windowAggFinal(Parse *pParse, Window *pMWin, int bFinal){
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
Window *pWin;
|
||||
@@ -1029,13 +1062,18 @@ 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,
|
||||
WhereInfo *pWInfo,
|
||||
int regFlushPart,
|
||||
int lblFlushPart,
|
||||
int *pRegSize
|
||||
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);
|
||||
@@ -1085,6 +1123,19 @@ static void windowPartitionCache(
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart);
|
||||
}
|
||||
|
||||
/*
|
||||
** 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
|
||||
** aggregate window functions that use the standard API, a single
|
||||
** OP_Gosub instruction is all that this routine generates. Extra VM code
|
||||
** for per-row processing is only generated for the following built-in window
|
||||
** functions:
|
||||
**
|
||||
** nth_value()
|
||||
** first_value()
|
||||
** lag()
|
||||
** lead()
|
||||
*/
|
||||
static void windowReturnOneRow(
|
||||
Parse *pParse,
|
||||
Window *pMWin,
|
||||
@@ -1148,15 +1199,29 @@ 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 ){
|
||||
** AggStep (xInverse)
|
||||
** }
|
||||
** Next (Window.iEphCsr)
|
||||
** }
|
||||
*/
|
||||
static void windowReturnRows(
|
||||
Parse *pParse,
|
||||
Window *pMWin,
|
||||
int regCtr,
|
||||
int bFinal,
|
||||
int regGosub,
|
||||
int addrGosub,
|
||||
int regInvArg,
|
||||
int regInvSize
|
||||
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);
|
||||
@@ -1349,7 +1414,6 @@ static void windowCodeRowExprStep(
|
||||
int regPeerVal = 0; /* Array of values identifying peer group */
|
||||
int iPeer = 0; /* Column offset in eph-table of peer vals */
|
||||
int nPeerVal; /* Number of peer values */
|
||||
int bRange = 0;
|
||||
int regSize = 0;
|
||||
|
||||
assert( pMWin->eStart==TK_PRECEDING
|
||||
@@ -1363,13 +1427,6 @@ static void windowCodeRowExprStep(
|
||||
|| pMWin->eEnd==TK_PRECEDING
|
||||
);
|
||||
|
||||
if( pMWin->eType==TK_RANGE
|
||||
&& pMWin->eStart==TK_CURRENT
|
||||
&& pMWin->eEnd==TK_UNBOUNDED
|
||||
){
|
||||
bRange = 1;
|
||||
}
|
||||
|
||||
/* Allocate register and label for the "flush_partition" sub-routine. */
|
||||
regFlushPart = ++pParse->nMem;
|
||||
lblFlushPart = sqlite3VdbeMakeLabel(v);
|
||||
@@ -1457,38 +1514,10 @@ static void windowCodeRowExprStep(
|
||||
if( pMWin->eStart==TK_FOLLOWING ){
|
||||
addrIfPos2 = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0 , 1);
|
||||
}
|
||||
if( bRange ){
|
||||
assert( pMWin->eStart==TK_CURRENT && pMWin->pOrderBy );
|
||||
regPeer = ++pParse->nMem;
|
||||
regPeerVal = pParse->nMem+1;
|
||||
iPeer = pMWin->nBufferCol + (pMWin->pPartition?pMWin->pPartition->nExpr:0);
|
||||
nPeerVal = pMWin->pOrderBy->nExpr;
|
||||
pParse->nMem += (2 * nPeerVal);
|
||||
for(k=0; k<nPeerVal; k++){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, iPeer+k, regPeerVal+k);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regPeer);
|
||||
}
|
||||
|
||||
windowAggFinal(pParse, pMWin, 0);
|
||||
if( bRange ){
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regPeer, 1);
|
||||
}
|
||||
windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)+2);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, lblFlushDone);
|
||||
if( bRange ){
|
||||
KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pMWin->pOrderBy,0,0);
|
||||
int addrJump = sqlite3VdbeCurrentAddr(v)-4;
|
||||
for(k=0; k<nPeerVal; k++){
|
||||
int iOut = regPeerVal + nPeerVal + k;
|
||||
sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, iPeer+k, iOut);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_Compare, regPeerVal, regPeerVal+nPeerVal, nPeerVal);
|
||||
sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
|
||||
addr = sqlite3VdbeCurrentAddr(v)+1;
|
||||
sqlite3VdbeAddOp3(v, OP_Jump, addr, addrJump, addr);
|
||||
}
|
||||
if( pMWin->eStart==TK_FOLLOWING ){
|
||||
sqlite3VdbeJumpHere(v, addrIfPos2);
|
||||
}
|
||||
@@ -1501,15 +1530,8 @@ static void windowCodeRowExprStep(
|
||||
if( pMWin->eStart==TK_PRECEDING ){
|
||||
addrJumpHere = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0 , 1);
|
||||
}
|
||||
if( bRange ){
|
||||
sqlite3VdbeAddOp3(v, OP_IfPos, regPeer, sqlite3VdbeCurrentAddr(v)+2, 1);
|
||||
addrJumpHere = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+1);
|
||||
windowAggStep(pParse, pMWin, csrStart, 1, regArg, regSize);
|
||||
if( bRange ){
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrJumpHere-1);
|
||||
}
|
||||
if( addrJumpHere ){
|
||||
sqlite3VdbeJumpHere(v, addrJumpHere);
|
||||
}
|
||||
@@ -1561,8 +1583,6 @@ static void windowCodeRowExprStep(
|
||||
** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||||
** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
|
||||
** RANGE BETWEEN CURRENT ROW AND CURRENT ROW
|
||||
**
|
||||
** TODO.
|
||||
*/
|
||||
static void windowCodeCacheStep(
|
||||
Parse *pParse,
|
||||
@@ -1582,7 +1602,7 @@ static void windowCodeCacheStep(
|
||||
int regNewPeer;
|
||||
|
||||
int addrGoto; /* Address of Goto used to jump flush_par.. */
|
||||
int addrRewind; /* Address of Rewind that starts loop */
|
||||
int addrNext; /* Jump here for next iteration of loop */
|
||||
int regFlushPart;
|
||||
int lblFlushPart;
|
||||
int csrLead;
|
||||
@@ -1590,12 +1610,17 @@ static void windowCodeCacheStep(
|
||||
int regArg; /* Register array to martial function args */
|
||||
int regSize;
|
||||
int nArg;
|
||||
int bReverse;
|
||||
int lblEmpty;
|
||||
|
||||
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(v);
|
||||
bReverse = (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED);
|
||||
regNewPeer = pParse->nMem+1;
|
||||
pParse->nMem += nPeer;
|
||||
|
||||
@@ -1618,11 +1643,19 @@ static void windowCodeCacheStep(
|
||||
regArg = windowInitAccum(pParse, pMWin);
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regCtr);
|
||||
addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, csrLead);
|
||||
sqlite3VdbeAddOp1(v, OP_Rewind, pMWin->iEphCsr);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, csrLead, lblEmpty);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, lblEmpty);
|
||||
|
||||
if( pOrderBy && pMWin->eEnd==TK_CURRENT ){
|
||||
int bCurrent = (pMWin->eEnd==TK_CURRENT && pMWin->eStart==TK_CURRENT);
|
||||
if( bReverse ){
|
||||
int addr = sqlite3VdbeCurrentAddr(v);
|
||||
windowAggStep(pParse, pMWin, csrLead, 0, regArg, regSize);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, csrLead, addr);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, csrLead, lblEmpty);
|
||||
}
|
||||
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);
|
||||
@@ -1637,20 +1670,21 @@ static void windowCodeCacheStep(
|
||||
sqlite3VdbeAddOp3(v, OP_Copy, regNewPeer, regPeer, nPeer-1);
|
||||
}
|
||||
|
||||
windowReturnRows(pParse, pMWin, regCtr, 0, regGosub, addrGosub,
|
||||
windowReturnRows(pParse, pMWin, regCtr, regGosub, addrGosub,
|
||||
(bCurrent ? regArg : 0), (bCurrent ? regSize : 0)
|
||||
);
|
||||
if( addrJump ) sqlite3VdbeJumpHere(v, addrJump);
|
||||
}
|
||||
|
||||
windowAggStep(pParse, pMWin, csrLead, 0, regArg, regSize);
|
||||
if( bReverse==0 ){
|
||||
windowAggStep(pParse, pMWin, csrLead, 0, regArg, regSize);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regCtr, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, csrLead, addrRewind+2);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, csrLead, addrNext);
|
||||
|
||||
windowReturnRows(pParse, pMWin, regCtr, 1, regGosub, addrGosub, 0, 0);
|
||||
windowReturnRows(pParse, pMWin, regCtr, regGosub, addrGosub, 0, 0);
|
||||
|
||||
sqlite3VdbeJumpHere(v, addrRewind);
|
||||
sqlite3VdbeJumpHere(v, addrRewind+1);
|
||||
sqlite3VdbeResolveLabel(v, lblEmpty);
|
||||
sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
|
||||
sqlite3VdbeAddOp1(v, OP_Return, regFlushPart);
|
||||
|
||||
@@ -1805,6 +1839,11 @@ static void windowCodeDefaultStep(
|
||||
sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)-1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate and return a duplicate of the Window object indicated by the
|
||||
** third argument. Set the Window.pOwner field of the new object to
|
||||
** pOwner.
|
||||
*/
|
||||
Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p){
|
||||
Window *pNew = 0;
|
||||
if( p ){
|
||||
@@ -1839,40 +1878,46 @@ void sqlite3WindowCodeStep(
|
||||
int addrGosub /* OP_Gosub here to return each row */
|
||||
){
|
||||
Window *pMWin = p->pWin;
|
||||
Window *pWin;
|
||||
ExprList *pOrderBy = pMWin->pOrderBy;
|
||||
|
||||
/* Call windowCodeRowExprStep() for all window modes *except*:
|
||||
/* Call windowCodeRowExprStep() for all "ROWS" window modes except:
|
||||
**
|
||||
** 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
|
||||
** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||||
*/
|
||||
if( (pMWin->eType==TK_ROWS
|
||||
&& (pMWin->eStart!=TK_UNBOUNDED||pMWin->eEnd!=TK_CURRENT||!pMWin->pOrderBy))
|
||||
|| (pMWin->eStart==TK_CURRENT&&pMWin->eEnd==TK_UNBOUNDED&&pMWin->pOrderBy)
|
||||
&& (pMWin->eStart!=TK_UNBOUNDED || pMWin->eEnd!=TK_CURRENT || !pOrderBy))
|
||||
){
|
||||
windowCodeRowExprStep(pParse, p, pWInfo, regGosub, addrGosub);
|
||||
return;
|
||||
}
|
||||
}else{
|
||||
Window *pWin;
|
||||
int bCache = 0;
|
||||
|
||||
/* Call windowCodeCacheStep() if there is a window function that requires
|
||||
** that the entire partition be cached in a temp table before any rows
|
||||
** are returned. */
|
||||
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
||||
FuncDef *pFunc = pWin->pFunc;
|
||||
if( (pFunc->funcFlags & SQLITE_FUNC_WINDOW_SIZE)
|
||||
|| (pFunc->xSFunc==nth_valueStepFunc)
|
||||
|| (pFunc->xSFunc==first_valueStepFunc)
|
||||
|| (pFunc->xSFunc==leadStepFunc)
|
||||
|| (pFunc->xSFunc==lagStepFunc)
|
||||
){
|
||||
if( pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED && pOrderBy ){
|
||||
bCache = 1;
|
||||
}else{
|
||||
/* Call windowCodeCacheStep() if there is a window function that requires
|
||||
** that the entire partition be cached in a temp table before any rows
|
||||
** are returned. */
|
||||
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
||||
FuncDef *pFunc = pWin->pFunc;
|
||||
if( (pFunc->funcFlags & SQLITE_FUNC_WINDOW_SIZE)
|
||||
|| (pFunc->xSFunc==nth_valueStepFunc)
|
||||
|| (pFunc->xSFunc==first_valueStepFunc)
|
||||
|| (pFunc->xSFunc==leadStepFunc)
|
||||
|| (pFunc->xSFunc==lagStepFunc)
|
||||
){
|
||||
bCache = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise, call windowCodeDefaultStep(). */
|
||||
if( bCache ){
|
||||
windowCodeCacheStep(pParse, p, pWInfo, regGosub, addrGosub);
|
||||
return;
|
||||
}else{
|
||||
windowCodeDefaultStep(pParse, p, pWInfo, regGosub, addrGosub);
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise, call windowCodeDefaultStep(). */
|
||||
windowCodeDefaultStep(pParse, p, pWInfo, regGosub, addrGosub);
|
||||
}
|
||||
|
||||
|
@@ -157,6 +157,12 @@ execsql_test 4.3 {
|
||||
SELECT abs(max(b) OVER (ORDER BY b)) FROM ttt GROUP BY b;
|
||||
}
|
||||
|
||||
execsql_test 4.4 {
|
||||
SELECT sum(b) OVER (
|
||||
ORDER BY a RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
|
||||
) FROM ttt;
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@@ -239,4 +239,10 @@ do_execsql_test 4.3 {
|
||||
SELECT abs(max(b) OVER (ORDER BY b)) FROM ttt GROUP BY b;
|
||||
} {1 2 3}
|
||||
|
||||
do_execsql_test 4.4 {
|
||||
SELECT sum(b) OVER (
|
||||
ORDER BY a RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
|
||||
) FROM ttt;
|
||||
} {18 17 15 12 11 9 6 5 3}
|
||||
|
||||
finish_test
|
||||
|
Reference in New Issue
Block a user