Add support for "ROWS BETWEEN <expr> PRECEDING AND <expr> FOLLOWING" window

frames.

FossilOrigin-Name: 3a203660f1e4da3b8d2d605c494f4843f6e00752f28042b49e11d7d6550dd406
This commit is contained in:
dan 2018-05-23 20:55:37 +00:00
parent 72779425b5
commit c3a20c19a8
11 changed files with 452 additions and 137 deletions

View File

@ -1,5 +1,5 @@
C Merge\slatest\strunk\schanges\sinto\sthis\sbranch.
D 2018-05-22T20:36:12.808
C Add\ssupport\sfor\s"ROWS\sBETWEEN\s<expr>\sPRECEDING\sAND\s<expr>\sFOLLOWING"\swindow\nframes.
D 2018-05-23T20:55:37.621
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F Makefile.in bfc40f350586923e0419d2ea4b559c37ec10ee4b6e210e08c14401f8e340f0da
@ -435,8 +435,8 @@ F src/auth.c 6277d63837357549fe14e723490d6dc1a38768d71c795c5eb5c0f8a99f918f73
F src/backup.c faf17e60b43233c214aae6a8179d24503a61e83b
F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
F src/btree.c 8270813c8f0ca91b2802e88ded3755d04ee962a923d431c13bcb6cf3e0c18f63
F src/btree.h 448f15b98ea85dcf7e4eb76f731cadb89636c676ad25dfaac6de77cd66556598
F src/btree.c b8fc4fcf851316fc0b84d4aa46899d127df952c39cfeb067bc97036060df1138
F src/btree.h d46a8e31a4bd15572cdb6f2c940966f57b605b865628028c5eccf7d1bed83bac
F src/btreeInt.h 620ab4c7235f43572cf3ac2ac8723cbdf68073be4d29da24897c7b77dda5fd96
F src/build.c 50ff3e0fa07646b4d797aae0f773efcdb7602f6a5e2f5da27856503f35200889
F src/callback.c fe677cb5f5abb02f7a772a62a98c2f516426081df68856e8f2d5f950929b966a
@ -449,7 +449,7 @@ F src/delete.c b0f90749e22d5e41a12dbf940f4811138cf97da54b46b737089b93eb64a2896f
F src/expr.c bb57b0b5ba1351335091ce4ec43b40968746f03afd65c9e2920d7cbe4dc98133
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c d617daf66b5515e2b42c1405b2b4984c30ca50fb705ab164271a9bf66c69e331
F src/func.c b1a5122c69ef13c7bf0100e792ca539a36034c1b50476233ded6d2f72afcfbfc
F src/func.c eff9c15696cda3485df3ae52ce3663692a2cd506fba63a5f49f56cb204831021
F src/global.c 9bf034fd560bdd514715170ed8460bb7f823cec113f0569ef3f18a20c7ccd128
F src/hash.c a12580e143f10301ed5166ea4964ae2853d3905a511d4e0c44497245c7ce1f7a
F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4
@ -494,12 +494,12 @@ F src/printf.c 1d1b4a568a58d0f32a5ff26c6b98db8a6e1883467f343a6406263cacd2e60c21
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
F src/resolve.c 446f60b2e0d2440bb233d6a69a4ed0f2ad030a4e63ac4b3cfc0e98cf73d9c5a3
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
F src/select.c 1ebe775c0651bf357ab83b4c9b9139194149cfe1277dfa3e16f3ed73669b6b04
F src/select.c 8a7f842a049a3407079e0b0748de916dcd91c00377394b2e8b1aefc5972a0b2f
F src/shell.c.in 51c100206f4b7f86cd9affd80b764825e0edc36ca0190c442e4ca7994611bfe2
F src/sqlite.h.in 34be2d0d18bf4726538793bdc9854cd87f689fda4b3789515134cdbd68188cf4
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7
F src/sqliteInt.h a3f0edb26ebfee6107fa99f3c300e71018ad23addeeba0746a4ac62425e36f3f
F src/sqliteInt.h 735b04170551a899e8703421c376f98c19503b8210ad4cd2e0f35b85b6af595d
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
@ -564,7 +564,7 @@ F src/upsert.c ae4a4823b45c4daf87e8aea8c0f582a8844763271f5ed54ee5956c4c612734f4
F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5
F src/util.c d9eb0a6c4aae1b00a7369eadd7ca0bbe946cb4c953b6751aa20d357c2f482157
F src/vacuum.c 37730af7540033135909ecaee3667dddec043293428d8718546d0d64ba4a5025
F src/vdbe.c d83cfec9ebf523d5b2a8a3756ba8f23e39723725334a2e2e947e602ef6e6b278
F src/vdbe.c 89c76c95a24e2561f5f94ef8530bd0127d512ed56b62932047ef89076d58fa91
F src/vdbe.h d970d9738efdd09cb2df73e3a40856e7df13e88a3486789c49fcdd322c9eb8a2
F src/vdbeInt.h 3878856fab3a8e64d27d472909e391db9d82f4f8b902a1737a1f7f351299ff52
F src/vdbeapi.c 29d2baf9c1233131ec467d7bed1b7c8a03c27579048d768c4b04acf427838858
@ -582,7 +582,7 @@ F src/where.c 60ec752fcbe9f9e0271ac60548d159a540a1ee47a4f9fedc85e88a3d0e392dd1
F src/whereInt.h cbae2bcd37cfebdb7812a8b188cdb19634ced2b9346470d1c270556b0c33ea53
F src/wherecode.c 728c7f70731430ccdac807a79969873e1af6968bf1c4745dff3f9dd35f636cc8
F src/whereexpr.c e90b2e76dcabc81edff56633bf281bc01d93b71e0c81482dc06925ce39f5844a
F src/window.c 37eb02c2af935f207ba902ef25ec27d635b68bb1567f9e5994a6720bac1c093e
F src/window.c 1313e941d1e50a44594e6f3e12bc7d0fe6f092ea35c1f3884c31bd224ba66d29
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
@ -1615,8 +1615,8 @@ F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972
F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d
F test/win32nolock.test ac4f08811a562e45a5755e661f45ca85892bdbbc
F test/window1.test 5705337783d220b47f6fb4432264543b7557a05be8013d772f57d71f2fded271
F test/window2.tcl 7e0b692974a18ae0992dd2e76be83d8e1c6c5cac3190d84fa62911ab0e5c7896
F test/window2.test e1453371b605e54eeb2264fc3a4a23c5eba93e95f6c7f3230fce9d34b4b5e8a4
F test/window2.tcl 29e9bb16a52eb1e9e8f376519185af5c64eed88a8e6f0bee54237ba2971803a7
F test/window2.test f580e1cc96d1ccb6bb220d1e338525ee5541e45e2206ed9ca74417ba862d8a62
F test/with1.test 58475190cd8caaeebea8cfeb2a264ec97a0c492b8ffe9ad20cefbb23df462f96
F test/with2.test e0030e2f0267a910d6c0e4f46f2dfe941c1cc0d4f659ba69b3597728e7e8f1ab
F test/with3.test 5e8ce2c585170bbbc0544e2a01a4941fa0be173ba5265e5c92eb588cd99a232d
@ -1733,7 +1733,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 16168146b202915252f9375aef38e65ca20c5d4aa906e851d4d3a484db57562d c6071ac99cfa4b6272ac4d739fc61a85acb544f6c1c2ae67b31e92aadcc995bd
R a9163894154abe7467dbbc7b918577f5
P cdb68d2c64e453fdcd29437d5915c5c5ab6fbc7b5ffac52f4cb393f35b4a0124
R 20d653691e285483f3e3bdb0b082aa4b
U dan
Z 671adb87a45ec21df0f125088181e31d
Z e628ea8085262c078f67c7957c5a3c95

