Finish consolidation of window frame code. Add untested support for GROUPS frames.

FossilOrigin-Name: 954bf369935083c188c3b14e77ed89fc5ec4323cc5b0c67e4a2e48fcc278df45
This commit is contained in:
dan 2019-03-08 20:02:52 +00:00
parent 54975cdff9
commit 6c75b39614
8 changed files with 843 additions and 598 deletions

View File

@ -1,5 +1,5 @@
C Fix\sother\s"ROWS\sBETWEEN"\scases\son\sthis\sbranch.
D 2019-03-07T20:47:46.020
C Finish\sconsolidation\sof\swindow\sframe\scode.\sAdd\suntested\ssupport\sfor\sGROUPS\sframes.
D 2019-03-08T20:02:52.323
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F Makefile.in 1ad7263f38329c0ecea543c80f30af839ee714ea77fc391bf1a3fbb919a5b6b5
@ -504,7 +504,7 @@ F src/os_win.c 85d9e532d0444ab6c16d7431490c2e279e282aa0917b0e988996b1ae0de5c5a0
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c 5ff85a1dafdbbc3d27e21dbb52ac02066507b1e2d7da4cd4961408eed2cce23a
F src/pager.h 217921e81eb5fe455caa5cda96061959706bcdd29ddb57166198645ef7822ac3
F src/parse.y deae85a1917e53e8533e7eed55ffb4405e2e375c022afb1cc3fecb280ee5cd7a
F src/parse.y e5628381465b183e96af2508dd49dd3c2d760b4c49f079e59bacb0157e9728e2
F src/pcache.c 696a01f1a6370c1b50a09c15972bc3bee3333f8fcd1f2da8e9a76b1b062c59ee
F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
F src/pcache1.c a72804486dfa8e4b6bc30d666c97ecf1155f91a4351fc6e48ea4097e4eb304fb
@ -604,7 +604,7 @@ F src/where.c 8a207cb2ca6b99e1edb1e4bbff9b0504385a759cbf66180d1deb34d80ca4b799
F src/whereInt.h 5f14db426ca46a83eabab1ae9aa6d4b8f27504ad35b64c290916289b1ddb2e88
F src/wherecode.c ce7b21e1be2b981d62683fc59c4ca73a04a7ff2f1ebec23d41baf2da2349afd6
F src/whereexpr.c 36b47f7261d6b6f1a72d774c113b74beddf6745aba1018e64b196e29db233442
F src/window.c fd7eae2b523f47c114f689f213407c218b737b45a29ed0a5b8ad6e3377f49594
F src/window.c e2e56938f05c93795bcfa6088e6c1799c9113dc62991e8f79665a37d92f69f21
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
@ -1197,7 +1197,7 @@ F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
F test/permutations.test 52d2c37fe8cc07ec7362024c214b04bb69432995b3a984a3fbabc60fa6ada3ee
F test/pg_common.tcl 72d4c464cf323768bbeb260ea2439602af2a780458b2e67d40e459127fd42b8b
F test/pg_common.tcl 55e8bf84d563fd72d617a05c4421c8dea2700775794c5bd8971caa1cb0ef6b26
F test/pragma.test c267bf02742c823a191960895b3d52933cebd7beee26757d1ed694f213fcd867
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
F test/pragma3.test 8300aa9c63cff1027006ca34bf413a148abbd6dcd471fa9a1ded322fe18c0df9
@ -1680,9 +1680,9 @@ F test/window1.test 5d9b3cfc4be6bf37fd81941e133f0f39741612c14dd8ad52e78850e9e61a
F test/window2.tcl 9bfa842d8a62b0d36dc8c1b5972206393c43847433c6d75940b87fec93ce3143
F test/window2.test 8e6d2a1b9f54dfebee1cde961c8590cd87b4db45c50f44947a211e1b63c2a05e
F test/window3.tcl 3bf7fc5b86024f47bb5834efc90015c9a64f57d696b82e26a1a4b648b7cd058a
F test/window3.test ee56c741e934a0b872fffd7de99f137f9c7788d50885f99f54da4075d951e8ee
F test/window3.test ab76dbaeeb8ad8c4a43041a5e481520517ec5013e8898c93968155ac2e9ee0a1
F test/window4.tcl 5fbaab489677914ee5686b2008426e336daf88a2f58be7df92757f780a5ebf91
F test/window4.test c5d6bf3403e4ade2f19df2afe4c16f29fb817c392c6c1c8017edb7165c191a62
F test/window4.test 0e3d595705f0e74c2fd77658b388d8547f6b612c7eb4fb2ad4dd5725a62bfc12
F test/window5.test d328dd18221217c49c144181975eea17339eaeaf0e9aa558cee3afb84652821e
F test/window6.test 5eae4ae7a590ccf1e605880969ca0bad3955616ac91cad3031baea38748badb3
F test/window7.tcl 6a1210f05d40ec89c22960213a22cd3f98d4e2f2eb20646c83c8c30d4d76108f
@ -1737,7 +1737,7 @@ F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439
F tool/mkautoconfamal.sh 422fc365358a2e92876ffc62971a0ff28ed472fc8bcf9de0df921c736fdeca5e
F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x
F tool/mkctimec.tcl dd183b73ae1c28249669741c250525f0407e579a70482371668fd5f130d9feb3
F tool/mkkeywordhash.c 1f7f2ac1d9f262c08b67faaca47e6a68262ff39113fa4b27d1db2843b4c33e73
F tool/mkkeywordhash.c a5a59223d2651161445164dcd2a73b202cf617109d041fdfe2816fc939790b13
F tool/mkmsvcmin.tcl cad0c7b54d7dd92bc87d59f36d4cc4f070eb2e625f14159dc2f5c4204e6a13ea
F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c
F tool/mkopcodeh.tcl 352a4319c0ad869eb26442bf7c3b015aa15594c21f1cce5a6420dbe999367c21
@ -1809,7 +1809,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 093d2b25f1b656bba57d665aac68109436861b157a5769432b612176dae3f6eb
R bc65bb166c813f4264e0cac9b9ddcfde
P a5f68f66472610b5beb4fe28669fbbfe83a32742be73cecad9b2ae28f8a17b30
R 1d357a160938e85235a93301382f7128
T +closed 467c1946fd2bfda359eac2044bbe40142a83607fbf7a46473db466a57914fd61
U dan
Z 787ed5434e794ce48cb312b7f3a2a9d5
Z 4204779764817a38b1e99ea1d813b082

