]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Experimental work on implementing an EXCLUDE clause following "*"
authordrh <>
Fri, 3 Jul 2026 00:56:05 +0000 (00:56 +0000)
committerdrh <>
Fri, 3 Jul 2026 00:56:05 +0000 (00:56 +0000)
wildcards in a SELECT statement.  Parsing only at this point.

FossilOrigin-Name: f38fc2c7d72a5f9eb99314ec3e6e399faf035066e27991fa4f73b07bb807b594

manifest
manifest.tags
manifest.uuid
src/parse.y
src/sqliteInt.h
src/treeview.c

index 830dbb12383caf50f2ac00ee43ed5b176d18cd07..7aef81fa28aa7dda47f19eeb22399f025e9fde95 100644 (file)
--- 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.
index bec971799ff1b8ee641c166c7aeb22d12c785393..cb7318721601fb4e5a2929d846b259034d4214a7 100644 (file)
@@ -1,2 +1,2 @@
-branch trunk
-tag trunk
+branch exclude-clause
+tag exclude-clause
index a089abfa640e6b6a4a1a51e60888aca8740a0e25..a5975479e844c6d64a9a6dae071d0f12dc568b2e 100644 (file)
@@ -1 +1 @@
-0d70a15c3b35e9af3052042cd5332db1b38a66416f1c3c8a50d342395bf22c01
+f38fc2c7d72a5f9eb99314ec3e6e399faf035066e27991fa4f73b07bb807b594
index 93ebf5c9ac4b3098107dcaf749d77451a54bae4b..a3d7a1f5b092e3b02da941d60800c295bad7d66e 100644 (file)
@@ -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; i<pSrc->nExpr; 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; i<pSrc->nId; 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 <id>" phrase that can follow one of the expressions that
 // define the result set, or one of the tables in the FROM clause.
 //
index 0c48f8707eb996ae4e6cc363ae2f07e7a3631022..b4fa23acc38cfae4f0c5c55c63f875f8608eabd4 100644 (file)
@@ -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,
index b482e1581aa0e99b23466120a1d696cde4608499..81c7254f31cc4fefbc781dc5199eef06872657f7 100644 (file)
@@ -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 ){