1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Add support for chaining of WINDOW definitions.

FossilOrigin-Name: c155125fd5dddb438c09d40f5137c47d88defb7a6ede4261f09d7bdaad250d37
This commit is contained in:
dan
2019-02-16 17:27:51 +00:00
parent 9c7e44cddd
commit e7c9ca41b2
8 changed files with 1934 additions and 1346 deletions

View File

@ -1,5 +1,5 @@
C Improved\soversized\scell\sdetection\swhen\supdating\sptrmap\spages\sin\nbalance_nonroot().
D 2019-02-14T15:27:12.650
C Add\ssupport\sfor\schaining\sof\sWINDOW\sdefinitions.
D 2019-02-16T17:27:51.404
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F Makefile.in 178d8eb6840771149cee40b322d1b3be30d330198c522c903c1b66fb5a1bfca4
@ -503,7 +503,7 @@ F src/os_win.c 85d9e532d0444ab6c16d7431490c2e279e282aa0917b0e988996b1ae0de5c5a0
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c 38022624ac9fba1f601d3068d7c393fcc909727fccab556242c93d9c7897b640
F src/pager.h 217921e81eb5fe455caa5cda96061959706bcdd29ddb57166198645ef7822ac3
F src/parse.y 741a270b7f2f85bc5d026d06fb5a9ccba5335304ff2831e1cb44b36cd0da6006
F src/parse.y deae85a1917e53e8533e7eed55ffb4405e2e375c022afb1cc3fecb280ee5cd7a
F src/pcache.c 696a01f1a6370c1b50a09c15972bc3bee3333f8fcd1f2da8e9a76b1b062c59ee
F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
F src/pcache1.c a72804486dfa8e4b6bc30d666c97ecf1155f91a4351fc6e48ea4097e4eb304fb
@ -519,7 +519,7 @@ F src/shell.c.in f2c1adbee3f6f36686b4a38d2168ebfc25298b4ad1e6d95199fc4e95b539251
F src/sqlite.h.in 7da74fd5bd7a9dbe92297060f036935520b26e240457287c5e67c7b9db51a986
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 960f1b86c3610fa23cb6a267572a97dcf286e77aa0dd3b9b23292ffaa1ea8683
F src/sqliteInt.h 8628c582eafa5ca93523cdaa7c45b2f38a11132e419c7923850706da7fa36bc2
F src/sqliteInt.h f44de7fea38d4fd03ee8db7f851f2727b701d4ec3fc21a8335e8d3888dda7176
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
@ -603,7 +603,7 @@ F src/where.c 8a207cb2ca6b99e1edb1e4bbff9b0504385a759cbf66180d1deb34d80ca4b799
F src/whereInt.h 5f14db426ca46a83eabab1ae9aa6d4b8f27504ad35b64c290916289b1ddb2e88
F src/wherecode.c a571d8d7c19d6db786a201f2df8788b320fefcb2842f2a1eb9a85b85e91bc35f
F src/whereexpr.c 36b47f7261d6b6f1a72d774c113b74beddf6745aba1018e64b196e29db233442
F src/window.c df2456386e0b1553a8d1fcf3a0ddc4c058fe2c650ea8c74b6bf8862082ddafc9
F src/window.c a96e58c91e77690726512e1dc5de2f95ece975097171e8554a9e8dbd3cdfb0d9
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
@ -1675,11 +1675,11 @@ F test/win32heap.test 10fd891266bd00af68671e702317726375e5407561d859be1aa04696f2
F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972
F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d
F test/win32nolock.test ac4f08811a562e45a5755e661f45ca85892bdbbc
F test/window1.test 2798c8249e0f122c9bacce6aa7324765a5cd9106e49e7aacc81f6033d281577b
F test/window1.test 4d98700cf287144be87942b587431fed6d0a262ba8ee72fae7ee860ca9ea5282
F test/window2.tcl 9bfa842d8a62b0d36dc8c1b5972206393c43847433c6d75940b87fec93ce3143
F test/window2.test 8e6d2a1b9f54dfebee1cde961c8590cd87b4db45c50f44947a211e1b63c2a05e
F test/window3.tcl 577a3b1ff913208e5248c04dab9df17fd760ce159a752789e26d0cb4a5f91823
F test/window3.test e274b7f8952ca4ed25996e0e45c047192b066e0aaff2a822d4293c8c4f1d8d98
F test/window3.tcl 3bf7fc5b86024f47bb5834efc90015c9a64f57d696b82e26a1a4b648b7cd058a
F test/window3.test ee56c741e934a0b872fffd7de99f137f9c7788d50885f99f54da4075d951e8ee
F test/window4.tcl 511425f6b0abf9b953df54cc9c7295cc7c25d78f4ed6f7a74b094eec0120eccb
F test/window4.test c5d6bf3403e4ade2f19df2afe4c16f29fb817c392c6c1c8017edb7165c191a62
F test/window5.test d328dd18221217c49c144181975eea17339eaeaf0e9aa558cee3afb84652821e
@ -1804,7 +1804,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 167b91df77fff1a84791f6ab5f72239b90475475be690a838248119b6dd312f0
R 5fce60e5e60143c7168638176073d3e9
U drh
Z 1127274df05b3c005a32360404b93266
P aa61435a4c3800f023788081c1342ad6a05a41449a424fa6c039d5ca46072256
R f8f3f55ce4dbb9bcc0042a3bde2239b1
T *branch * window-functions
T *sym-window-functions *
T -sym-trunk *
U dan
Z b3b312efff88db643f5cb6a825d2a2ac