View File

@ -1 +1 @@
a5f68f66472610b5beb4fe28669fbbfe83a32742be73cecad9b2ae28f8a17b30
954bf369935083c188c3b14e77ed89fc5ec4323cc5b0c67e4a2e48fcc278df45

View File

@ -1699,6 +1699,7 @@ frame_opt(A) ::= range_or_rows(X) BETWEEN frame_bound_s(Y) AND frame_bound_e(Z).
range_or_rows(A) ::= RANGE. { A = TK_RANGE; }
range_or_rows(A) ::= ROWS. { A = TK_ROWS; }
range_or_rows(A) ::= GROUPS. { A = TK_GROUPS;}
frame_bound_s(A) ::= frame_bound(X). { A = X; }

View File

@ -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, &regSize);
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 */

View File

@ -101,10 +101,12 @@ do_test $tn {
lappend myres [format $F [set r]]
}
set res2 {$res2}
set i 0
foreach r [set myres] r2 [set res2] {
if {[set r]<([set r2]-$T) || [set r]>([set r2]+$T)} {
error "list element [set i] does not match: got=[set r] expected=[set r2]"
}
incr i
}
set {} {}
} {}

File diff suppressed because it is too large Load Diff

View File

@ -1224,10 +1224,12 @@ do_test 9.3 {
lappend myres [format %.4f [set r]]
}
set res2 {1.0000 0.0000 1.0000 0.0000 1.0000 0.0000 4.0000 0.0000 4.0000 0.0000 6.0000 0.0000 7.0000 0.0000}
set i 0
foreach r [set myres] r2 [set res2] {
if {[set r]<([set r2]-0.0001) || [set r]>([set r2]+0.0001)} {
error "list element [set i] does not match: got=[set r] expected=[set r2]"
}
incr i
}
set {} {}
} {}
@ -1247,10 +1249,12 @@ do_test 9.6 {
lappend myres [format %.4f [set r]]
}
set res2 {0.0000 0.0000 0.0000}
set i 0
foreach r [set myres] r2 [set res2] {
if {[set r]<([set r2]-0.0001) || [set r]>([set r2]+0.0001)} {
error "list element [set i] does not match: got=[set r] expected=[set r2]"
}
incr i
}
set {} {}
} {}
@ -1262,10 +1266,12 @@ do_test 9.7 {
lappend myres [format %.4f [set r]]
}
set res2 {1.0000 1.0000 1.0000}
set i 0
foreach r [set myres] r2 [set res2] {
if {[set r]<([set r2]-0.0001) || [set r]>([set r2]+0.0001)} {
error "list element [set i] does not match: got=[set r] expected=[set r2]"
}
incr i
}
set {} {}
} {}

View File

@ -216,6 +216,7 @@ static Keyword aKeywordTable[] = {
{ "FULL", "TK_JOIN_KW", ALWAYS },
{ "GLOB", "TK_LIKE_KW", ALWAYS },
{ "GROUP", "TK_GROUP", ALWAYS },
{ "GROUPS", "TK_GROUPS", WINDOWFUNC },
{ "HAVING", "TK_HAVING", ALWAYS },
{ "IF", "TK_IF", ALWAYS },
{ "IGNORE", "TK_IGNORE", CONFLICT|TRIGGER },