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