diff --git a/manifest b/manifest index b33fe88ab5..2da13c9943 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sminor\sproblems\son\sthis\sbranch. -D 2018-05-19T14:15:29.031 +C Begin\sadding\ssupport\sfor\smore\sesoteric\swindow\sframes. +D 2018-05-21T19:45:11.880 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in bfc40f350586923e0419d2ea4b559c37ec10ee4b6e210e08c14401f8e340f0da @@ -493,12 +493,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 34a5cb9e6a37d4f7e25160ba0caeb02bccc1abee462805ba5f3fde9abc266873 +F src/select.c 1ebe775c0651bf357ab83b4c9b9139194149cfe1277dfa3e16f3ed73669b6b04 F src/shell.c.in 53affa90711f280342f7238f9c9aa9dcaed321ec6218a18043cf92154ef8a704 F src/sqlite.h.in 34be2d0d18bf4726538793bdc9854cd87f689fda4b3789515134cdbd68188cf4 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7 -F src/sqliteInt.h 6b3400a90f179542045ed318c780bf73675b19b3de437506859adf998a41e125 +F src/sqliteInt.h a3f0edb26ebfee6107fa99f3c300e71018ad23addeeba0746a4ac62425e36f3f F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34 @@ -581,7 +581,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 33d3751eb1442ce8a7e428e028cbc6c220359446a7ab2a9514244c3edfea1b63 +F src/window.c da24f2e57a704dd8e0ce96df18e7442145582c65b4eb1c3176367e530d665928 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d @@ -1614,8 +1614,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 f9df2fded8de70fc4252030775351b42440bc10aa2eb07d2f625e5d14a666775 -F test/window2.test 57c0500f98a08fd372f7fecf60f0d2ade58ad3373dad5031063d0a278fd79b18 +F test/window2.tcl 19a7d45c4502f00917649a171a4eb1da3670657f5cb102bce7a813e15a112b3f +F test/window2.test 23daf252647d780f046a2e612d7f0492d4fe589b3302fd6c062b0abfff3743bf F test/with1.test 58475190cd8caaeebea8cfeb2a264ec97a0c492b8ffe9ad20cefbb23df462f96 F test/with2.test e0030e2f0267a910d6c0e4f46f2dfe941c1cc0d4f659ba69b3597728e7e8f1ab F test/with3.test 5e8ce2c585170bbbc0544e2a01a4941fa0be173ba5265e5c92eb588cd99a232d @@ -1732,7 +1732,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 c9f0f140941660ff368e5bb5752d54feb1964b7a9eac986d4bfb8f24a1c20d86 -R 6dddf8e93774e8d8cc7600c6ceb6a6b5 +P 19c2e4b2f164521eab84cb0a0e12984be9431eaedd001dd3671e9ea1a6212353 +R 977b49583a548e1f3f37fbf1de8b5833 U dan -Z 12927ac87a28c4acd3b751d346125a3b +Z 314fa758fa6aadc36a0f111ad4e55744 diff --git a/manifest.uuid b/manifest.uuid index e8182e6ce4..2c19d62e69 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -19c2e4b2f164521eab84cb0a0e12984be9431eaedd001dd3671e9ea1a6212353 \ No newline at end of file +bc4b81d60d40583de0f929730159011c1a7696802532ebd02220de3ace94a60d \ No newline at end of file diff --git a/src/select.c b/src/select.c index cbe3183c0f..12d6e5ff39 100644 --- a/src/select.c +++ b/src/select.c @@ -530,14 +530,6 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){ return 0; } -/* Forward reference */ -static KeyInfo *keyInfoFromExprList( - Parse *pParse, /* Parsing context */ - ExprList *pList, /* Form the KeyInfo object from this ExprList */ - int iStart, /* Begin with this column of pList */ - int nExtra /* Add this many extra columns to the end */ -); - /* ** An instance of this object holds information (beyond pParse and pSelect) ** needed to load the next result row that is to be added to the sorter. @@ -679,7 +671,7 @@ static void pushOntoSorter( memset(pKI->aSortOrder, 0, pKI->nKeyField); /* Makes OP_Jump testable */ sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO); testcase( pKI->nAllField > pKI->nKeyField+2 ); - pOp->p4.pKeyInfo = keyInfoFromExprList(pParse, pSort->pOrderBy, nOBSat, + pOp->p4.pKeyInfo = sqlite3KeyInfoFromExprList(pParse,pSort->pOrderBy,nOBSat, pKI->nAllField-pKI->nKeyField-1); addrJmp = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v); @@ -1349,7 +1341,7 @@ int sqlite3KeyInfoIsWriteable(KeyInfo *p){ return p->nRef==1; } ** function is responsible for seeing that this structure is eventually ** freed. */ -static KeyInfo *keyInfoFromExprList( +KeyInfo *sqlite3KeyInfoFromExprList( Parse *pParse, /* Parsing context */ ExprList *pList, /* Form the KeyInfo object from this ExprList */ int iStart, /* Begin with this column of pList */ @@ -5088,7 +5080,7 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){ "argument"); pFunc->iDistinct = -1; }else{ - KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList, 0, 0); + KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pE->x.pList,0,0); sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0, (char*)pKeyInfo, P4_KEYINFO); } @@ -6008,7 +6000,8 @@ int sqlite3Select( */ if( sSort.pOrderBy ){ KeyInfo *pKeyInfo; - pKeyInfo = keyInfoFromExprList(pParse, sSort.pOrderBy, 0, pEList->nExpr); + pKeyInfo = sqlite3KeyInfoFromExprList( + pParse, sSort.pOrderBy, 0, pEList->nExpr); sSort.iECursor = pParse->nTab++; sSort.addrSortIndex = sqlite3VdbeAddOp4(v, OP_OpenEphemeral, @@ -6042,9 +6035,9 @@ int sqlite3Select( if( p->selFlags & SF_Distinct ){ sDistinct.tabTnct = pParse->nTab++; sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenEphemeral, - sDistinct.tabTnct, 0, 0, - (char*)keyInfoFromExprList(pParse, p->pEList,0,0), - P4_KEYINFO); + sDistinct.tabTnct, 0, 0, + (char*)sqlite3KeyInfoFromExprList(pParse, p->pEList,0,0), + P4_KEYINFO); sqlite3VdbeChangeP5(v, BTREE_UNORDERED); sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED; }else{ @@ -6052,22 +6045,15 @@ int sqlite3Select( } if( !isAgg && pGroupBy==0 ){ - Window *pMWin = p->pWin; /* Master window object (or NULL) */ - int regPart = 0; + Window *pWin = p->pWin; /* Master window object (or NULL) */ /* No aggregate functions and no GROUP BY clause */ u16 wctrlFlags = (sDistinct.isTnct ? WHERE_WANT_DISTINCT : 0); assert( WHERE_USE_LIMIT==SF_FixedLimit ); wctrlFlags |= p->selFlags & SF_FixedLimit; - if( pMWin ){ - int nPart = (pMWin->pPartition ? pMWin->pPartition->nExpr : 0); - nPart += (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0); - if( nPart ){ - regPart = pParse->nMem+1; - pParse->nMem += nPart; - sqlite3VdbeAddOp3(v, OP_Null, 0, regPart, regPart+nPart-1); - } + if( pWin ){ + sqlite3WindowCodeInit(pParse, pWin); } /* Begin the database scan. */ @@ -6098,112 +6084,18 @@ int sqlite3Select( } assert( p->pEList==pEList ); - if( pMWin ){ - 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 regGosub = regRowid+1; + if( pWin ){ + int addrGosub = sqlite3VdbeMakeLabel(v); + int regGosub = ++pParse->nMem; int addr; - int addrGosub; - pParse->nMem += nSub + 3; - - /* Martial the row returned by the sub-select into an array of - ** registers. */ - for(k=0; kpPartition; - 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 = keyInfoFromExprList(pParse, pPart, 0, 0); - addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, 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 = regPart + nPart; - - KeyInfo *pKeyInfo = keyInfoFromExprList(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); - } - - addrGosub = sqlite3VdbeAddOp1(v, OP_Gosub, regGosub); - sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr); - sqlite3VdbeAddOp3( - v, OP_Copy, reg+pMWin->nBufferCol, 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, sqlite3VdbeCurrentAddr(v)+2); + sqlite3WindowCodeStep(pParse, p, pWInfo, regGosub, addrGosub); sqlite3VdbeAddOp0(v, OP_Goto); - if( regPart ){ - sqlite3VdbeJumpHere(v, addrGosub); - } - addr = sqlite3VdbeAddOp1(v, OP_Rewind, pMWin->iEphCsr); + sqlite3VdbeResolveLabel(v, addrGosub); + addr = sqlite3VdbeAddOp1(v, OP_Rewind, pWin->iEphCsr); selectInnerLoop(pParse, p, -1, &sSort, &sDistinct, pDest, addr+1, 0); - sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, addr+1); + sqlite3VdbeAddOp2(v, OP_Next, pWin->iEphCsr, addr+1); sqlite3VdbeJumpHere(v, addr); sqlite3VdbeAddOp1(v, OP_Return, regGosub); sqlite3VdbeJumpHere(v, addr-1); /* OP_Goto jumps here */ @@ -6346,7 +6238,7 @@ int sqlite3Select( ** will be converted into a Noop. */ sAggInfo.sortingIdx = pParse->nTab++; - pKeyInfo = keyInfoFromExprList(pParse, pGroupBy, 0, sAggInfo.nColumn); + pKeyInfo = sqlite3KeyInfoFromExprList(pParse,pGroupBy,0,sAggInfo.nColumn); addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen, sAggInfo.sortingIdx, sAggInfo.nSortingColumn, 0, (char*)pKeyInfo, P4_KEYINFO); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 7f3d37ba48..45e4934c2e 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3485,6 +3485,7 @@ struct Window { FuncDef *pFunc; int nArg; + int regPart; Expr *pOwner; /* Expression object this window is attached to */ int nBufferCol; /* Number of columns in buffer table */ int iArgCol; /* Offset of first argument for this function */ @@ -3494,6 +3495,8 @@ void sqlite3WindowDelete(sqlite3*, Window*); 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); /* ** Assuming zIn points to the first byte of a UTF-8 character, @@ -4202,6 +4205,8 @@ KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int,int); void sqlite3KeyInfoUnref(KeyInfo*); KeyInfo *sqlite3KeyInfoRef(KeyInfo*); KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*); +KeyInfo *sqlite3KeyInfoFromExprList(Parse*, ExprList*, int, int); + #ifdef SQLITE_DEBUG int sqlite3KeyInfoIsWriteable(KeyInfo*); #endif diff --git a/src/window.c b/src/window.c index d2d6f47668..5b1a9bd424 100644 --- a/src/window.c +++ b/src/window.c @@ -66,4 +66,216 @@ int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2){ return 0; } +void sqlite3WindowCodeInit(Parse *pParse, Window *pWin){ + Vdbe *v = sqlite3GetVdbe(pParse); + int nPart = (pWin->pPartition ? pWin->pPartition->nExpr : 0); + nPart += (pWin->pOrderBy ? pWin->pOrderBy->nExpr : 0); + if( nPart ){ + pWin->regPart = pParse->nMem+1; + pParse->nMem += nPart; + sqlite3VdbeAddOp3(v, OP_Null, 0, pWin->regPart, pWin->regPart+nPart-1); + } +} + +/* +** 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). +** +** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +** +** One way is to just reverse the sort order and do as for BETWEEN +** UNBOUNDED PRECEDING AND CURRENT ROW. But that is not quite the same for +** things like group_concat(). And perhaps other user defined aggregates +** as well. +** +** ... +** if( new partition ){ +** Gosub flush_partition; +** ResetSorter eph-table +** } +** AggStep +** Insert (record into eph-table) +** sqlite3WhereEnd() +** Gosub flush_partition +** +** flush_partition: +** OpenDup (csr -> csr2) +** foreach (record in eph-table) { +** if( new peer ){ +** while( csr2!=csr ){ +** AggStep (xInverse) +** Next (csr2) +** } +** } +** AggFinal (xValue) +** Gosub addrGosub +** } +** +**======================================================================== +** +** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW +** ... +** if( new partition ){ +** AggFinal (xFinalize) +** } +** AggStep +** AggFinal (xValue) +** Gosub addrGosub +** sqlite3WhereEnd() +** +** ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING +** ROWS BETWEEN CURRENT ROW AND CURRENT ROW +** ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +** +**======================================================================== +** +** ROWS BETWEEN UNBOUNDED PRECEDING AND PRECEDING +** ROWS BETWEEN PRECEDING AND PRECEDING +** ROWS BETWEEN PRECEDING AND CURRENT ROW +** ROWS BETWEEN UNBOUNDED PRECEDING AND FOLLOWING +** ROWS BETWEEN PRECEDING AND FOLLOWING +** ROWS BETWEEN CURRENT ROW AND FOLLOWING +** ROWS BETWEEN FOLLOWING AND FOLLOWING +** ROWS BETWEEN PRECEDING AND UNBOUNDED FOLLOWING +** ROWS BETWEEN FOLLOWING AND UNBOUNDED FOLLOWING +** +** Cases that involve PRECEDING or FOLLOWING. +** +** ... +** Insert (record in eph-table) +** sqlite3WhereEnd() +** +*/ +void sqlite3WindowCodeStep( + Parse *pParse, + Select *p, + WhereInfo *pWInfo, + int regGosub, + int addrGosub +){ + 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; 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); +} + diff --git a/test/window2.tcl b/test/window2.tcl index 9c567f24e3..b81c3ae1e1 100644 --- a/test/window2.tcl +++ b/test/window2.tcl @@ -39,13 +39,18 @@ proc execsql {sql} { } #puts $lSql - set ret [list] + set ret "" foreach stmt $lSql { set res [pg_exec $::db $stmt] set err [pg_result $res -error] if {$err!=""} { error $err } for {set i 0} {$i < [pg_result $res -numTuples]} {incr i} { - lappend ret {*}[pg_result $res -getTuple $i] + if {$i==0} { + set ret [pg_result $res -getTuple 0] + } else { + append ret " [pg_result $res -getTuple $i]" + } + # lappend ret {*}[pg_result $res -getTuple $i] } pg_result $res -clear } @@ -89,6 +94,15 @@ puts $::fd [string trimleft " puts $::fd "" } +proc -- {args} { + puts $::fd "# $args" +} + +proc ========== {args} { + puts $::fd "#[string repeat = 74]" + puts $::fd "" +} + proc finish_test {} { puts $::fd finish_test close $::fd @@ -122,6 +136,30 @@ 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 + ) FROM t1 +} + +execsql_test 2.2 { + SELECT a, sum(d) OVER ( + ORDER BY b + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + ) FROM t1 +} + +execsql_test 2.3 { + SELECT a, sum(d) OVER ( + ORDER BY d + ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING + ) FROM t1 +} + finish_test diff --git a/test/window2.test b/test/window2.test index 02b47aee71..9db7ee0baf 100644 --- a/test/window2.test +++ b/test/window2.test @@ -32,14 +32,38 @@ do_execsql_test 1.0 { do_execsql_test 1.1 { SELECT c, sum(d) OVER (PARTITION BY b ORDER BY c) FROM t1; -} {four 4 six 10 two 12 five 5 one 6 three 9} +} {four 4 six 10 two 12 five 5 one 6 three 9} do_execsql_test 1.2 { SELECT sum(d) OVER () FROM t1; -} {21 21 21 21 21 21} +} {21 21 21 21 21 21} do_execsql_test 1.3 { SELECT sum(d) OVER (PARTITION BY b) FROM t1; -} {12 12 12 9 9 9} +} {12 12 12 9 9 9} + +finish_test +#========================================================================== + +do_execsql_test 2.1 { + SELECT a, sum(d) OVER ( + PARTITION BY b ORDER BY d + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + ) FROM t1 +} {2 12 4 10 6 6 1 9 3 8 5 5} + +do_execsql_test 2.2 { + SELECT a, sum(d) OVER ( + ORDER BY b + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + ) FROM t1 +} {2 21 4 21 6 21 1 9 3 9 5 9} + +do_execsql_test 2.3 { + SELECT a, sum(d) OVER ( + ORDER BY d + ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING + ) FROM t1 +} {1 21 2 21 3 21 4 21 5 21 6 21} finish_test