Experimental implementation of FILTER clause for aggregate functions.

FossilOrigin-Name: 1f1ae2d6ac8dcbb62e5aa3dc17bc67d559cb565fc0d0a8c00a596075d35f8130
This commit is contained in:
dan 2019-07-02 11:56:47 +00:00
parent 00a6153faf
commit 6ba7ab0d25
12 changed files with 342 additions and 45 deletions

@ -1,5 +1,5 @@
C Use\sthe\sOP_Sequence\sopcode\sfor\sgenerating\sunique\srowid\svalues\sfor\san\nautoindex\son\sa\sco-routine\simplementation\sof\sa\ssubquery.
D 2019-06-28T07:08:13.998
C Experimental\simplementation\sof\sFILTER\sclause\sfor\saggregate\sfunctions.
D 2019-07-02T11:56:47.637
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -474,7 +474,7 @@ F src/date.c e1d8ac7102f3f283e63e13867acb0efa33861cf34f0faf4cdbaf9fa7a1eb7041
F src/dbpage.c 135eb3b5e74f9ef74bde5cec2571192c90c86984fa534c88bf4a055076fa19b7
F src/dbstat.c c12833de69cb655751487d2c5a59607e36be1c58ba1f4bd536609909ad47b319
F src/delete.c d08c9e01a2664afd12edcfa3a9c6578517e8ff8735f35509582693adbe0edeaf
F src/expr.c 2756d2875ac5b7a7efcc54edea3c8a31f4126684ded18da6fb406f4d3780a5f5
F src/expr.c 33267aaf04145af9f1f06c1f4c10f8e7a9acce1c681d75526e6a3fa7e58c1d79
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c 0e14d4bef8eac2d87bbd517e492d9084c65008d117823f8922c5e7b2b599bd33
F src/func.c 905c0424900979fade3731b4a271592059425c988cb7d5b245032253712dd7c8
@ -511,7 +511,7 @@ F src/os_win.c 85d9e532d0444ab6c16d7431490c2e279e282aa0917b0e988996b1ae0de5c5a0
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c 422fd8cfa59fb9173eff36a95878904a0eeb0dcc62ba49350acc8b1e51c4dc7b
F src/pager.h 217921e81eb5fe455caa5cda96061959706bcdd29ddb57166198645ef7822ac3
F src/parse.y 026e3e6fff32432166a4eae0bdb78ad09a53c84eb49acb44dc32e49b4c0b8a48
F src/parse.y 2902f393b08e33f4af42e2b63f943be62f5de096138461459de60084f598a056
F src/pcache.c fd2d0553b3222d6b9f7cb251079e5bca1299d1161da3027b525932d8bf46340a
F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
F src/pcache1.c be64b2f3908a7f97c56c963676eb12f0d6254c95b28cdc1d73a186eff213219d
@ -520,14 +520,14 @@ F src/pragma.h 4a9fabff14db4487a734dfeeb4be984ce662bfdccfae16145b9c732327735e13
F src/prepare.c 78027c6231fbb19ca186a5f5f0c0a1375d9c2cec0655273f9bd90d9ff74a34b3
F src/printf.c 9be6945837c839ba57837b4bc3af349eba630920fa5532aa518816defe42a7d4
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
F src/resolve.c 93b7bc7c45efa6322d92293361c51a873690daed50cf77eeff88a448246b0d5a
F src/resolve.c 65514625b68e79a4087d218107d51d489477509d55543e1be375d1e58f27c2f3
F src/rowset.c d977b011993aaea002cab3e0bb2ce50cf346000dff94e944d547b989f4b1fe93
F src/select.c 9187f2c65744e975b191ccee49946732ee922f8bf40da998b322aca1633405ea
F src/select.c 1774cc457eb0b6cd757a350fd9f5600c4d13f3e899d6615033aecaa4f00a5e7d
F src/shell.c.in 82f8a473c01662f52233c6c75b9bf88d0d2cab276086d5d4ca6f2ff57a3e48eb
F src/sqlite.h.in bc4aa09a59dcf3c34b8e57ce9ade8ff6aed8ef7a7ddc59951f59f9b009fc7cc0
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 9ecc93b8493bd20c0c07d52e2ac0ed8bab9b549c7f7955b59869597b650dd8b5
F src/sqliteInt.h bf65dfa6565fae2e0d501088fc9c5212244adc1429a543827a16b2f5eaf41982
F src/sqliteInt.h 13fa73fd67968a4e3c58c84fbd655770989a82e0902880b0446747cd6711ad74
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
@ -607,12 +607,12 @@ F src/vtab.c 1fa256c6ddad7a81e2a4dc080d015d4b0a7135767717d311298e47f6fca64bb3
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c b09a2a9cab50efa08451a8c81d47052120ad5da174048c6d0b08d405384abdf2
F src/wal.h 606292549f5a7be50b6227bd685fa76e3a4affad71bb8ac5ce4cb5c79f6a176a
F src/walker.c 7607f1a68130c028255d8d56094ea602fc402c79e1e35a46e6282849d90d5fe4
F src/walker.c 31844651eacdc408474c62df526f20d5ea7994b5b8ffcd23eed5dc8f688c26f5
F src/where.c 2f11eeb14335b7640f886b2fb441f54a94c35ab5cde8b53461a1074bfd587081
F src/whereInt.h 1b728f71654ebf8421a1715497a587f02d6f538e819af58dc826908f8577e810
F src/wherecode.c 37a1004237d630d785c47bba2290eac652a7a8b0047518eba3cb7c808b604c4a
F src/whereexpr.c 5e559bdd24b06e3bc2e68f258bf751302954dc1e432daf71fdd8098a71462326
F src/window.c 5be2cf7d8763cc97137fc44d015aed8a1a4a56fe9700d7933ed560172617c756
F src/window.c c595e250528b0f5c31d26d96e0077d137aa3be2aec3b33ab30fbf3e7f69110c2
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
@ -856,6 +856,9 @@ F test/extraquick.test cb254400bd42bfb777ff675356aabf3287978f79
F test/fallocate.test 37a62e396a68eeede8f8d2ecf23573a80faceb630788d314d0a073d862616717
F test/filectrl.test 6e871c2d35dead1d9a88e176e8d2ca094fec6bb3
F test/filefmt.test f393e80c4b8d493b7a7f8f3809a8425bbf4292af1f5140f01cb1427798a2bbd4
F test/filter1.test d706edd2c32c6b148143d885927e5cf4ea141106b803e243e5d1bfbdc7e2b5cf
F test/filter2.tcl 87e24fd0a2ab502bd4d0350c4eee17a50e2b4364a1b67230f82b8f2a5029f6bf
F test/filter2.test dfddced42a534e71a730aa3d4c6b148d9fbf29aa306f9cec6b51f31b8cf7e224
F test/fkey1.test d11dbb8a93ead9b5c46ae5d02da016d61245d47662fb2d844c99214f6163f768
F test/fkey2.test d35d1c81e7569bdd2b872e91750f7098117d2e8291369f70b7e3d50a0e523dc2
F test/fkey3.test 76d475c80b84ee7a5d062e56ccb6ea68882e2b49
@ -1830,7 +1833,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 5fd20e09a522b62a529cf4d76fbdf0a09426f67ffa30430cac6b81ebf32ba43e
R d8e38b1a6647f90f85dd30ba60740431
U drh
Z 069759ad3085e2b5f17cc34ec369471a
P eab4297577e4d325fed4757867fc77860de7448998d86f098c8a50272e17d35e
R 43e2b0402b61e905810368aafa77ca67
T *branch * filter-clause
T *sym-filter-clause *
T -sym-trunk *
U dan
Z 9e8394b31320bbcccfabf9cff0d64121