View File

@ -1 +1 @@
cdb68d2c64e453fdcd29437d5915c5c5ab6fbc7b5ffac52f4cb393f35b4a0124
3a203660f1e4da3b8d2d605c494f4843f6e00752f28042b49e11d7d6550dd406

View File

@ -5176,6 +5176,13 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
return rc;
}
void sqlite3BtreeSkipNext(BtCursor *pCur){
if( pCur->eState==CURSOR_VALID ){
pCur->eState = CURSOR_SKIPNEXT;
pCur->skipNext = 1;
}
}
/* Move the cursor to the last entry in the table. Return SQLITE_OK
** on success. Set *pRes to 0 if the cursor actually points to something
** or set *pRes to 1 if the table is empty.

View File

@ -301,6 +301,7 @@ struct BtreePayload {
int sqlite3BtreeInsert(BtCursor*, const BtreePayload *pPayload,
int flags, int seekResult);
int sqlite3BtreeFirst(BtCursor*, int *pRes);
void sqlite3BtreeSkipNext(BtCursor*);
int sqlite3BtreeLast(BtCursor*, int *pRes);
int sqlite3BtreeNext(BtCursor*, int flags);
int sqlite3BtreeEof(BtCursor*);

View File

@ -1513,6 +1513,27 @@ static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){
}
}
}
static void sumInverse(sqlite3_context *context, int argc, sqlite3_value**argv){
SumCtx *p;
int type;
assert( argc==1 );
UNUSED_PARAMETER(argc);
p = sqlite3_aggregate_context(context, sizeof(*p));
type = sqlite3_value_numeric_type(argv[0]);
if( p && type!=SQLITE_NULL ){
p->cnt--;
if( type==SQLITE_INTEGER ){
i64 v = sqlite3_value_int64(argv[0]);
p->rSum -= v;
if( (p->approx|p->overflow)==0 && sqlite3AddInt64(&p->iSum, -1*v) ){
p->overflow = 1;
}
}else{
p->rSum += sqlite3_value_double(argv[0]);
p->approx = 1;
}
}
}
static void sumFinalize(sqlite3_context *context){
SumCtx *p;
p = sqlite3_aggregate_context(context, 0);
@ -1873,12 +1894,12 @@ void sqlite3RegisterBuiltinFunctions(void){
FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ),
FUNCTION(substr, 2, 0, 0, substrFunc ),
FUNCTION(substr, 3, 0, 0, substrFunc ),
WAGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize),
WAGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ),
WAGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ),
WAGGREGATE(sum, 1, 0, 0, sumStep, sumInverse, sumFinalize),
WAGGREGATE(total, 1, 0, 0, sumStep, sumInverse, totalFinalize ),
WAGGREGATE(avg, 1, 0, 0, sumStep, sumInverse, avgFinalize ),
AGGREGATE2(count, 0, 0, 0, countStep, countFinalize,
SQLITE_FUNC_COUNT ),
WAGGREGATE(count, 1, 0, 0, countStep, countFinalize ),
WAGGREGATE(count, 1, 0, 0, countStep, 0, countFinalize ),
AGGREGATE(group_concat, 1, 0, 0, groupConcatStep, groupConcatFinalize,
groupConcatValue),
AGGREGATE(group_concat, 2, 0, 0, groupConcatStep, groupConcatFinalize,

View File

@ -5576,7 +5576,7 @@ static int selectWindowRewrite(Parse *pParse, Select *p){
}
#endif
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pMWin->iEphCsr, pMWin->nBufferCol);
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pMWin->iEphCsr, pSublist->nExpr);
}
return rc;
@ -6087,16 +6087,23 @@ int sqlite3Select(
if( pWin ){
int addrGosub = sqlite3VdbeMakeLabel(v);
int regGosub = ++pParse->nMem;
int addr;
int addr = 0;
int bLoop = 0;
sqlite3WindowCodeStep(pParse, p, pWInfo, regGosub, addrGosub);
sqlite3WindowCodeStep(pParse, p, pWInfo, regGosub, addrGosub, &bLoop);
sqlite3VdbeAddOp0(v, OP_Goto);
sqlite3VdbeResolveLabel(v, addrGosub);
addr = sqlite3VdbeAddOp1(v, OP_Rewind, pWin->iEphCsr);
if( bLoop ){
addr = sqlite3VdbeAddOp1(v, OP_Rewind, pWin->iEphCsr);
}else{
addr = sqlite3VdbeCurrentAddr(v);
}
selectInnerLoop(pParse, p, -1, &sSort, &sDistinct, pDest, addr+1, 0);
sqlite3VdbeAddOp2(v, OP_Next, pWin->iEphCsr, addr+1);
sqlite3VdbeJumpHere(v, addr);
if( bLoop ){
sqlite3VdbeAddOp2(v, OP_Next, pWin->iEphCsr, addr+1);
sqlite3VdbeJumpHere(v, addr);
}
sqlite3VdbeAddOp1(v, OP_Return, regGosub);
sqlite3VdbeJumpHere(v, addr-1); /* OP_Goto jumps here */

