diff --git a/manifest b/manifest index 1220173c37..4b50211203 100644 --- a/manifest +++ b/manifest @@ -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\sPRECEDING\sAND\s\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 diff --git a/manifest.uuid b/manifest.uuid index 1fe3511414..8f6226467b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -cdb68d2c64e453fdcd29437d5915c5c5ab6fbc7b5ffac52f4cb393f35b4a0124 \ No newline at end of file +3a203660f1e4da3b8d2d605c494f4843f6e00752f28042b49e11d7d6550dd406 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index a73896e161..316fa701d6 100644 --- a/src/btree.c +++ b/src/btree.c @@ -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. diff --git a/src/btree.h b/src/btree.h index b5bf9f7564..20fdbdae2e 100644 --- a/src/btree.h +++ b/src/btree.h @@ -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*); diff --git a/src/func.c b/src/func.c index ea1fffaf0b..c5836f55f1 100644 --- a/src/func.c +++ b/src/func.c @@ -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, diff --git a/src/select.c b/src/select.c index 12d6e5ff39..bff5d36b54 100644 --- a/src/select.c +++ b/src/select.c @@ -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 */ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 45e4934c2e..35db193975 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -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, diff --git a/src/vdbe.c b/src/vdbe.c index 7ab1044552..9f8af702ed 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -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)); diff --git a/src/window.c b/src/window.c index 4eb04db91c..6af6d4c7ce 100644 --- a/src/window.c +++ b/src/window.c @@ -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 PRECEDING */ + int regFollow; /* Value of 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; kpPartition ){ + 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; inArg; 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; inArg; 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; kregPart ){ + 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; keType==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); } diff --git a/test/window2.tcl b/test/window2.tcl index 54fa9d6bc9..4d2503715b 100644 --- a/test/window2.tcl +++ b/test/window2.tcl @@ -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 } diff --git a/test/window2.test b/test/window2.test index 40a652cf14..db4eb44dc7 100644 --- a/test/window2.test +++ b/test/window2.test @@ -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 #==========================================================================