View File

@ -1 +1 @@
aa61435a4c3800f023788081c1342ad6a05a41449a424fa6c039d5ca46072256
c155125fd5dddb438c09d40f5137c47d88defb7a6ede4261f09d7bdaad250d37

View File

@ -1633,13 +1633,14 @@ wqlist(A) ::= wqlist(A) COMMA nm(X) eidlist_opt(Y) AS LP select(Z) RP. {
windowdefn_list(A) ::= windowdefn(Z). { A = Z; }
windowdefn_list(A) ::= windowdefn_list(Y) COMMA windowdefn(Z). {
assert( Z!=0 );
sqlite3WindowChain(pParse, Z, Y);
Z->pNextWin = Y;
A = Z;
}
%type windowdefn {Window*}
%destructor windowdefn {sqlite3WindowDelete(pParse->db, $$);}
windowdefn(A) ::= nm(X) AS window(Y). {
windowdefn(A) ::= nm(X) AS LP window(Y) RP. {
if( ALWAYS(Y) ){
Y->zName = sqlite3DbStrNDup(pParse->db, X.z, X.n);
}
@ -1667,19 +1668,27 @@ windowdefn(A) ::= nm(X) AS window(Y). {
%type frame_bound_e {struct FrameBound}
%destructor frame_bound_e {sqlite3ExprDelete(pParse->db, $$.pExpr);}
window(A) ::= LP part_opt(X) orderby_opt(Y) frame_opt(Z) RP. {
window(A) ::= PARTITION BY nexprlist(X) orderby_opt(Y) frame_opt(Z). {
A = sqlite3WindowAssemble(pParse, Z, X, Y, 0);
}
window(A) ::= nm(W) PARTITION BY nexprlist(X) orderby_opt(Y) frame_opt(Z). {
A = sqlite3WindowAssemble(pParse, Z, X, Y, &W);
}
window(A) ::= ORDER BY sortlist(Y) frame_opt(Z). {
A = sqlite3WindowAssemble(pParse, Z, 0, Y, 0);
}
window(A) ::= nm(W) ORDER BY sortlist(Y) frame_opt(Z). {
A = sqlite3WindowAssemble(pParse, Z, 0, Y, &W);
}
window(A) ::= frame_opt(Z). {
A = Z;
if( ALWAYS(A) ){
A->pPartition = X;
A->pOrderBy = Y;
}
}
window(A) ::= nm(W) frame_opt(Z). {
A = sqlite3WindowAssemble(pParse, Z, 0, 0, &W);
}
part_opt(A) ::= PARTITION BY nexprlist(X). { A = X; }
part_opt(A) ::= . { A = 0; }
frame_opt(A) ::= . {
A = sqlite3WindowAlloc(pParse, TK_RANGE, TK_UNBOUNDED, 0, TK_CURRENT, 0);
A = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0);
}
frame_opt(A) ::= range_or_rows(X) frame_bound_s(Y). {
A = sqlite3WindowAlloc(pParse, X, Y.eType, Y.pExpr, TK_CURRENT, 0);
@ -1707,7 +1716,7 @@ window_clause(A) ::= WINDOW windowdefn_list(B). { A = B; }
%type over_clause {Window*}
%destructor over_clause {sqlite3WindowDelete(pParse->db, $$);}
over_clause(A) ::= filter_opt(W) OVER window(Z). {
over_clause(A) ::= filter_opt(W) OVER LP window(Z) RP. {
A = Z;
assert( A!=0 );
A->pFilter = W;

View File

@ -3555,11 +3555,13 @@ struct TreeView {
*/
struct Window {
char *zName; /* Name of window (may be NULL) */
char *zBase; /* Name of base window for chaining (may be NULL) */
ExprList *pPartition; /* PARTITION BY clause */
ExprList *pOrderBy; /* ORDER BY clause */
u8 eType; /* TK_RANGE or TK_ROWS */
u8 eStart; /* UNBOUNDED, CURRENT, PRECEDING or FOLLOWING */
u8 eEnd; /* UNBOUNDED, CURRENT, PRECEDING or FOLLOWING */
u8 bImplicitFrame; /* True if frame was implicitly specified */
Expr *pStart; /* Expression for "<expr> PRECEDING" */
Expr *pEnd; /* Expression for "<expr> FOLLOWING" */
Window *pNextWin; /* Next window function belonging to this SELECT */
@ -3591,6 +3593,8 @@ void sqlite3WindowUpdate(Parse*, Window*, Window*, FuncDef*);
Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p);
Window *sqlite3WindowListDup(sqlite3 *db, Window *p);
void sqlite3WindowFunctions(void);
void sqlite3WindowChain(Parse*, Window*, Window*);
Window *sqlite3WindowAssemble(Parse*, Window*, ExprList*, ExprList*, Token*);
#else
# define sqlite3WindowDelete(a,b)
# define sqlite3WindowFunctions()

View File

@ -512,6 +512,17 @@ void sqlite3WindowFunctions(void){
sqlite3InsertBuiltinFuncs(aWindowFuncs, ArraySize(aWindowFuncs));
}
static Window *windowFind(Parse *pParse, Window *pList, const char *zName){
Window *p;
for(p=pList; p; p=p->pNextWin){
if( sqlite3StrICmp(p->zName, zName)==0 ) break;
}
if( p==0 ){
sqlite3ErrorMsg(pParse, "no such window: %s", zName);
}
return p;
}
/*
** This function is called immediately after resolving the function name
** for a window function within a SELECT statement. Argument pList is a
@ -536,14 +547,8 @@ void sqlite3WindowUpdate(
FuncDef *pFunc /* Window function definition */
){
if( pWin->zName && pWin->eType==0 ){
Window *p;
for(p=pList; p; p=p->pNextWin){
if( sqlite3StrICmp(p->zName, pWin->zName)==0 ) break;
}
if( p==0 ){
sqlite3ErrorMsg(pParse, "no such window: %s", pWin->zName);
return;
}
Window *p = windowFind(pParse, pList, pWin->zName);
if( p==0 ) return;
pWin->pPartition = sqlite3ExprListDup(pParse->db, p->pPartition, 0);
pWin->pOrderBy = sqlite3ExprListDup(pParse->db, p->pOrderBy, 0);
pWin->pStart = sqlite3ExprDup(pParse->db, p->pStart, 0);
@ -551,6 +556,8 @@ void sqlite3WindowUpdate(
pWin->eStart = p->eStart;
pWin->eEnd = p->eEnd;
pWin->eType = p->eType;
}else{
sqlite3WindowChain(pParse, pWin, pList);
}
if( pFunc->funcFlags & SQLITE_FUNC_WINDOW ){
sqlite3 *db = pParse->db;
@ -856,6 +863,7 @@ void sqlite3WindowDelete(sqlite3 *db, Window *p){
sqlite3ExprDelete(db, p->pEnd);
sqlite3ExprDelete(db, p->pStart);
sqlite3DbFree(db, p->zName);
sqlite3DbFree(db, p->zBase);
sqlite3DbFree(db, p);
}
}
@ -899,9 +907,10 @@ Window *sqlite3WindowAlloc(
Expr *pEnd /* End window size if TK_FOLLOWING or PRECEDING */
){
Window *pWin = 0;
int bImplicitFrame = 0;
/* Parser assures the following: */
assert( eType==TK_RANGE || eType==TK_ROWS );
assert( eType==0 || eType==TK_RANGE || eType==TK_ROWS );
assert( eStart==TK_CURRENT || eStart==TK_PRECEDING
|| eStart==TK_UNBOUNDED || eStart==TK_FOLLOWING );
assert( eEnd==TK_CURRENT || eEnd==TK_FOLLOWING
@ -909,6 +918,10 @@ Window *sqlite3WindowAlloc(
assert( (eStart==TK_PRECEDING || eStart==TK_FOLLOWING)==(pStart!=0) );
assert( (eEnd==TK_FOLLOWING || eEnd==TK_PRECEDING)==(pEnd!=0) );
if( eType==0 ){
bImplicitFrame = 1;
eType = TK_RANGE;
}
/* If a frame is declared "RANGE" (not "ROWS"), then it may not use
** either "<expr> PRECEDING" or "<expr> FOLLOWING".
@ -944,6 +957,7 @@ Window *sqlite3WindowAlloc(
pWin->eType = eType;
pWin->eStart = eStart;
pWin->eEnd = eEnd;
pWin->bImplicitFrame = bImplicitFrame;
pWin->pEnd = sqlite3WindowOffsetExpr(pParse, pEnd);
pWin->pStart = sqlite3WindowOffsetExpr(pParse, pStart);
return pWin;
@ -954,6 +968,69 @@ windowAllocErr:
return 0;
}
/*
** Attach PARTITION and ORDER BY clauses pPartition and pOrderBy to window
** pWin. Also, if parameter pBase is not NULL, set pWin->zBase to the
** equivalent nul-terminated string.
*/
Window *sqlite3WindowAssemble(
Parse *pParse,
Window *pWin,
ExprList *pPartition,
ExprList *pOrderBy,
Token *pBase
){
if( pWin ){
pWin->pPartition = pPartition;
pWin->pOrderBy = pOrderBy;
if( pBase ){
pWin->zBase = sqlite3DbStrNDup(pParse->db, pBase->z, pBase->n);
}
}else{
sqlite3ExprListDelete(pParse->db, pPartition);
sqlite3ExprListDelete(pParse->db, pOrderBy);
}
return pWin;
}
/*
** Window *pWin has just been created from a WINDOW clause. Tokne pBase
** is the base window. Earlier windows from the same WINDOW clause are
** stored in the linked list starting at pWin->pNextWin. This function
** either updates *pWin according to the base specification, or else
** leaves an error in pParse.
*/
void sqlite3WindowChain(Parse *pParse, Window *pWin, Window *pList){
if( pWin->zBase ){
sqlite3 *db = pParse->db;
Window *pExist = windowFind(pParse, pList, pWin->zBase);
if( pExist ){
const char *zErr = 0;
/* Check for errors */
if( pWin->pPartition ){
zErr = "PARTITION clause";
}else if( pExist->pOrderBy && pWin->pOrderBy ){
zErr = "ORDER BY clause";
}else if( pExist->bImplicitFrame==0 ){
zErr = "frame specification";
}
if( zErr ){
sqlite3ErrorMsg(pParse,
"cannot override %s of window: %s", zErr, pWin->zBase
);
}else{
pWin->pPartition = sqlite3ExprListDup(db, pExist->pPartition, 0);
if( pExist->pOrderBy ){
assert( pWin->pOrderBy==0 );
pWin->pOrderBy = sqlite3ExprListDup(db, pExist->pOrderBy, 0);
}
sqlite3DbFree(db, pWin->zBase);
pWin->zBase = 0;
}
}
}
}
/*
** Attach window object pWin to expression p.
*/

View File

@ -700,5 +700,106 @@ do_execsql_test 16.2 {
3 101
}
#-------------------------------------------------------------------------
# Test error cases from chaining window definitions.
#
reset_db
do_execsql_test 17.0 {
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c TEXT, d INTEGER);
INSERT INTO t1 VALUES(1, 'odd', 'one', 1);
INSERT INTO t1 VALUES(2, 'even', 'two', 2);
INSERT INTO t1 VALUES(3, 'odd', 'three', 3);
INSERT INTO t1 VALUES(4, 'even', 'four', 4);
INSERT INTO t1 VALUES(5, 'odd', 'five', 5);
INSERT INTO t1 VALUES(6, 'even', 'six', 6);
}
foreach {tn sql error} {
1 {
SELECT c, sum(d) OVER win2 FROM t1
WINDOW win1 AS (ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING),
win2 AS (win1 ORDER BY b)
} {cannot override frame specification of window: win1}
2 {
SELECT c, sum(d) OVER win2 FROM t1
WINDOW win1 AS (),
win2 AS (win4 ORDER BY b)
} {no such window: win4}
3 {
SELECT c, sum(d) OVER win2 FROM t1
WINDOW win1 AS (),
win2 AS (win1 PARTITION BY d)
} {cannot override PARTITION clause of window: win1}
4 {
SELECT c, sum(d) OVER win2 FROM t1
WINDOW win1 AS (ORDER BY b),
win2 AS (win1 ORDER BY d)
} {cannot override ORDER BY clause of window: win1}
} {
do_catchsql_test 17.1.$tn $sql [list 1 $error]
}
foreach {tn sql error} {
1 {
SELECT c, sum(d) OVER (win1 ORDER BY b) FROM t1
WINDOW win1 AS (ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)
} {cannot override frame specification of window: win1}
2 {
SELECT c, sum(d) OVER (win4 ORDER BY b) FROM t1
WINDOW win1 AS ()
} {no such window: win4}
3 {
SELECT c, sum(d) OVER (win1 PARTITION BY d) FROM t1
WINDOW win1 AS ()
} {cannot override PARTITION clause of window: win1}
4 {
SELECT c, sum(d) OVER (win1 ORDER BY d) FROM t1
WINDOW win1 AS (ORDER BY b)
} {cannot override ORDER BY clause of window: win1}
} {
do_catchsql_test 17.2.$tn $sql [list 1 $error]
}
do_execsql_test 17.3.1 {
SELECT group_concat(c, '.') OVER (PARTITION BY b ORDER BY c)
FROM t1
} {four four.six four.six.two five five.one five.one.three}
do_execsql_test 17.3.2 {
SELECT group_concat(c, '.') OVER (win1 ORDER BY c)
FROM t1
WINDOW win1 AS (PARTITION BY b)
} {four four.six four.six.two five five.one five.one.three}
do_execsql_test 17.3.3 {
SELECT group_concat(c, '.') OVER win2
FROM t1
WINDOW win1 AS (PARTITION BY b),
win2 AS (win1 ORDER BY c)
} {four four.six four.six.two five five.one five.one.three}
do_execsql_test 17.3.4 {
SELECT group_concat(c, '.') OVER (win2)
FROM t1
WINDOW win1 AS (PARTITION BY b),
win2 AS (win1 ORDER BY c)
} {four four.six four.six.two five five.one five.one.three}
do_execsql_test 17.3.5 {
SELECT group_concat(c, '.') OVER win5
FROM t1
WINDOW win1 AS (PARTITION BY b),
win2 AS (win1),
win3 AS (win2),
win4 AS (win3),
win5 AS (win4 ORDER BY c)
} {four four.six four.six.two five five.one five.one.three}
finish_test

View File

@ -21,7 +21,6 @@ execsql_test 1.0 {
DROP TABLE IF EXISTS t2;
CREATE TABLE t2(a INTEGER PRIMARY KEY, b INTEGER);
INSERT INTO t2(a, b) VALUES
(1,0), (2,74), (3,41), (4,74), (5,23), (6,99), (7,26), (8,33), (9,2),
(10,89), (11,81), (12,96), (13,59), (14,38), (15,68), (16,39), (17,62),
(18,91), (19,46), (20,6), (21,99), (22,97), (23,27), (24,46), (25,78),
(26,54), (27,97), (28,8), (29,67), (30,29), (31,93), (32,84), (33,77),
@ -307,6 +306,28 @@ foreach {tn window} {
SELECT string_agg(CAST(b AS TEXT), '.') OVER (PARTITION BY b%2,a ORDER BY b%10 $window) FROM t2
"
execsql_test 1.$tn.14.7 "
SELECT string_agg(CAST(b AS TEXT), '.') OVER (win1 ORDER BY b%10 $window)
FROM t2
WINDOW win1 AS (PARTITION BY b%2,a)
ORDER BY 1
"
execsql_test 1.$tn.14.8 "
SELECT string_agg(CAST(b AS TEXT), '.') OVER (win1 $window)
FROM t2
WINDOW win1 AS (PARTITION BY b%2,a ORDER BY b%10)
ORDER BY 1
"
execsql_test 1.$tn.14.9 "
SELECT string_agg(CAST(b AS TEXT), '.') OVER win2
FROM t2
WINDOW win1 AS (PARTITION BY b%2,a ORDER BY b%10),
win2 AS (win1 $window)
ORDER BY 1
"
execsql_test 1.$tn.15.1 "
SELECT count(*) OVER win, string_agg(CAST(b AS TEXT), '.')
FILTER (WHERE a%2=0) OVER win FROM t2

File diff suppressed because one or more lines are too long