diff --git a/manifest b/manifest index e6173ed2ce..34429f047a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Allow\smin()\sand\smax()\sto\sbe\sused\sas\swindow\sfunctions. -D 2018-05-30T20:44:58.014 +C Allow\san\sentire\spartition\sto\sbe\scached\sin\sa\stemp\stable\sfor\sall\stypes\sof\swindow\nframes.\sThis\sis\srequired\sby\snth_value()\sand\sothers. +D 2018-06-01T21:00:08.284 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in bfc40f350586923e0419d2ea4b559c37ec10ee4b6e210e08c14401f8e340f0da @@ -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 1f2b1590db468bde0ae2c53fe8b89f7a47afd8fd66a65a1385ffe6ce6f292279 +F src/window.c 59f519e2090a930b73f0789e44e481bf5a27d256d22ed74dee100ea14ad3bb02 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d @@ -1736,7 +1736,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 e74c6e91859ec395c12ba2742542ff176a1d8364dcfb66b862240746bef54efc -R c4e13ad00b1be186d1a741c62965ae01 +P c16125a884a9131b707ac20033968c4c3177ea79625a15efb64d754568c6c7a0 +R 3ad4cd9edcd57b310020dff4b3e91b5b U dan -Z 73f6b911c1a6e43e389c2921969fdc8b +Z 0764619e321410e8611ab0fa83a4bd79 diff --git a/manifest.uuid b/manifest.uuid index adf1a6034d..b17b1d1123 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c16125a884a9131b707ac20033968c4c3177ea79625a15efb64d754568c6c7a0 \ No newline at end of file +b5b18f661341d8d450147e62d321791c706f16c0550bcd98eec3e0220c039189 \ No newline at end of file diff --git a/src/window.c b/src/window.c index 2745a0d077..69efa05330 100644 --- a/src/window.c +++ b/src/window.c @@ -193,6 +193,59 @@ static void windowAggFinal(Parse *pParse, Window *pMWin, int bFinal){ } } +static void windowPartitionCache( + Parse *pParse, + Select *p, + WhereInfo *pWInfo, + int regFlushPart, + int lblFlushPart +){ + Window *pMWin = p->pWin; + Vdbe *v = sqlite3GetVdbe(pParse); + Window *pWin; + 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; + + pParse->nMem += nSub + 2; + + /* Martial the row returned by the sub-select into an array of + ** registers. */ + for(k=0; kpPartition ){ + int addr; + ExprList *pPart = pMWin->pPartition; + int nPart = (pPart ? pPart->nExpr : 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); + sqlite3VdbeAddOp3(v, OP_Jump, addr+2, addr+4, addr+2); + sqlite3VdbeAddOp3(v, OP_Copy, regNewPart, pMWin->regPart, nPart-1); + sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart); + } + + /* 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); +} /* ** ROWS BETWEEN PRECEDING AND FOLLOWING @@ -329,15 +382,13 @@ static void windowCodeRowExprStep( 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 lblFlushPart; /* Label for "Gosub flush_partition" */ int lblFlushDone; /* Label for "Gosub flush_partition_done" */ - int reg = pParse->nMem+1; - int regRecord = reg+nSub; - int regRowid = regRecord+1; + int regArg; + int nArg; int addr; int csrStart = pParse->nTab++; int csrEnd = pParse->nTab++; @@ -373,8 +424,6 @@ static void windowCodeRowExprStep( bRange = 1; } - pParse->nMem += nSub + 2; - /* Allocate register and label for the "flush_partition" sub-routine. */ regFlushPart = ++pParse->nMem; lblFlushPart = sqlite3VdbeMakeLabel(v); @@ -383,37 +432,8 @@ static void windowCodeRowExprStep( regStart = ++pParse->nMem; regEnd = ++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 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); - sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart); - 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, lblFlushPart); addrGoto = sqlite3VdbeAddOp0(v, OP_Goto); /* Start of "flush_partition" */ @@ -443,9 +463,13 @@ static void windowCodeRowExprStep( } /* Initialize the accumulator register for each window function to NULL */ + nArg = 0; for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); + nArg = MAX(nArg, pWin->nArg); } + regArg = pParse->nMem+1; + pParse->nMem += nArg; sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, lblFlushDone); sqlite3VdbeAddOp2(v, OP_Rewind, csrStart, lblFlushDone); @@ -462,7 +486,7 @@ static void windowCodeRowExprStep( } sqlite3VdbeAddOp2(v, OP_Next, csrEnd, sqlite3VdbeCurrentAddr(v)+2); addr = sqlite3VdbeAddOp0(v, OP_Goto); - windowAggStep(pParse, pMWin, csrEnd, 0, reg); + windowAggStep(pParse, pMWin, csrEnd, 0, regArg); if( pMWin->eEnd==TK_UNBOUNDED ){ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop); sqlite3VdbeJumpHere(v, addr); @@ -528,7 +552,7 @@ static void windowCodeRowExprStep( addrJumpHere = sqlite3VdbeAddOp0(v, OP_Goto); } sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+1); - windowAggStep(pParse, pMWin, csrStart, 1, reg); + windowAggStep(pParse, pMWin, csrStart, 1, regArg); if( bRange ){ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrJumpHere-1); } @@ -550,6 +574,146 @@ static void windowCodeRowExprStep( sqlite3VdbeJumpHere(v, addrGoto); } +/* +** 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; ipWin; + Vdbe *v = sqlite3GetVdbe(pParse); + Window *pWin; + int k; + int addr; + ExprList *pPart = pMWin->pPartition; + ExprList *pOrderBy = pMWin->pOrderBy; + int nPeer = pOrderBy->nExpr; + int regNewPeer; + + int addrGoto; /* Address of Goto used to jump flush_par.. */ + int addrRewind; /* Address of Rewind that starts loop */ + int regFlushPart; + int lblFlushPart; + int csrLead; + int regCtr; + int regArg; /* Register array to martial function args */ + int nArg; + + assert( (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT) + ); + + regNewPeer = pParse->nMem+1; + pParse->nMem += nPeer; + + /* Allocate register and label for the "flush_partition" sub-routine. */ + regFlushPart = ++pParse->nMem; + lblFlushPart = sqlite3VdbeMakeLabel(v); + + csrLead = pParse->nTab++; + regCtr = ++pParse->nMem; + + windowPartitionCache(pParse, p, pWInfo, regFlushPart, lblFlushPart); + addrGoto = sqlite3VdbeAddOp0(v, OP_Goto); + + /* Start of "flush_partition" */ + sqlite3VdbeResolveLabel(v, lblFlushPart); + sqlite3VdbeAddOp2(v, OP_Once, 0, sqlite3VdbeCurrentAddr(v)+2); + sqlite3VdbeAddOp2(v, OP_OpenDup, csrLead, pMWin->iEphCsr); + + /* Initialize the accumulator register for each window function to NULL */ + nArg = 0; + for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ + sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); + nArg = MAX(nArg, pWin->nArg); + } + regArg = pParse->nMem+1; + pParse->nMem += nArg; + + sqlite3VdbeAddOp2(v, OP_Integer, 0, regCtr); + addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, csrLead); + sqlite3VdbeAddOp1(v, OP_Rewind, pMWin->iEphCsr); + + if( pOrderBy ){ + int addrJump; /* Address of OP_Jump below */ + 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; kiEphCsr, sqlite3VdbeCurrentAddr(v)-3); + sqlite3VdbeJumpHere(v, addrJump); + } + + windowAggStep(pParse, pMWin, csrLead, 0, regArg); + sqlite3VdbeAddOp2(v, OP_AddImm, regCtr, 1); + + sqlite3VdbeAddOp2(v, OP_Next, csrLead, addrRewind+2); + + windowAggFinal(pParse, pMWin, 1); + + sqlite3VdbeAddOp3(v, OP_IfPos, regCtr, sqlite3VdbeCurrentAddr(v)+2 , 1); + sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+3); + sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub); + sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)-3); + + sqlite3VdbeJumpHere(v, addrRewind); + sqlite3VdbeJumpHere(v, addrRewind+1); + 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 ** @@ -569,6 +733,20 @@ static void windowCodeRowExprStep( ** 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, @@ -742,15 +920,26 @@ void sqlite3WindowCodeStep( ){ Window *pMWin = p->pWin; + *pbLoop = 0; if( (pMWin->eType==TK_ROWS && (pMWin->eStart!=TK_UNBOUNDED||pMWin->eEnd!=TK_CURRENT||!pMWin->pOrderBy)) || (pMWin->eStart==TK_CURRENT&&pMWin->eEnd==TK_UNBOUNDED&&pMWin->pOrderBy) ){ - *pbLoop = 0; windowCodeRowExprStep(pParse, p, pWInfo, regGosub, addrGosub); return; } +#if 0 + if( pMWin->eType==TK_RANGE + && pMWin->eStart==TK_UNBOUNDED + && pMWin->eEnd==TK_CURRENT + && pMWin->pOrderBy + ){ + windowCodeCacheStep(pParse, p, pWInfo, regGosub, addrGosub); + return; + } +#endif + *pbLoop = 1; windowCodeDefaultStep(pParse, p, pWInfo, regGosub, addrGosub); }