@ -1 +1 @@
eab4297577e4d325fed4757867fc77860de7448998d86f098c8a50272e17d35e
1f1ae2d6ac8dcbb62e5aa3dc17bc67d559cb565fc0d0a8c00a596075d35f8130

@ -1027,7 +1027,7 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
assert( !ExprHasProperty(p, EP_WinFunc) || p->y.pWin!=0 || db->mallocFailed );
assert( p->op!=TK_FUNCTION || ExprHasProperty(p, EP_TokenOnly|EP_Reduced)
|| p->y.pWin==0 || ExprHasProperty(p, EP_WinFunc) );
|| p->y.pWin==0 || ExprHasProperty(p, EP_WinFunc|EP_Filter) );
#ifdef SQLITE_DEBUG
if( ExprHasProperty(p, EP_Leaf) && !ExprHasProperty(p, EP_TokenOnly) ){
assert( p->pLeft==0 );
@ -1046,10 +1046,15 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
}else{
sqlite3ExprListDelete(db, p->x.pList);
}
#ifndef SQLITE_OMIT_WINDOWFUNC
if( ExprHasProperty(p, EP_WinFunc) ){
assert( p->op==TK_FUNCTION );
assert( p->op==TK_FUNCTION && !ExprHasProperty(p, EP_Filter) );
sqlite3WindowDelete(db, p->y.pWin);
}else if( ExprHasProperty(p, EP_Filter) ){
assert( p->op==TK_FUNCTION || p->op==TK_AGG_FUNCTION );
sqlite3ExprDelete(db, p->y.pFilter);
}
#endif
}
if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken);
if( !ExprHasProperty(p, EP_Static) ){
@ -1264,7 +1269,7 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){
}
/* Fill in pNew->pLeft and pNew->pRight. */
if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly|EP_WinFunc) ){
if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly|EP_WinFunc|EP_Filter) ){
zAlloc += dupedExprNodeSize(p, dupFlags);
if( !ExprHasProperty(pNew, EP_TokenOnly|EP_Leaf) ){
pNew->pLeft = p->pLeft ?
@ -1277,6 +1282,10 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){
pNew->y.pWin = sqlite3WindowDup(db, pNew, p->y.pWin);
assert( ExprHasProperty(pNew, EP_WinFunc) );
}
if( ExprHasProperty(p, EP_Filter) ){
pNew->y.pFilter = sqlite3ExprDup(db, p->y.pFilter, 0);
assert( ExprHasProperty(pNew, EP_Filter) );
}
#endif /* SQLITE_OMIT_WINDOWFUNC */
if( pzBuffer ){
*pzBuffer = zAlloc;
@ -1332,8 +1341,8 @@ static With *withDup(sqlite3 *db, With *p){
** objects found there, assembling them onto the linked list at Select->pWin.
*/
static int gatherSelectWindowsCallback(Walker *pWalker, Expr *pExpr){
if( pExpr->op==TK_FUNCTION && pExpr->y.pWin!=0 ){
assert( ExprHasProperty(pExpr, EP_WinFunc) );
if( pExpr->op==TK_FUNCTION && ExprHasProperty(pExpr, EP_WinFunc) ){
assert( pExpr->y.pWin );
pExpr->y.pWin->pNextWin = pWalker->u.pSelect->pWin;
pWalker->u.pSelect->pWin = pExpr->y.pWin;
}
@ -4862,6 +4871,14 @@ int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTab){
}else if( ALWAYS(pB->u.zToken!=0) && strcmp(pA->u.zToken,pB->u.zToken)!=0 ){
return 2;
}
#ifndef SQLITE_OMIT_WINDOWFUNC
else if( pA->op==TK_AGG_FUNCTION ){
assert( ExprHasProperty(pA, EP_WinFunc)==0 );
if( sqlite3ExprCompare(pParse, pA->y.pFilter, pB->y.pFilter, iTab) ){
return 2;
}
}
#endif
}
if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 2;
if( (combinedFlags & EP_TokenOnly)==0 ){

@ -1036,21 +1036,23 @@ expr(A) ::= CAST LP expr(E) AS typetoken(T) RP. {
%endif SQLITE_OMIT_CAST
%ifdef SQLITE_OMIT_WINDOWFUNC
expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP. {
A = sqlite3ExprFunction(pParse, Y, &X, D);
}
expr(A) ::= id(X) LP STAR RP. {
A = sqlite3ExprFunction(pParse, 0, &X, 0);
}
%endif
%ifndef SQLITE_OMIT_WINDOWFUNC
expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP over_clause(Z). {
expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP filter_opt(F) over_opt(Z). {
A = sqlite3ExprFunction(pParse, Y, &X, D);
sqlite3WindowAttach(pParse, A, Z);
sqlite3WindowAttach(pParse, A, F, Z);
}
expr(A) ::= id(X) LP STAR RP over_clause(Z). {
expr(A) ::= id(X) LP STAR RP filter_opt(F) over_opt(Z). {
A = sqlite3ExprFunction(pParse, 0, &X, 0);
sqlite3WindowAttach(pParse, A, Z);
sqlite3WindowAttach(pParse, A, F, Z);
}
%endif
@ -1724,20 +1726,17 @@ frame_exclude(A) ::= GROUP|TIES(X). {A = @X; /*A-overwrites-X*/}
%destructor window_clause {sqlite3WindowListDelete(pParse->db, $$);}
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 LP window(Z) RP. {
%type over_opt {Window*}
%destructor over_opt {sqlite3WindowDelete(pParse->db, $$);}
over_opt(A) ::= . { A=0; }
over_opt(A) ::= OVER LP window(Z) RP. {
A = Z;
assert( A!=0 );
A->pFilter = W;
}
over_clause(A) ::= filter_opt(W) OVER nm(Z). {
over_opt(A) ::= OVER nm(Z). {
A = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
if( A ){
A->zName = sqlite3DbStrNDup(pParse->db, Z.z, Z.n);
A->pFilter = W;
}else{
sqlite3ExprDelete(pParse->db, W);
}
}

@ -849,6 +849,12 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
sqlite3ErrorMsg(pParse, "misuse of %s function %.*s()",zType,nId,zId);
pNC->nErr++;
is_agg = 0;
}else if( is_agg==0 && ExprHasProperty(pExpr, EP_Filter) ){
sqlite3ErrorMsg(pParse,
"filter clause may not be used with non-aggregate %.*s()",
nId, zId
);
pNC->nErr++;
}
#else
if( (is_agg && (pNC->ncFlags & NC_AllowAgg)==0) ){
@ -883,7 +889,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
sqlite3WalkExprList(pWalker, pList);
if( is_agg ){
#ifndef SQLITE_OMIT_WINDOWFUNC
if( pExpr->y.pWin ){
if( ExprHasProperty(pExpr, EP_WinFunc) ){
Select *pSel = pNC->pWinSelect;
if( IN_RENAME_OBJECT==0 ){
sqlite3WindowUpdate(pParse, pSel->pWinDefn, pExpr->y.pWin, pDef);
@ -904,6 +910,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
NameContext *pNC2 = pNC;
pExpr->op = TK_AGG_FUNCTION;
pExpr->op2 = 0;
#ifndef SQLITE_OMIT_WINDOWFUNC
sqlite3WalkExpr(pWalker, pExpr->y.pFilter);
#endif
while( pNC2 && !sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){
pExpr->op2++;
pNC2 = pNC2->pNext;

@ -4406,7 +4406,9 @@ static u8 minMaxQuery(sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){
assert( *ppMinMax==0 );
assert( pFunc->op==TK_AGG_FUNCTION );
if( pEList==0 || pEList->nExpr!=1 ) return eRet;
if( pEList==0 || pEList->nExpr!=1 || ExprHasProperty(pFunc, EP_Filter) ){
return eRet;
}
zFunc = pFunc->u.zToken;
if( sqlite3StrICmp(zFunc, "min")==0 ){
eRet = WHERE_ORDERBY_MIN;
@ -4453,7 +4455,7 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
if( pExpr->op!=TK_AGG_FUNCTION ) return 0;
if( NEVER(pAggInfo->nFunc==0) ) return 0;
if( (pAggInfo->aFunc[0].pFunc->funcFlags&SQLITE_FUNC_COUNT)==0 ) return 0;
if( pExpr->flags&EP_Distinct ) return 0;
if( ExprHasProperty(pExpr, EP_Distinct|EP_Filter) ) return 0;
return pTab;
}
@ -5333,6 +5335,11 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){
int regAgg;
ExprList *pList = pF->pExpr->x.pList;
assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) );
if( ExprHasProperty(pF->pExpr, EP_Filter) ){
Expr *pFilter = pF->pExpr->y.pFilter;
addrNext = sqlite3VdbeMakeLabel(pParse);
sqlite3ExprIfFalse(pParse, pFilter, addrNext, SQLITE_JUMPIFNULL);
}
if( pList ){
nArg = pList->nExpr;
regAgg = sqlite3GetTempRange(pParse, nArg);
@ -5342,7 +5349,9 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){
regAgg = 0;
}
if( pF->iDistinct>=0 ){
addrNext = sqlite3VdbeMakeLabel(pParse);
if( addrNext==0 ){
addrNext = sqlite3VdbeMakeLabel(pParse);
}
testcase( nArg==0 ); /* Error condition */
testcase( nArg>1 ); /* Also an error */
codeDistinct(pParse, pF->iDistinct, addrNext, 1, regAgg);
@ -6225,9 +6234,14 @@ int sqlite3Select(
minMaxFlag = WHERE_ORDERBY_NORMAL;
}
for(i=0; i<sAggInfo.nFunc; i++){
assert( !ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_xIsSelect) );
Expr *pExpr = sAggInfo.aFunc[i].pExpr;
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
sNC.ncFlags |= NC_InAggFunc;
sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->x.pList);
sqlite3ExprAnalyzeAggList(&sNC, pExpr->x.pList);
#ifndef SQLITE_OMIT_WINDOWFUNC
assert( !ExprHasProperty(pExpr, EP_WinFunc) );
sqlite3ExprAnalyzeAggregates(&sNC, pExpr->y.pFilter);
#endif
sNC.ncFlags &= ~NC_InAggFunc;
}
sAggInfo.mxReg = pParse->nMem;

@ -2491,7 +2491,8 @@ struct Expr {
union {
Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL
** for a column of an index on an expression */
Window *pWin; /* TK_FUNCTION: Window definition for the func */
Window *pWin; /* EP_WinFunc: Window definition for the func */
Expr *pFilter; /* EP_Filter: Filter definition for the func */
struct { /* TK_IN, TK_SELECT, and TK_EXISTS */
int iAddr; /* Subroutine entry address */
int regReturn; /* Register used to hold return address */
@ -2536,6 +2537,7 @@ struct Expr {
#define EP_Static 0x8000000 /* Held in memory not obtained from malloc() */
#define EP_IsTrue 0x10000000 /* Always has boolean value of TRUE */
#define EP_IsFalse 0x20000000 /* Always has boolean value of FALSE */
#define EP_Filter 0x40000000 /* TK_[AGG_]FUNCTION with Expr.y.pFilter set */
/*
** The EP_Propagate mask is a set of properties that automatically propagate
@ -3602,7 +3604,7 @@ struct Window {
void sqlite3WindowDelete(sqlite3*, Window*);
void sqlite3WindowListDelete(sqlite3 *db, Window *p);
Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*, u8);
void sqlite3WindowAttach(Parse*, Expr*, Window*);
void sqlite3WindowAttach(Parse*, Expr*, Expr*, Window*);
int sqlite3WindowCompare(Parse*, Window*, Window*);
void sqlite3WindowCodeInit(Parse*, Window*);
void sqlite3WindowCodeStep(Parse*, Select*, WhereInfo*, int, int);
@ -3617,7 +3619,7 @@ Window *sqlite3WindowAssemble(Parse*, Window*, ExprList*, ExprList*, Token*);
#else
# define sqlite3WindowDelete(a,b)
# define sqlite3WindowFunctions()
# define sqlite3WindowAttach(a,b,c)
# define sqlite3WindowAttach(a,b,c,d)
#endif
/*

@ -74,6 +74,9 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){
if( ExprHasProperty(pExpr, EP_WinFunc) ){
if( walkWindowList(pWalker, pExpr->y.pWin) ) return WRC_Abort;
}
if( ExprHasProperty(pExpr, EP_Filter) ){
if( walkExpr(pWalker, pExpr->y.pFilter) ) return WRC_Abort;
}
#endif
}
break;

@ -1178,13 +1178,10 @@ void sqlite3WindowChain(Parse *pParse, Window *pWin, Window *pList){
/*
** Attach window object pWin to expression p.
*/
void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){
void sqlite3WindowAttach(Parse *pParse, Expr *p, Expr *pFilter, Window *pWin){
if( p ){
assert( p->op==TK_FUNCTION );
/* This routine is only called for the parser. If pWin was not
** allocated due to an OOM, then the parser would fail before ever
** invoking this routine */
if( ALWAYS(pWin) ){
if( pWin ){
p->y.pWin = pWin;
ExprSetProperty(p, EP_WinFunc);
pWin->pOwner = p;
@ -1192,9 +1189,14 @@ void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){
sqlite3ErrorMsg(pParse,
"DISTINCT is not supported for window functions");
}
pWin->pFilter = pFilter;
}else if( pFilter ){
p->y.pFilter = pFilter;
ExprSetProperty(p, EP_Filter);
}
}else{
sqlite3WindowDelete(pParse->db, pWin);
sqlite3ExprDelete(pParse->db, pFilter);
}
}

90
test/filter1.test Normal file

@ -0,0 +1,90 @@
# 2018 May 8
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix filter1
ifcapable !windowfunc {
finish_test
return
}
do_execsql_test 1.0 {
CREATE TABLE t1(a);
CREATE INDEX i1 ON t1(a);
INSERT INTO t1 VALUES(1), (2), (3), (4), (5), (6), (7), (8), (9);
}
do_execsql_test 1.1 { SELECT sum(a) FROM t1; } 45
do_execsql_test 1.2 { SELECT sum(a) FILTER( WHERE a<5 ) FROM t1; } 10
do_execsql_test 1.3 {
SELECT sum(a) FILTER( WHERE a>9 ),
sum(a) FILTER( WHERE a>8 ),
sum(a) FILTER( WHERE a>7 ),
sum(a) FILTER( WHERE a>6 ),
sum(a) FILTER( WHERE a>5 ),
sum(a) FILTER( WHERE a>4 ),
sum(a) FILTER( WHERE a>3 ),
sum(a) FILTER( WHERE a>2 ),
sum(a) FILTER( WHERE a>1 ),
sum(a) FILTER( WHERE a>0 )
FROM t1;
} {{} 9 17 24 30 35 39 42 44 45}
do_execsql_test 1.4 {
SELECT max(a) FILTER (WHERE (a % 2)==0) FROM t1
} {8}
do_execsql_test 1.5 {
SELECT min(a) FILTER (WHERE a>4) FROM t1
} {5}
do_execsql_test 1.6 {
SELECT count(*) FILTER (WHERE a!=5) FROM t1
} {8}
do_execsql_test 1.6 {
SELECT min(a) FILTER (WHERE a>3) FROM t1 GROUP BY (a%2) ORDER BY 1;
} {4 5}
#-------------------------------------------------------------------------
# Test some errors:
#
# .1 FILTER on a non-aggregate function,
# .2 Window function in FILTER clause,
# .3 Aggregate function in FILTER clause,
#
reset_db
do_execsql_test 2.0 {
CREATE TABLE t1(a);
INSERT INTO t1 VALUES(1), (2), (3), (4), (5), (6), (7), (8), (9);
}
do_catchsql_test 2.1 {
SELECT upper(a) FILTER (WHERE a=1) FROM t1
} {1 {filter clause may not be used with non-aggregate upper()}}
do_catchsql_test 2.2 {
SELECT sum(a) FILTER (WHERE 1 - max(a) OVER () > 0) FROM t1
} {1 {misuse of window function max()}}
do_catchsql_test 2.3 {
SELECT sum(a) FILTER (WHERE 1 - count(a)) FROM t1
} {1 {misuse of aggregate: count()}}
finish_test

73
test/filter2.tcl Normal file

@ -0,0 +1,73 @@
# 2018 May 19
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
source [file join [file dirname $argv0] pg_common.tcl]
#=========================================================================
start_test filter2 "2019 July 2"
ifcapable !windowfunc
execsql_test 1.0 {
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b INTEGER);
INSERT INTO t1 VALUES
(1, 7), (2, 3), (3, 5), (4, 30), (5, 26), (6, 23), (7, 27),
(8, 3), (9, 17), (10, 26), (11, 33), (12, 25), (13, NULL), (14, 47),
(15, 36), (16, 13), (17, 45), (18, 31), (19, 11), (20, 36), (21, 37),
(22, 21), (23, 22), (24, 14), (25, 16), (26, 3), (27, 7), (28, 29),
(29, 50), (30, 38), (31, 3), (32, 36), (33, 12), (34, 4), (35, 46),
(36, 3), (37, 48), (38, 23), (39, NULL), (40, 24), (41, 5), (42, 46),
(43, 11), (44, NULL), (45, 18), (46, 25), (47, 15), (48, 18), (49, 23);
}
execsql_test 1.1 { SELECT sum(b) FROM t1 }
execsql_test 1.2 { SELECT sum(b) FILTER (WHERE a<10) FROM t1 }
execsql_test 1.3 { SELECT count(DISTINCT b) FROM t1 }
execsql_test 1.4 { SELECT count(DISTINCT b) FILTER (WHERE a!=19) FROM t1 }
execsql_test 1.5 {
SELECT min(b) FILTER (WHERE a>19),
min(b) FILTER (WHERE a>0),
max(a+b) FILTER (WHERE a>19),
max(b+a) FILTER (WHERE a BETWEEN 10 AND 40)
FROM t1;
}
execsql_test 1.6 {
SELECT min(b),
min(b),
max(a+b),
max(b+a)
FROM t1
GROUP BY (a%10)
ORDER BY 1, 2, 3, 4;
}
execsql_test 1.7 {
SELECT min(b) FILTER (WHERE a>19),
min(b) FILTER (WHERE a>0),
max(a+b) FILTER (WHERE a>19),
max(b+a) FILTER (WHERE a BETWEEN 10 AND 40)
FROM t1
GROUP BY (a%10)
ORDER BY 1, 2, 3, 4;
}
finish_test

82
test/filter2.test Normal file

@ -0,0 +1,82 @@
# 2019 July 2
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
####################################################
# DO NOT EDIT! THIS FILE IS AUTOMATICALLY GENERATED!
####################################################
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix filter2
ifcapable !windowfunc { finish_test ; return }
do_execsql_test 1.0 {
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b INTEGER);
INSERT INTO t1 VALUES
(1, 7), (2, 3), (3, 5), (4, 30), (5, 26), (6, 23), (7, 27),
(8, 3), (9, 17), (10, 26), (11, 33), (12, 25), (13, NULL), (14, 47),
(15, 36), (16, 13), (17, 45), (18, 31), (19, 11), (20, 36), (21, 37),
(22, 21), (23, 22), (24, 14), (25, 16), (26, 3), (27, 7), (28, 29),
(29, 50), (30, 38), (31, 3), (32, 36), (33, 12), (34, 4), (35, 46),
(36, 3), (37, 48), (38, 23), (39, NULL), (40, 24), (41, 5), (42, 46),
(43, 11), (44, NULL), (45, 18), (46, 25), (47, 15), (48, 18), (49, 23);
} {}
do_execsql_test 1.1 {
SELECT sum(b) FROM t1
} {1041}
do_execsql_test 1.2 {
SELECT sum(b) FILTER (WHERE a<10) FROM t1
} {141}
do_execsql_test 1.3 {
SELECT count(DISTINCT b) FROM t1
} {31}
do_execsql_test 1.4 {
SELECT count(DISTINCT b) FILTER (WHERE a!=19) FROM t1
} {31}
do_execsql_test 1.5 {
SELECT min(b) FILTER (WHERE a>19),
min(b) FILTER (WHERE a>0),
max(a+b) FILTER (WHERE a>19),
max(b+a) FILTER (WHERE a BETWEEN 10 AND 40)
FROM t1;
} {3 3 88 85}
do_execsql_test 1.6 {
SELECT min(b),
min(b),
max(a+b),
max(b+a)
FROM t1
GROUP BY (a%10)
ORDER BY 1, 2, 3, 4;
} {3 3 58 58 3 3 66 66 3 3 71 71 3 3 88 88 4 4 61 61 5 5 54 54
7 7 85 85 11 11 79 79 16 16 81 81 24 24 68 68}
do_execsql_test 1.7 {
SELECT min(b) FILTER (WHERE a>19),
min(b) FILTER (WHERE a>0),
max(a+b) FILTER (WHERE a>19),
max(b+a) FILTER (WHERE a BETWEEN 10 AND 40)
FROM t1
GROUP BY (a%10)
ORDER BY 1, 2, 3, 4;
} {3 3 58 58 3 3 71 39 4 4 38 61 7 7 85 85 11 5 54 45 16 16 81 81
18 3 66 61 21 3 88 68 23 11 79 79 24 24 68 68}
finish_test