From: drh <> Date: Fri, 3 Jul 2026 00:56:05 +0000 (+0000) Subject: Experimental work on implementing an EXCLUDE clause following "*" X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=18263295dd144ae72d6429d8dd1d663db9d21eb9;p=thirdparty%2Fsqlite.git Experimental work on implementing an EXCLUDE clause following "*" wildcards in a SELECT statement. Parsing only at this point. FossilOrigin-Name: f38fc2c7d72a5f9eb99314ec3e6e399faf035066e27991fa4f73b07bb807b594 --- diff --git a/manifest b/manifest index 830dbb1238..7aef81fa28 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C In\sext/wasm,\sadd\sthe\sability\sto\somit\sspecific\sJS\sAPIs\sand\sVFSes\sat\sbuild-time. -D 2026-07-02T09:52:03.709 +C Experimental\swork\son\simplementing\san\sEXCLUDE\sclause\sfollowing\s"*"\nwildcards\sin\sa\sSELECT\sstatement.\s\sParsing\sonly\sat\sthis\spoint. +D 2026-07-03T00:56:05.259 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -729,7 +729,7 @@ F src/os_win.c 1227a3e962b017d676d985a8aec0d64f273991931ea0a0b99773651102f62df4 F src/os_win.h c06ccc3a090cf54202ea58981c298817f3309d4c9e4d52ad0a02927346493721 F src/pager.c 678ee3b68638ee427cc1b071b11181d0f2a827f6cb4d6ec7c12d5fa5f6a752d0 F src/pager.h 6137149346e6c8a3ddc1eeb40aee46381e9bc8b0fcc6dda8a1efde993c2275b8 -F src/parse.y d5a3c5b0277a441c38b35071c05e2b61ff5fc918a63309c809f4b6706179c320 +F src/parse.y dc11c53d183d26f1931a30f63b9b3a9a2311c50ec1ff6b7bed210a53767c7e1e F src/pcache.c 588cc3c5ccaaadde689ed35ce5c5c891a1f7b1f4d1f56f6cf0143b74d8ee6484 F src/pcache.h 092b758d2c5e4dabb30eae46d8dfad77c0f70b16bf3ff1943f7a232b0fe0d4ba F src/pcache1.c d7ee0f95992501a65379f620b3de1430b64e52e397769938668a9fd9dd1c8145 @@ -744,7 +744,7 @@ F src/shell.c.in a4e83895cfa336065ad7f7a7dea8fc2a19d050f7ce7466621c67208acaac9e4 F src/sqlite.h.in d9ec41feb4cd804e68b174328b43beb3f1f71bba13e1c7a439efb826d301cccc F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479 F src/sqlite3ext.h 0efd4723bad9124ea1f581d9f1ea0254ac1c6f3e5fb29e4f3dcf36c72485a456 -F src/sqliteInt.h 92563b8edc673513b82d87bf6a8d7aeb554b2c7b7d5e30992fbca04010db79ac +F src/sqliteInt.h e0324e481eaa7adaf5fc2d79caae838b1d88d3466c50453345d4628b003f3db6 F src/sqliteLimit.h c70656b67ab5b96741a8f1c812bdd80c81f2b1c1e443d0cc3ea8c33bb1f1a092 F src/status.c 7565d63a79aa2f326339a24a0461a60096d0bd2bce711fefb50b5c89335f3592 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -799,7 +799,7 @@ F src/test_window.c 6d80e11fba89a1796525e6f0048ff0c7789aa2c6b0b11c80827dc1437bd8 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/threads.c 85d8b9f05f78211c61e3739ab5db761d7118766d1916ae7f2764735106bc4e13 F src/tokenize.c e9d52d9f7374d82dadcd11726bea8a597d43709d6672704f3f0375bf1d726912 -F src/treeview.c feaa59f14db4f7b5aacca9c5ad5aeb562c1f98262c1ffd74371f4186ade91fc5 +F src/treeview.c d4949e9bb73b5ee6d850213225a30d4f8f2988582d4f7166080644aadcc3e94c F src/trigger.c 5028874dd1f4fd3085bacd55c30114b5ad651525508656f65af09adf227181d5 F src/update.c 3e5e7ff66fa19ebe4d1b113d480639a24cc1175adbefabbd1a948a07f28e37cf F src/upsert.c dd9f0fcccbfb4f20e1026a21a7254ba3f2c08e9cfa92affaff5b5ec3b00ea549 @@ -2210,9 +2210,11 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c -P 88901fffcd445ccb97e4e379ea15d8b06d87b8170cad0f717c909944ff21851f 225dd27e147b46e85097ada7234a307f55842d98155cd36fe00da57744598bc3 -R d20cfce4969b948014d3a3d610a966ce -T +closed 225dd27e147b46e85097ada7234a307f55842d98155cd36fe00da57744598bc3 Closed\sby\sintegrate-merge. -U stephan -Z 4776f4f7741018f2b4629b3adc43c7dd +P 0d70a15c3b35e9af3052042cd5332db1b38a66416f1c3c8a50d342395bf22c01 +R fbfb38b4a2d1f59e008a86ce344506fc +T *branch * exclude-clause +T *sym-exclude-clause * +T -sym-trunk * +U drh +Z 085c7684bdb90daee11e1c534e7f72a6 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.tags b/manifest.tags index bec971799f..cb73187216 100644 --- a/manifest.tags +++ b/manifest.tags @@ -1,2 +1,2 @@ -branch trunk -tag trunk +branch exclude-clause +tag exclude-clause diff --git a/manifest.uuid b/manifest.uuid index a089abfa64..a5975479e8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0d70a15c3b35e9af3052042cd5332db1b38a66416f1c3c8a50d342395bf22c01 +f38fc2c7d72a5f9eb99314ec3e6e399faf035066e27991fa4f73b07bb807b594 diff --git a/src/parse.y b/src/parse.y index 93ebf5c9ac..a3d7a1f5b0 100644 --- a/src/parse.y +++ b/src/parse.y @@ -714,20 +714,149 @@ selcollist(A) ::= sclp(A) scanpt(B) expr(X) scanpt(Z) as(Y). { if( Y.n>0 ) sqlite3ExprListSetName(pParse, A, &Y, 1); sqlite3ExprListSetSpan(pParse,A,B,Z); } -selcollist(A) ::= sclp(A) scanpt STAR(X). { - Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0); +%include { + /* Construct a TK_ASTERISK Expr for the "*" wildcard in a result set + ** specification, with its optional EXCLUDE clause + */ + static Expr *parserWildcardExpr(sqlite3 *db, ExprList *pExclude){ + Expr *p = sqlite3Expr(db, TK_ASTERISK, 0); + if( pExclude ){ + if( p ){ + p->x.pList = pExclude; + }else{ + sqlite3ExprListDelete(db, pExclude); + } + } + return p; + } +} +selcollist(A) ::= sclp(A) scanpt STAR(X) colexcl(E). { + Expr *p = parserWildcardExpr(pParse->db, E); sqlite3ExprSetErrorOffset(p, (int)(X.z - pParse->zTail)); A = sqlite3ExprListAppend(pParse, A, p); } -selcollist(A) ::= sclp(A) scanpt nm(X) DOT STAR(Y). { +selcollist(A) ::= sclp(A) scanpt nm(X) DOT STAR(Y) colexcl(E). { Expr *pRight, *pLeft, *pDot; - pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0); + pRight = parserWildcardExpr(pParse->db, E); sqlite3ExprSetErrorOffset(pRight, (int)(Y.z - pParse->zTail)); pLeft = tokenExpr(pParse, TK_ID, X); pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); A = sqlite3ExprListAppend(pParse,A, pDot); } +// The colexcl nonterminal represents the EXCLUDE clause that can +// optionally follow the "*" wildcard in the result set of a SELECT. +// Example: +// +// SELECT * (EXCLUDE (a, b) +// REPLACE (c WITH x+y, d WITH z*2) +// RENAME (e AS ee, f AS foo)) +// FROM t ... +// +// The value is an ExprList with one entry for each exluded, replaced, or +// renamed column, with ExprList_tiem fields set as follows: +// +// ExprList_item.zEName: Name of the column +// ExprList_item.fg.eEName: One of ENAME_EXCLUDE, ENAME_REPLACE, +// or ENAME_RENAME +// ExprList_item.pExpr: If ENAME_EXCLUDE: NULL +// If ENAME_REPLACE: Substitute expr +// If ENAME_RENAME: TK_RENAME expr with new +// name u.zToken +// +%type colexcl {ExprList*} +%destructor colexcl {sqlite3ExprListDelete(pParse->db, $$);} +%type cexcllist {ExprList*} +%destructor cexcllist {sqlite3ExprListDelete(pParse->db, $$);} +%type cexclitem {ExprList*} +%destructor cexclitem {sqlite3ExprListDelete(pParse->db, $$);} +%type cexclreplace {ExprList*} +%destructor cexclreplace {sqlite3ExprListDelete(pParse->db, $$);} +%type cexclrename {ExprList*} +%destructor cexclrename {sqlite3ExprListDelete(pParse->db, $$);} + +colexcl(A) ::= . {A = 0;} +colexcl(A) ::= cexclitem(A). +colexcl(A) ::= LP cexcllist(X) RP. {A = X;} +cexcllist(A) ::= cexclitem(A). +cexcllist(A) ::= cexcllist(A) cexclitem(X). + {A = parserExcludeConcat(pParse,A,X);} +cexclitem(A) ::= EXCLUDE LP idlist(X) RP. + {A = parserExcludeIdList(pParse,X);} +cexclitem(A) ::= REPLACE LP cexclreplace(X) RP. {A = X;} +cexclitem(A) ::= RENAME LP cexclrename(X) RP. {A = X;} +cexclreplace(A) ::= nm(X) WITH expr(Y). + {A = parserExcludeExpr(pParse,0,&X,Y,ENAME_REPLACE);} +cexclreplace(A) ::= cexclreplace(A) COMMA nm(X) WITH expr(Y). + {A = parserExcludeExpr(pParse,A,&X,Y,ENAME_REPLACE);} +cexclrename(A) ::= nm(X) AS nm(Y). { + Expr *p = tokenExpr(pParse, TK_RENAME, Y); + A = parserExcludeExpr(pParse,0,&X,p,ENAME_RENAME); +} +cexclrename(A) ::= cexclrename(A) COMMA nm(X) AS nm(Y). { + Expr *p = tokenExpr(pParse, TK_RENAME, Y); + A = parserExcludeExpr(pParse,A,&X,p,ENAME_RENAME); +} +%include { + /* Append the pSrc ExprList onto the end of pDest. + ** Free pSrc when done. */ + static ExprList *parserExcludeConcat( + Parse *pParse, /* Parsing context */ + ExprList *pDest, /* Append to this ExprList */ + ExprList *pSrc /* Take terms off of this ExprList */ + ){ + int i; + if( pSrc==0 ) return pDest; + for(i=0; inExpr; i++){ + pDest = sqlite3ExprListAppend(pParse, pDest, 0); + if( pDest ){ + int j = pDest->nExpr - 1; + pDest->a[j] = pSrc->a[i]; + pSrc->a[i].pExpr = 0; + pSrc->a[i].zEName = 0; + } + } + sqlite3ExprListDelete(pParse->db, pSrc); + return pDest; + } + /* Convert an IdList in pSrc into an EXCLUDE ExprList and return + ** the ExprList. Free pSrc when done */ + static ExprList *parserExcludeIdList( + Parse *pParse, /* Parsing context */ + IdList *pSrc /* Identifiers come from this list */ + ){ + int i; + ExprList *pDest = 0; + if( pSrc==0 ) return 0; + for(i=0; inId; i++){ + pDest = sqlite3ExprListAppend(pParse, pDest, 0); + if( pDest ){ + int j = pDest->nExpr - 1; + pDest->a[j].zEName = pSrc->a[i].zName; + pDest->a[j].fg.eEName = ENAME_EXCLUDE; + pSrc->a[i].zName = 0; + } + } + sqlite3IdListDelete(pParse->db, pSrc); + return pDest; + } + /* Add a single new REPLACE or RENAME term into the EXCLUDE ExprList + ** in pDest. */ + static ExprList *parserExcludeExpr( + Parse *pParse, /* Parsing context */ + ExprList *pDest, /* Append one term to this ExprList */ + Token *pName, /* Name of the column to be replaced */ + Expr *pExpr, /* Replacement expression */ + int eEName /* ENAME_REPLACE or ENAME_RENAME */ + ){ + pDest = sqlite3ExprListAppend(pParse, pDest, pExpr); + sqlite3ExprListSetName(pParse, pDest, pName, 1); + if( pDest ){ + pDest->a[pDest->nExpr-1].fg.eEName = eEName; + } + return pDest; + } +} // An option "AS " phrase that can follow one of the expressions that // define the result set, or one of the tables in the FROM clause. // diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 0c48f8707e..b4fa23acc3 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3235,6 +3235,10 @@ struct Expr { ** ** ENAME_SPAN Text of the original result set ** expression. +** +** ENAME_EXCLUDE Column name that is part of an +** ENAME_REPLACE (EXCLUDE(..)) expression following +** ENAME_RENAME the "*" wildcard in a SELECT result set */ struct ExprList { int nExpr; /* Number of expressions on the list */ @@ -3244,7 +3248,7 @@ struct ExprList { char *zEName; /* Token associated with this expression */ struct { u8 sortFlags; /* Mask of KEYINFO_ORDER_* flags */ - unsigned eEName :2; /* Meaning of zEName */ + unsigned eEName :3; /* Meaning of zEName */ unsigned done :1; /* Indicates when processing is finished */ unsigned reusable :1; /* Constant expression is reusable */ unsigned bSorterRef :1; /* Defer evaluation until after sorting */ @@ -3273,10 +3277,13 @@ struct ExprList { /* ** Allowed values for Expr.a.eEName */ -#define ENAME_NAME 0 /* The AS clause of a result set */ -#define ENAME_SPAN 1 /* Complete text of the result set expression */ -#define ENAME_TAB 2 /* "DB.TABLE.NAME" for the result set */ -#define ENAME_ROWID 3 /* "DB.TABLE._rowid_" for * expansion of rowid */ +#define ENAME_NAME 0 /* The AS clause of a result set */ +#define ENAME_SPAN 1 /* Complete text of the result set expression */ +#define ENAME_TAB 2 /* "DB.TABLE.NAME" for the result set */ +#define ENAME_ROWID 3 /* "DB.TABLE._rowid_" for * expansion of rowid */ +#define ENAME_EXCLUDE 4 /* *EXCLUDE column name */ +#define ENAME_REPLACE 5 /* *REPLACE column name */ +#define ENAME_RENAME 6 /* *RENAME column name */ /* ** An instance of this structure can hold a simple list of identifiers, diff --git a/src/treeview.c b/src/treeview.c index b482e1581a..81c7254f31 100644 --- a/src/treeview.c +++ b/src/treeview.c @@ -611,6 +611,11 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){ sqlite3TreeViewLine(pView,"ID \"%w\"", pExpr->u.zToken); break; } + case TK_RENAME: { + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + sqlite3TreeViewLine(pView,"RENAME \"%w\"", pExpr->u.zToken); + break; + } #ifndef SQLITE_OMIT_CAST case TK_CAST: { /* Expressions of the form: CAST(pLeft AS token) */ @@ -877,6 +882,14 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){ } break; } + case TK_ASTERISK: { + sqlite3TreeViewLine(pView, "ASTERISK"); + if( pExpr->x.pList ){ + sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, "EXCLUDE"); + } + break; + } + default: { sqlite3TreeViewLine(pView, "op=%d", pExpr->op); break; @@ -931,6 +944,15 @@ void sqlite3TreeViewBareExprList( case ENAME_SPAN: fprintf(stdout, "SPAN(\"%s\") ", zName); break; + case ENAME_EXCLUDE: + fprintf(stdout, "EXCLUDE(\"%s\") ", zName); + break; + case ENAME_REPLACE: + fprintf(stdout, "REPLACE(\"%s\") ", zName); + break; + case ENAME_RENAME: + fprintf(stdout, "RENAME(\"%s\") ", zName); + break; } } if( j ){