Add support for RANGE window frames. Some cases still do not work.
FossilOrigin-Name: ffc32b246d92d53c66094afe11950b53ffab6a1c230c602eebbfedafb2eb57f4
This commit is contained in:
parent
f7b846e4ed
commit
72b9fdcf20
24
manifest
24
manifest
@ -1,5 +1,5 @@
|
||||
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
|
||||
@ -604,7 +604,7 @@ F src/where.c 8a207cb2ca6b99e1edb1e4bbff9b0504385a759cbf66180d1deb34d80ca4b799
|
||||
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
|
||||
@ -1197,7 +1197,7 @@ F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
|
||||
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
|
||||
@ -1684,13 +1684,13 @@ F test/window3.test ab76dbaeeb8ad8c4a43041a5e481520517ec5013e8898c93968155ac2e9e
|
||||
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
|
||||
@ -1812,7 +1812,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 2872702dacadad11f4fd7c437b77ef82763e840c9c92eefe87eac2491c38f963 62ac9cb07f5f2d14e716cd0f99f3f2fd34f6c9b4303619be8dfbca2eecf5b727
|
||||
R 7dae3a69d25cf61004b3b4b2809f5e75
|
||||
P 53ea550ce759ae4683bcf0ae3af655bfe04f85db70f74edee6bcab56d83bb041
|
||||
R 0f4fd4c61f7bf03ea8cd9c353e6d8bba
|
||||
U dan
|
||||
Z 21004fdd8de0d424bfa03883c4a4f17c
|
||||
Z a0f8d74a60721d288c2aa9343e8b2a5a
|
||||
|
@ -1 +1 @@
|
||||
53ea550ce759ae4683bcf0ae3af655bfe04f85db70f74edee6bcab56d83bb041
|
||||
ffc32b246d92d53c66094afe11950b53ffab6a1c230c602eebbfedafb2eb57f4
|
148
src/window.c
148
src/window.c
@ -559,6 +559,14 @@ void sqlite3WindowUpdate(
|
||||
}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 ){
|
||||
@ -927,14 +935,6 @@ Window *sqlite3WindowAlloc(
|
||||
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:
|
||||
@ -952,7 +952,7 @@ Window *sqlite3WindowAlloc(
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1539,6 +1539,39 @@ static void windowReadPeerValues(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** 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,
|
||||
@ -1555,6 +1588,9 @@ static int windowCodeOp(
|
||||
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 ){
|
||||
@ -1563,8 +1599,40 @@ static int windowCodeOp(
|
||||
}
|
||||
|
||||
if( regCountdown>0 ){
|
||||
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 ){
|
||||
windowAggFinal(pParse, pMWin, 0);
|
||||
@ -1610,6 +1678,10 @@ static int windowCodeOp(
|
||||
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;
|
||||
@ -1724,6 +1796,13 @@ static void windowCodeStep(
|
||||
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;
|
||||
@ -1745,8 +1824,9 @@ static void windowCodeStep(
|
||||
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;
|
||||
@ -1758,18 +1838,6 @@ static void windowCodeStep(
|
||||
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? */
|
||||
@ -1791,14 +1859,12 @@ static void windowCodeStep(
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1848,7 +1914,7 @@ static void windowCodeStep(
|
||||
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);
|
||||
}
|
||||
@ -1888,9 +1954,19 @@ static void windowCodeStep(
|
||||
if( pMWin->eStart==TK_FOLLOWING ){
|
||||
windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
|
||||
if( pMWin->eEnd!=TK_UNBOUNDED ){
|
||||
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 ){
|
||||
windowCodeOp(&s, WINDOW_AGGSTEP, regEnd, 0);
|
||||
@ -1900,12 +1976,27 @@ static void windowCodeStep(
|
||||
int addr;
|
||||
windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
|
||||
if( pMWin->eEnd!=TK_UNBOUNDED ){
|
||||
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 ){
|
||||
sqlite3VdbeJumpHere(v, addrPeerJump);
|
||||
}
|
||||
@ -1938,6 +2029,11 @@ static void windowCodeStep(
|
||||
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);
|
||||
|
@ -72,7 +72,8 @@ proc errorsql_test {tn sql} {
|
||||
if {$rc==0} {
|
||||
error "errorsql_test SQL did not cause an error!"
|
||||
}
|
||||
puts $::fd "# PG says \"[string trim $msg]\""
|
||||
set msg [lindex [split [string trim $msg] "\n"] 0]
|
||||
puts $::fd "# PG says $msg"
|
||||
set sql [string map {string_agg group_concat} $sql]
|
||||
puts $::fd "do_test $tn { catch { execsql {"
|
||||
puts $::fd " [string trim $sql]"
|
||||
|
@ -219,17 +219,17 @@ do_execsql_test 9.0 {
|
||||
} {
|
||||
1 1 2 1,2 3 1,2,3 4 2,3,4 5 3,4,5
|
||||
}
|
||||
do_catchsql_test 9.1 {
|
||||
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<5)
|
||||
SELECT x, group_concat(x) OVER (ORDER BY x RANGE 2 PRECEDING)
|
||||
FROM c;
|
||||
} {1 {RANGE must use only UNBOUNDED or CURRENT ROW}}
|
||||
|
||||
do_catchsql_test 9.2 {
|
||||
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<5)
|
||||
SELECT x, group_concat(x) OVER (ORDER BY x RANGE BETWEEN UNBOUNDED PRECEDING AND 2 FOLLOWING)
|
||||
FROM c;
|
||||
} {1 {RANGE must use only UNBOUNDED or CURRENT ROW}}
|
||||
#do_catchsql_test 9.1 {
|
||||
# WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<5)
|
||||
# SELECT x, group_concat(x) OVER (ORDER BY x RANGE 2 PRECEDING)
|
||||
# FROM c;
|
||||
#} {1 {RANGE must use only UNBOUNDED or CURRENT ROW}}
|
||||
#
|
||||
#do_catchsql_test 9.2 {
|
||||
# WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<5)
|
||||
# SELECT x, group_concat(x) OVER (ORDER BY x RANGE BETWEEN UNBOUNDED PRECEDING AND 2 FOLLOWING)
|
||||
# FROM c;
|
||||
#} {1 {RANGE must use only UNBOUNDED or CURRENT ROW}}
|
||||
|
||||
do_catchsql_test 9.3 {
|
||||
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<5)
|
||||
@ -262,7 +262,7 @@ foreach {tn frame} {
|
||||
SELECT count() OVER (
|
||||
ORDER BY x ROWS $frame
|
||||
) FROM c;
|
||||
" {1 {unsupported frame delimiter for ROWS}}
|
||||
" {1 {unsupported frame specification}}
|
||||
}
|
||||
|
||||
do_catchsql_test 9.8.1 {
|
||||
|
@ -89,6 +89,24 @@ foreach {tn frame} {
|
||||
"
|
||||
}
|
||||
|
||||
==========
|
||||
|
||||
execsql_test 2.0 {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1(a INTEGER, b INTEGER);
|
||||
INSERT INTO t1 VALUES
|
||||
(13, 26), (15, 30);
|
||||
}
|
||||
|
||||
foreach {tn frame} {
|
||||
1 { ORDER BY a RANGE BETWEEN 5 PRECEDING AND 5 FOLLOWING }
|
||||
2 { ORDER BY a RANGE BETWEEN 10 PRECEDING AND 5 PRECEDING }
|
||||
3 { ORDER BY a RANGE BETWEEN 2 FOLLOWING AND 3 FOLLOWING }
|
||||
} {
|
||||
execsql_test 2.$tn "SELECT a, sum(b) OVER win FROM t1 WINDOW win AS ($frame)"
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
@ -433,4 +433,25 @@ do_execsql_test 1.19.5 {
|
||||
SELECT a, b, min(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING ) FROM t3 ORDER BY 1, 2, 3;
|
||||
} {AA aa 102 AA aa 102 AA aa 102 AA aa 102 AA bb 102 AA bb 102 AA bb 102 AA bb 102 BB aa 102 BB aa 102 BB aa 102 BB aa 102 BB aa 102 BB aa 102 BB bb 102 BB bb 102 BB bb 102 BB bb 102 BB bb 102 BB bb 102 CC aa 102 CC aa 102 CC aa 102 CC aa 102 CC bb 102 CC bb 102 DD aa 102 DD aa 102 DD aa 102 DD bb 102 DD bb 102 DD bb 102 DD bb 102 EE aa 102 EE aa 102 EE bb 102 EE bb 102 EE bb 102 FF aa 102 FF aa 102 FF aa 102 FF aa 102 FF bb 113 FF bb 113 FF bb 113 FF bb 113 FF bb 113 FF bb 113 GG aa 113 GG aa 113 GG aa 113 GG aa 113 GG bb 113 GG bb 113 GG bb 113 GG bb 113 HH aa 113 HH aa 113 HH aa 113 HH bb 113 HH bb 113 HH bb 113 HH bb 113 HH bb 113 HH bb 113 II aa 113 II aa 113 II bb 113 II bb 113 II bb 113 II bb 113 II bb 113 JJ aa 257 JJ aa 257 JJ aa 257 JJ aa 257 JJ bb {} JJ bb {} JJ bb {} JJ bb {}}
|
||||
|
||||
#==========================================================================
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1(a INTEGER, b INTEGER);
|
||||
INSERT INTO t1 VALUES
|
||||
(13, 26), (15, 30);
|
||||
} {}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
SELECT a, sum(b) OVER win FROM t1 WINDOW win AS ( ORDER BY a RANGE BETWEEN 5 PRECEDING AND 5 FOLLOWING )
|
||||
} {13 56 15 56}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
SELECT a, sum(b) OVER win FROM t1 WINDOW win AS ( ORDER BY a RANGE BETWEEN 10 PRECEDING AND 5 PRECEDING )
|
||||
} {13 {} 15 {}}
|
||||
|
||||
do_execsql_test 2.3 {
|
||||
SELECT a, sum(b) OVER win FROM t1 WINDOW win AS ( ORDER BY a RANGE BETWEEN 2 FOLLOWING AND 3 FOLLOWING )
|
||||
} {13 30 15 {}}
|
||||
|
||||
finish_test
|
||||
|
@ -20,6 +20,11 @@ ifcapable !windowfunc
|
||||
execsql_test 1.0 {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1(a INTEGER, b INTEGER);
|
||||
INSERT INTO t1 VALUES(1, 1);
|
||||
INSERT INTO t1 VALUES(2, 2);
|
||||
INSERT INTO t1 VALUES(3, 3);
|
||||
INSERT INTO t1 VALUES(4, 4);
|
||||
INSERT INTO t1 VALUES(5, 5);
|
||||
}
|
||||
|
||||
foreach {tn frame} {
|
||||
@ -31,11 +36,13 @@ foreach {tn frame} {
|
||||
|
||||
5 "ORDER BY a GROUPS BETWEEN -1 PRECEDING AND 1 FOLLOWING"
|
||||
6 "ORDER BY a GROUPS BETWEEN 1 PRECEDING AND -1 FOLLOWING"
|
||||
|
||||
7 "ORDER BY a,b RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING"
|
||||
} {
|
||||
errorsql_test 1.$tn "
|
||||
SELECT a, sum(b) OVER (
|
||||
$frame
|
||||
) FROM t3 ORDER BY 1
|
||||
) FROM t1 ORDER BY 1
|
||||
"
|
||||
}
|
||||
|
||||
|
@ -23,48 +23,60 @@ ifcapable !windowfunc { finish_test ; return }
|
||||
do_execsql_test 1.0 {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1(a INTEGER, b INTEGER);
|
||||
INSERT INTO t1 VALUES(1, 1);
|
||||
INSERT INTO t1 VALUES(2, 2);
|
||||
INSERT INTO t1 VALUES(3, 3);
|
||||
INSERT INTO t1 VALUES(4, 4);
|
||||
INSERT INTO t1 VALUES(5, 5);
|
||||
} {}
|
||||
|
||||
# PG says "ERROR: frame starting offset must not be negative"
|
||||
# PG says ERROR: frame starting offset must not be negative
|
||||
do_test 1.1 { catch { execsql {
|
||||
SELECT a, sum(b) OVER (
|
||||
ORDER BY a ROWS BETWEEN -1 PRECEDING AND 1 FOLLOWING
|
||||
) FROM t3 ORDER BY 1
|
||||
) FROM t1 ORDER BY 1
|
||||
} } } 1
|
||||
|
||||
# PG says "ERROR: frame ending offset must not be negative"
|
||||
# PG says ERROR: frame ending offset must not be negative
|
||||
do_test 1.2 { catch { execsql {
|
||||
SELECT a, sum(b) OVER (
|
||||
ORDER BY a ROWS BETWEEN 1 PRECEDING AND -1 FOLLOWING
|
||||
) FROM t3 ORDER BY 1
|
||||
) FROM t1 ORDER BY 1
|
||||
} } } 1
|
||||
|
||||
# PG says "ERROR: invalid preceding or following size in window function"
|
||||
# PG says ERROR: invalid preceding or following size in window function
|
||||
do_test 1.3 { catch { execsql {
|
||||
SELECT a, sum(b) OVER (
|
||||
ORDER BY a RANGE BETWEEN -1 PRECEDING AND 1 FOLLOWING
|
||||
) FROM t3 ORDER BY 1
|
||||
) FROM t1 ORDER BY 1
|
||||
} } } 1
|
||||
|
||||
# PG says "ERROR: invalid preceding or following size in window function"
|
||||
# PG says ERROR: invalid preceding or following size in window function
|
||||
do_test 1.4 { catch { execsql {
|
||||
SELECT a, sum(b) OVER (
|
||||
ORDER BY a RANGE BETWEEN 1 PRECEDING AND -1 FOLLOWING
|
||||
) FROM t3 ORDER BY 1
|
||||
) FROM t1 ORDER BY 1
|
||||
} } } 1
|
||||
|
||||
# PG says "ERROR: frame starting offset must not be negative"
|
||||
# PG says ERROR: frame starting offset must not be negative
|
||||
do_test 1.5 { catch { execsql {
|
||||
SELECT a, sum(b) OVER (
|
||||
ORDER BY a GROUPS BETWEEN -1 PRECEDING AND 1 FOLLOWING
|
||||
) FROM t3 ORDER BY 1
|
||||
) FROM t1 ORDER BY 1
|
||||
} } } 1
|
||||
|
||||
# PG says "ERROR: frame ending offset must not be negative"
|
||||
# PG says ERROR: frame ending offset must not be negative
|
||||
do_test 1.6 { catch { execsql {
|
||||
SELECT a, sum(b) OVER (
|
||||
ORDER BY a GROUPS BETWEEN 1 PRECEDING AND -1 FOLLOWING
|
||||
) FROM t3 ORDER BY 1
|
||||
) FROM t1 ORDER BY 1
|
||||
} } } 1
|
||||
|
||||
# PG says ERROR: RANGE with offset PRECEDING/FOLLOWING requires exactly one ORDER BY column
|
||||
do_test 1.7 { catch { execsql {
|
||||
SELECT a, sum(b) OVER (
|
||||
ORDER BY a,b RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING
|
||||
) FROM t1 ORDER BY 1
|
||||
} } } 1
|
||||
|
||||
finish_test
|
||||
|
Loading…
Reference in New Issue
Block a user