View File

@ -1723,9 +1723,9 @@ struct FuncDestructor {
{nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|extraFlags, \
SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xFinal,0,#zName, {0}}
#define WAGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \
#define WAGGREGATE(zName, nArg, arg, nc, xStep, xInverse, xFinal) \
{nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL), \
SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xFinal,0,#zName, {0}}
SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xFinal,xInverse,#zName, {0}}
/*
** All current savepoints are stored in a linked list starting at
@ -3496,7 +3496,7 @@ Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*);
void sqlite3WindowAttach(Parse*, Expr*, Window*);
int sqlite3WindowCompare(Parse*, Window*, Window*);
void sqlite3WindowCodeInit(Parse*, Window*);
void sqlite3WindowCodeStep(Parse*, Select*, WhereInfo*, int, int);
void sqlite3WindowCodeStep(Parse*, Select*, WhereInfo*, int, int, int*);
/*
** Assuming zIn points to the first byte of a UTF-8 character,

View File

@ -5018,6 +5018,7 @@ case OP_Rewind: { /* jump */
pCrsr = pC->uc.pCursor;
assert( pCrsr );
rc = sqlite3BtreeFirst(pCrsr, &res);
if( pOp->p5 ) sqlite3BtreeSkipNext(pCrsr);
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
}
@ -6273,7 +6274,8 @@ case OP_AggStep: {
assert( pCtx->pOut->flags==MEM_Null );
assert( pCtx->isError==0 );
assert( pCtx->skipFlag==0 );
(pCtx->pFunc->xSFunc)(pCtx,pCtx->argc,pCtx->argv); /* IMP: R-24505-23230 */
(pOp->p1 ? (pCtx->pFunc->xInverse) : (pCtx->pFunc->xSFunc))
(pCtx,pCtx->argc,pCtx->argv); /* IMP: R-24505-23230 */
if( pCtx->isError ){
if( pCtx->isError>0 ){
sqlite3VdbeError(p, "%s", sqlite3_value_text(pCtx->pOut));

View File

@ -25,8 +25,8 @@ void sqlite3WindowDelete(sqlite3 *db, Window *p){
Window *sqlite3WindowAlloc(
Parse *pParse,
int eType,
int eEnd, Expr *pEnd,
int eStart, Expr *pStart
int eStart, Expr *pStart,
int eEnd, Expr *pEnd
){
Window *pWin = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
@ -77,6 +77,279 @@ void sqlite3WindowCodeInit(Parse *pParse, Window *pWin){
}
}
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;
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);
}
static void windowCodeRowExprStep(
Parse *pParse,
Select *p,
WhereInfo *pWInfo,
int regGosub,
int addrGosub
){
Window *pMWin = p->pWin;
Vdbe *v = sqlite3GetVdbe(pParse);
Window *pWin;
int k;
int iSubCsr = p->pSrc->a[0].iCursor;
int nSub = p->pSrc->a[0].pTab->nCol;
int regFlushPart; /* Register for "Gosub flush_partition" */
int addrFlushPart; /* Label for "Gosub flush_partition" */
int addrDone; /* Label for "Gosub flush_partition_done" */
int reg = pParse->nMem+1;
int regRecord = reg+nSub;
int regRowid = regRecord+1;
int addr;
int csrPrec = pParse->nTab++;
int csrFollow = pParse->nTab++;
int regPrec; /* Value of <expr> PRECEDING */
int regFollow; /* Value of <expr> FOLLOWING */
int addrNext;
int addrGoto;
int addrIfPos1;
int addrIfPos2;
pParse->nMem += nSub + 2;
/* Allocate register and label for the "flush_partition" sub-routine. */
regFlushPart = ++pParse->nMem;
addrFlushPart = sqlite3VdbeMakeLabel(v);
addrDone = sqlite3VdbeMakeLabel(v);
regPrec = ++pParse->nMem;
regFollow = ++pParse->nMem;
/* Martial 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);
}
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 ){
ExprList *pPart = pMWin->pPartition;
int nPart = (pPart ? pPart->nExpr : 0);
int addrJump = 0;
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, addr+4, addr+2);
sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, addrFlushPart);
sqlite3VdbeAddOp3(v, OP_Copy, regNewPart, pMWin->regPart, nPart);
}
/* 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, addrFlushPart);
addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
/* flush_partition: */
sqlite3VdbeResolveLabel(v, addrFlushPart);
sqlite3VdbeAddOp2(v, OP_Once, 0, sqlite3VdbeCurrentAddr(v)+3);
sqlite3VdbeAddOp2(v, OP_OpenDup, csrPrec, pMWin->iEphCsr);
sqlite3VdbeAddOp2(v, OP_OpenDup, csrFollow, pMWin->iEphCsr);
sqlite3ExprCode(pParse, pMWin->pStart, regPrec);
sqlite3ExprCode(pParse, pMWin->pEnd, regFollow);
sqlite3VdbeAddOp2(v, OP_Null, 0, pMWin->regResult);
sqlite3VdbeAddOp2(v, OP_Null, 0, pMWin->regAccum);
/* If either regPrec or regFollow are not non-negative integers, throw an
** exception. */
windowCheckFrameValue(pParse, regPrec, 0);
windowCheckFrameValue(pParse, regFollow, 1);
sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, addrDone);
sqlite3VdbeAddOp2(v, OP_Rewind, csrPrec, addrDone);
sqlite3VdbeChangeP5(v, 1);
sqlite3VdbeAddOp2(v, OP_Rewind, csrFollow, addrDone);
sqlite3VdbeChangeP5(v, 1);
/* Invoke AggStep function for each window function using the row that
** csrFollow currently points to. Or, if csrFollow is already at EOF,
** do nothing. */
addrNext = sqlite3VdbeCurrentAddr(v);
sqlite3VdbeAddOp2(v, OP_Next, csrFollow, addrNext+2);
sqlite3VdbeAddOp0(v, OP_Goto);
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
int i;
for(i=0; i<pWin->nArg; i++){
sqlite3VdbeAddOp3(v, OP_Column, csrFollow, pWin->iArgCol+i, reg+i);
}
sqlite3VdbeAddOp3(v, OP_AggStep0, 0, reg, pWin->regAccum);
sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, (u8)pWin->nArg);
}
sqlite3VdbeJumpHere(v, addrNext+1);
addrIfPos1 = sqlite3VdbeAddOp3(v, OP_IfPos, regFollow, 0 , 1);
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
sqlite3VdbeAddOp3(v,
OP_AggFinal, pWin->regAccum, pWin->nArg, pWin->regResult
);
sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
}
sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)+2);
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrDone);
addrIfPos2 = sqlite3VdbeAddOp3(v, OP_IfPos, regPrec, 0 , 1);
sqlite3VdbeAddOp2(v, OP_Next, csrPrec, sqlite3VdbeCurrentAddr(v)+1);
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
int i;
for(i=0; i<pWin->nArg; i++){
sqlite3VdbeAddOp3(v, OP_Column, csrPrec, pWin->iArgCol+i, reg+i);
}
sqlite3VdbeAddOp3(v, OP_AggStep0, 1, reg, pWin->regAccum);
sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, (u8)pWin->nArg);
}
sqlite3VdbeJumpHere(v, addrIfPos2);
sqlite3VdbeJumpHere(v, addrIfPos1);
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNext);
/* flush_partition_done: */
sqlite3VdbeResolveLabel(v, addrDone);
sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
sqlite3VdbeAddOp1(v, OP_Return, regFlushPart);
/* Jump to here to skip over flush_partition */
sqlite3VdbeJumpHere(v, addrGoto);
}
static void windowCodeDefaultStep(
Parse *pParse,
Select *p,
WhereInfo *pWInfo,
int regGosub,
int addrGosub
){
Window *pMWin = p->pWin;
Vdbe *v = sqlite3GetVdbe(pParse);
Window *pWin;
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;
pParse->nMem += nSub + 2;
/* Martial 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( pMWin->regPart ){
ExprList *pPart = pMWin->pPartition;
int nPart = (pPart ? pPart->nExpr : 0);
ExprList *pOrderBy = pMWin->pOrderBy;
int nPeer = (pOrderBy ? pOrderBy->nExpr : 0);
int addrGoto = 0;
int addrJump = 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);
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, pWin->nArg);
sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult);
}
if( pOrderBy ){
addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
}
}
if( pOrderBy ){
int regNewPeer = reg + pMWin->nBufferCol + nPart;
int regPeer = pMWin->regPart + nPart;
KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0);
if( addrJump ) sqlite3VdbeJumpHere(v, addrJump);
addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer, nPeer);
sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
sqlite3VdbeAddOp3(v,
OP_AggFinal, pWin->regAccum, pWin->nArg, pWin->regResult
);
sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
}
if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto);
}
sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
sqlite3VdbeAddOp3(
v, OP_Copy, reg+pMWin->nBufferCol, pMWin->regPart, nPart+nPeer-1
);
sqlite3VdbeJumpHere(v, addrJump);
}
/* Invoke step function for window functions */
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
sqlite3VdbeAddOp3(v, OP_AggStep0, 0, reg+pWin->iArgCol, pWin->regAccum);
sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, (u8)pWin->nArg);
}
/* 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);
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, pWin->nArg);
sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult);
}
sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
}
/*
** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
**
@ -243,105 +516,22 @@ void sqlite3WindowCodeStep(
Select *p,
WhereInfo *pWInfo,
int regGosub,
int addrGosub
int addrGosub,
int *pbLoop
){
Vdbe *v = sqlite3GetVdbe(pParse);
Window *pWin;
Window *pMWin = p->pWin;
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;
pParse->nMem += nSub + 2;
/* Martial 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);
if( pMWin->eType==TK_ROWS
&& pMWin->eStart==TK_PRECEDING
&& pMWin->eEnd==TK_FOLLOWING
){
*pbLoop = 0;
windowCodeRowExprStep(pParse, p, pWInfo, regGosub, addrGosub);
return;
}
/* Check if this is the start of a new partition or peer group. */
if( pMWin->regPart ){
ExprList *pPart = pMWin->pPartition;
int nPart = (pPart ? pPart->nExpr : 0);
ExprList *pOrderBy = pMWin->pOrderBy;
int nPeer = (pOrderBy ? pOrderBy->nExpr : 0);
int addrGoto = 0;
int addrJump = 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);
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, pWin->nArg);
sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult);
}
if( pOrderBy ){
addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
}
}
if( pOrderBy ){
int regNewPeer = reg + pMWin->nBufferCol + nPart;
int regPeer = pMWin->regPart + nPart;
KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0);
if( addrJump ) sqlite3VdbeJumpHere(v, addrJump);
addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer, nPeer);
sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
sqlite3VdbeAddOp3(v,
OP_AggFinal, pWin->regAccum, pWin->nArg, pWin->regResult
);
sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
}
if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto);
}
sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
sqlite3VdbeAddOp3(
v, OP_Copy, reg+pMWin->nBufferCol, pMWin->regPart, nPart+nPeer-1
);
sqlite3VdbeJumpHere(v, addrJump);
}
/* Invoke step function for window functions */
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
sqlite3VdbeAddOp3(v, OP_AggStep0, 0, reg+pWin->iArgCol, pWin->regAccum);
sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, (u8)pWin->nArg);
}
/* 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);
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, pWin->nArg);
sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult);
}
sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
*pbLoop = 1;
windowCodeDefaultStep(pParse, p, pWInfo, regGosub, addrGosub);
}

View File

@ -136,41 +136,75 @@ execsql_test 1.3 {
SELECT sum(d) OVER (PARTITION BY b) FROM t1;
}
puts $::fd finish_test
==========
execsql_test 2.1 {
SELECT a, sum(d) OVER (
PARTITION BY b ORDER BY d
RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
ORDER BY d
ROWS BETWEEN 1000 PRECEDING AND 1 FOLLOWING
) FROM t1
}
execsql_test 2.2 {
SELECT a, sum(d) OVER (
ORDER BY b
RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
ORDER BY d
ROWS BETWEEN 1000 PRECEDING AND 1000 FOLLOWING
) FROM t1
}
execsql_test 2.3 {
SELECT a, sum(d) OVER (
ORDER BY d
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
ROWS BETWEEN 1 PRECEDING AND 1000 FOLLOWING
) FROM t1
}
execsql_test 2.4 {
SELECT a, sum(d) OVER (
ORDER BY d
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
) FROM t1
}
execsql_test 2.5 {
SELECT a, sum(d) OVER (
ORDER BY d
ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING
ROWS BETWEEN 1 PRECEDING AND 0 FOLLOWING
) FROM t1
}
execsql_test 2.6 {
SELECT a, sum(d) OVER (
PARTITION BY b
ORDER BY d
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
) FROM t1
}
execsql_test 2.7 {
SELECT a, sum(d) OVER (
PARTITION BY b
ORDER BY d
ROWS BETWEEN 0 PRECEDING AND 0 FOLLOWING
) FROM t1
}
puts $::fd finish_test
==========
execsql_test 3.1 {
SELECT a, sum(d) OVER (
PARTITION BY b ORDER BY d
RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
) FROM t1
}
execsql_test 3.2 {
SELECT a, sum(d) OVER (
ORDER BY b
RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
) FROM t1
}
execsql_test 3.3 {
SELECT a, sum(d) OVER (
ORDER BY d
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) FROM t1
}

View File

@ -42,6 +42,59 @@ do_execsql_test 1.3 {
SELECT sum(d) OVER (PARTITION BY b) FROM t1;
} {12 12 12 9 9 9}
#==========================================================================
do_execsql_test 2.1 {
SELECT a, sum(d) OVER (
ORDER BY d
ROWS BETWEEN 1000 PRECEDING AND 1 FOLLOWING
) FROM t1
} {1 3 2 6 3 10 4 15 5 21 6 21}
do_execsql_test 2.2 {
SELECT a, sum(d) OVER (
ORDER BY d
ROWS BETWEEN 1000 PRECEDING AND 1000 FOLLOWING
) FROM t1
} {1 21 2 21 3 21 4 21 5 21 6 21}
do_execsql_test 2.3 {
SELECT a, sum(d) OVER (
ORDER BY d
ROWS BETWEEN 1 PRECEDING AND 1000 FOLLOWING
) FROM t1
} {1 21 2 21 3 20 4 18 5 15 6 11}
do_execsql_test 2.4 {
SELECT a, sum(d) OVER (
ORDER BY d
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
) FROM t1
} {1 3 2 6 3 9 4 12 5 15 6 11}
do_execsql_test 2.5 {
SELECT a, sum(d) OVER (
ORDER BY d
ROWS BETWEEN 1 PRECEDING AND 0 FOLLOWING
) FROM t1
} {1 1 2 3 3 5 4 7 5 9 6 11}
do_execsql_test 2.6 {
SELECT a, sum(d) OVER (
PARTITION BY b
ORDER BY d
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
) FROM t1
} {2 6 4 12 6 10 1 4 3 9 5 8}
do_execsql_test 2.7 {
SELECT a, sum(d) OVER (
PARTITION BY b
ORDER BY d
ROWS BETWEEN 0 PRECEDING AND 0 FOLLOWING
) FROM t1
} {2 2 4 4 6 6 1 1 3 3 5 5}
finish_test
#==========================================================================