-C Merge\slatest\strunk\schanges\sinto\sthis\sbranch.
-D 2019-03-09T07:38:16.247
+C Add\ssupport\sfor\sRANGE\swindow\sframes.\sSome\scases\sstill\sdo\snot\swork.
+D 2019-03-09T20:49:17.662
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F Makefile.in 236d2739dc3e823c3c909bca2d6cef93009bafbefd7018a8f3281074ecb92954
F src/whereInt.h 5f14db426ca46a83eabab1ae9aa6d4b8f27504ad35b64c290916289b1ddb2e88
F src/wherecode.c ce7b21e1be2b981d62683fc59c4ca73a04a7ff2f1ebec23d41baf2da2349afd6
F src/whereexpr.c 36b47f7261d6b6f1a72d774c113b74beddf6745aba1018e64b196e29db233442
-F src/window.c e2e56938f05c93795bcfa6088e6c1799c9113dc62991e8f79665a37d92f69f21
+F src/window.c bbdf43afee5bb4178170baae8111d68dd4d6499610028d49c86979a377ac365c
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
F test/permutations.test 52d2c37fe8cc07ec7362024c214b04bb69432995b3a984a3fbabc60fa6ada3ee
-F test/pg_common.tcl 55e8bf84d563fd72d617a05c4421c8dea2700775794c5bd8971caa1cb0ef6b26
+F test/pg_common.tcl 74f0f59f8eb87450f2b0284ae8c1155699f07f2f3af690f4069137b30c21a009
F test/pragma.test c267bf02742c823a191960895b3d52933cebd7beee26757d1ed694f213fcd867
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
F test/pragma3.test 8300aa9c63cff1027006ca34bf413a148abbd6dcd471fa9a1ded322fe18c0df9
F test/window4.tcl 5fbaab489677914ee5686b2008426e336daf88a2f58be7df92757f780a5ebf91
F test/window4.test 0e3d595705f0e74c2fd77658b388d8547f6b612c7eb4fb2ad4dd5725a62bfc12
F test/window5.test d328dd18221217c49c144181975eea17339eaeaf0e9aa558cee3afb84652821e
-F test/window6.test 5eae4ae7a590ccf1e605880969ca0bad3955616ac91cad3031baea38748badb3
+F test/window6.test 604890f2b0f976339e6a1774cd90d48840e57a54b4f21a8b4b3047aa2c9787d1
F test/window7.tcl 6a1210f05d40ec89c22960213a22cd3f98d4e2f2eb20646c83c8c30d4d76108f
F test/window7.test ce7f865241fdd1c5c4db869cd7bb2986c3be836bc2e73649a6846dd920f63e0f
-F test/window8.tcl 7b7da7cf72510247f9bcafc7e4af85f6192e0eb3c4fcaeedd83afa497bf46367
-F test/window8.test e7f8169b4f1c0aed2a2386aaa058c7417778db49c77786fe2edfc69de81c1884
-F test/windowerr.tcl 5211e12818c013af2180c8da8f6dc007ef746e4cf386a5f2a87a5c340cb76829
-F test/windowerr.test 8a38c774e41c1b0c76e20d035c979040a69e104fcc6f10d9d3d2d50daa3cfafd
+F test/window8.tcl ec44b7ae3add64907950293a02bd1371ead3b8623b16b79076d8adce83b48771
+F test/window8.test 54a2c5baa388a4db35607de34a9dcce5a441a8d43bfae159ff067b765793f943
+F test/windowerr.tcl 4f0b111cdbb8de401a8135cd116604fb8add13042bb47a1f1496dd47a690e864
+F test/windowerr.test 461bc504d4af0ae16a081e0c3bdb3724356cdcfb07bdc4b1b2dc4ad91b69bb7e
F test/windowfault.test 12ceb6bbb355d13e8fcd88c5731a57256dfdf77b9a7ae20842a76fcd4623df5b
F test/with1.test a07b5aad7f77acdf13e52e8814ea94606fcc72e9ea4c99baf293e9d7c63940be
F test/with2.test e0030e2f0267a910d6c0e4f46f2dfe941c1cc0d4f659ba69b3597728e7e8f1ab
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 2872702dacadad11f4fd7c437b77ef82763e840c9c92eefe87eac2491c38f963 62ac9cb07f5f2d14e716cd0f99f3f2fd34f6c9b4303619be8dfbca2eecf5b727
-R 7dae3a69d25cf61004b3b4b2809f5e75
+P 53ea550ce759ae4683bcf0ae3af655bfe04f85db70f74edee6bcab56d83bb041
+R 0f4fd4c61f7bf03ea8cd9c353e6d8bba
U dan
-Z 21004fdd8de0d424bfa03883c4a4f17c
+Z a0f8d74a60721d288c2aa9343e8b2a5a
}else{
sqlite3WindowChain(pParse, pWin, pList);
}
+ if( (pWin->eType==TK_RANGE)
+ && (pWin->pStart || pWin->pEnd)
+ && (pWin->pOrderBy==0 || pWin->pOrderBy->nExpr!=1)
+ ){
+ sqlite3ErrorMsg(pParse,
+ "RANGE with offset PRECEDING/FOLLOWING requires one ORDER BY expression"
+ );
+ }else
if( pFunc->funcFlags & SQLITE_FUNC_WINDOW ){
sqlite3 *db = pParse->db;
if( pWin->pFilter ){
eType = TK_RANGE;
}
- /* If a frame is declared "RANGE" (not "ROWS"), then it may not use
- ** either "<expr> PRECEDING" or "<expr> FOLLOWING".
- */
- if( eType==TK_RANGE && (pStart!=0 || pEnd!=0) ){
- sqlite3ErrorMsg(pParse, "RANGE must use only UNBOUNDED or CURRENT ROW");
- goto windowAllocErr;
- }
-
/* Additionally, the
** starting boundary type may not occur earlier in the following list than
** the ending boundary type:
if( (eStart==TK_CURRENT && eEnd==TK_PRECEDING)
|| (eStart==TK_FOLLOWING && (eEnd==TK_PRECEDING || eEnd==TK_CURRENT))
){
- sqlite3ErrorMsg(pParse, "unsupported frame delimiter for ROWS");
+ sqlite3ErrorMsg(pParse, "unsupported frame specification");
goto windowAllocErr;
}
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{
}
}
+/*
+** This function is called as part of generating VM programs for RANGE
+** offset PRECEDING/FOLLOWING frame boundaries. It generates code equivalent
+** to:
+**
+** if( csr1.peerVal + regVal >= csr2.peerVal ) goto lbl;
+** if( csr1.rowid >= csr2.rowid ) goto lbl;
+*/
+static void windowCodeRangeTest(
+ WindowCodeArg *p,
+ int op, /* OP_Ge or OP_Gt */
+ int csr1,
+ int regVal,
+ int csr2,
+ int lbl
+){
+ Parse *pParse = p->pParse;
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ int reg1 = sqlite3GetTempReg(pParse);
+ int reg2 = sqlite3GetTempReg(pParse);
+ windowReadPeerValues(p, csr1, reg1);
+ windowReadPeerValues(p, csr2, reg2);
+ sqlite3VdbeAddOp3(v, OP_Add, reg1, regVal, reg1);
+ sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1);
+ sqlite3VdbeAddOp2(v, OP_Rowid, csr1, reg1);
+ sqlite3VdbeAddOp2(v, OP_Rowid, csr2, reg2);
+ sqlite3VdbeAddOp3(v, OP_Gt, reg2, lbl, reg1);
+ sqlite3ReleaseTempReg(pParse, reg1);
+ sqlite3ReleaseTempReg(pParse, reg2);
+
+ assert( op==OP_Ge || op==OP_Gt || op==OP_Lt || op==OP_Le );
+}
+
static int windowCodeOp(
WindowCodeArg *p,
int op,
int addrGoto = 0;
int bPeer = (pMWin->eType!=TK_ROWS);
+ int lblDone = sqlite3VdbeMakeLabel(pParse);
+ int addrNextRange = 0;
+
/* Special case - WINDOW_AGGINVERSE is always a no-op if the frame
** starts with UNBOUNDED PRECEDING. */
if( op==WINDOW_AGGINVERSE && pMWin->eStart==TK_UNBOUNDED ){
}
if( regCountdown>0 ){
- addrIf = sqlite3VdbeAddOp3(v, OP_IfPos, regCountdown, 0, 1);
+ if( pMWin->eType==TK_RANGE ){
+ addrNextRange = sqlite3VdbeCurrentAddr(v);
+
+ switch( op ){
+ case WINDOW_RETURN_ROW: {
+ assert( 0 );
+ break;
+ }
+
+ case WINDOW_AGGINVERSE: {
+ if( pMWin->eStart==TK_FOLLOWING ){
+ windowCodeRangeTest(
+ p, OP_Le, p->current.csr, regCountdown, p->start.csr, lblDone
+ );
+ }else{
+ windowCodeRangeTest(
+ p, OP_Ge, p->start.csr, regCountdown, p->current.csr, lblDone
+ );
+ }
+ break;
+ }
+
+ case WINDOW_AGGSTEP: {
+ windowCodeRangeTest(
+ p, OP_Gt, p->end.csr, regCountdown, p->current.csr, lblDone
+ );
+ break;
+ }
+ }
+
+ }else{
+ addrIf = sqlite3VdbeAddOp3(v, OP_IfPos, regCountdown, 0, 1);
+ }
}
if( op==WINDOW_RETURN_ROW ){
sqlite3ReleaseTempRange(pParse, regTmp, nReg);
}
+ if( addrNextRange ){
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNextRange);
+ }
+ sqlite3VdbeResolveLabel(v, lblDone);
if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto);
if( addrIf ) sqlite3VdbeJumpHere(v, addrIf);
return ret;
int regNewPeer = 0;
WindowCodeArg s;
+ assert( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_CURRENT
+ || pMWin->eStart==TK_FOLLOWING || pMWin->eStart==TK_UNBOUNDED
+ );
+ assert( pMWin->eEnd==TK_FOLLOWING || pMWin->eEnd==TK_CURRENT
+ || pMWin->eEnd==TK_UNBOUNDED || pMWin->eEnd==TK_PRECEDING
+ );
+
memset(&s, 0, sizeof(WindowCodeArg));
s.pParse = pParse;
s.pMWin = pMWin;
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 this is not a "ROWS BETWEEN ..." frame, then allocate arrays of
+ ** registers to store a copies of the ORDER BY expressions for the
+ ** main loop, and for each cursor (start, current and end). */
if( pMWin->eType!=TK_ROWS ){
int nPeer = (pOrderBy ? pOrderBy->nExpr : 0);
regNewPeer = reg + pMWin->nBufferCol;
s.end.reg = pParse->nMem+1; pParse->nMem += nPeer;
}
- assert( pMWin->eStart==TK_PRECEDING
- || pMWin->eStart==TK_CURRENT
- || pMWin->eStart==TK_FOLLOWING
- || pMWin->eStart==TK_UNBOUNDED
- );
- assert( pMWin->eEnd==TK_FOLLOWING
- || pMWin->eEnd==TK_CURRENT
- || pMWin->eEnd==TK_UNBOUNDED
- || pMWin->eEnd==TK_PRECEDING
- );
-
-
/* Load the column values for the row returned by the sub-select
** into an array of registers starting at reg. Assemble them into
** a record in register regRecord. TODO: An optimization here? */
int regNewPart = reg + pMWin->nBufferCol;
KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0);
- addrIf = sqlite3VdbeAddOp1(v, OP_If, pMWin->regFirst);
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);
addrGosubFlush = sqlite3VdbeAddOp1(v, OP_Gosub, regFlushPart);
VdbeComment((v, "call flush_partition"));
- sqlite3VdbeJumpHere(v, addrIf);
sqlite3VdbeAddOp3(v, OP_Copy, regNewPart, pMWin->regPart, nPart-1);
}
addrShortcut = sqlite3VdbeAddOp0(v, OP_Goto);
sqlite3VdbeJumpHere(v, addrGe);
}
- if( pMWin->eStart==TK_FOLLOWING && regEnd ){
+ if( pMWin->eStart==TK_FOLLOWING && pMWin->eType!=TK_RANGE && regEnd ){
assert( pMWin->eEnd==TK_FOLLOWING );
sqlite3VdbeAddOp3(v, OP_Subtract, regStart, regEnd, regStart);
}
if( pMWin->eStart==TK_FOLLOWING ){
windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
if( pMWin->eEnd!=TK_UNBOUNDED ){
- windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 0);
- windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ if( pMWin->eType==TK_RANGE ){
+ int lbl = sqlite3VdbeMakeLabel(pParse);
+ int addrNext = sqlite3VdbeCurrentAddr(v);
+ windowCodeRangeTest(&s, OP_Ge, s.current.csr, regEnd, s.end.csr, lbl);
+ windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNext);
+ sqlite3VdbeResolveLabel(v, lbl);
+ }else{
+ windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 0);
+ windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ }
}
}else
if( pMWin->eEnd==TK_PRECEDING ){
int addr;
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, 0, 0);
- windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
- if( regEnd ) sqlite3VdbeJumpHere(v, addr);
+ if( pMWin->eType==TK_RANGE ){
+ int lbl;
+ addr = sqlite3VdbeCurrentAddr(v);
+ if( regEnd ){
+ lbl = sqlite3VdbeMakeLabel(pParse);
+ windowCodeRangeTest(&s, OP_Ge, s.current.csr, regEnd, s.end.csr, lbl);
+ }
+ windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
+ windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ if( regEnd ){
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
+ sqlite3VdbeResolveLabel(v, lbl);
+ }
+ }else{
+ if( regEnd ) addr = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0, 1);
+ windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
+ windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ if( regEnd ) sqlite3VdbeJumpHere(v, addr);
+ }
}
}
if( addrPeerJump ){
int addrBreak2;
int addrBreak3;
windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
+ if( pMWin->eType==TK_RANGE ){
+ addrStart = sqlite3VdbeCurrentAddr(v);
+ addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 1);
+ addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1);
+ }else
if( pMWin->eEnd==TK_UNBOUNDED ){
addrStart = sqlite3VdbeCurrentAddr(v);
addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regStart, 1);