From f163bfba4edf4c574705a0e3c0544e866582b973 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 20 Mar 2026 19:30:58 +0000 Subject: [PATCH] If the argument to REINDEX is EXPRESSIONS (with no schema prefix) then it updates both expression indexes, and any indexes named "expressions" or all indexes of any tables name "expressions". FossilOrigin-Name: 72650dc152279a6da1937f377b1e16b79f4cd664841e12222dc5459838408d22 --- manifest | 17 ++--- manifest.uuid | 2 +- src/build.c | 189 +++++++++++++++++++------------------------------ test/auth.test | 8 +-- 4 files changed, 85 insertions(+), 131 deletions(-) diff --git a/manifest b/manifest index 73930bf865..d427d530e6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C If\sthere\sare\snot\scollating\ssequences,\stables,\sor\sindexes\snamed\s"EXPRESSIONS"\nthen\sthe\s"REINDEX\sEXPRESSIONS"\scommand\srebuilds\sall\sexpression\sindexes. -D 2026-03-20T14:37:39.310 +C If\sthe\sargument\sto\sREINDEX\sis\sEXPRESSIONS\s(with\sno\sschema\sprefix)\sthen\sit\nupdates\sboth\sexpression\sindexes,\sand\sany\sindexes\snamed\s"expressions"\sor\nall\sindexes\sof\sany\stables\sname\s"expressions". +D 2026-03-20T19:30:58.886 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -676,7 +676,7 @@ F src/btmutex.c 30dada73a819a1ef5b7583786370dce1842e12e1ad941e4d05ac29695528daea F src/btree.c fb350c445316c1cc0529703c0b76450770a1de0ab0440641a56b19f05d6fefbe F src/btree.h e823c46d87f63d904d735a24b76146d19f51f04445ea561f71cc3382fd1307f0 F src/btreeInt.h 9c0f9ea5c9b5f4dcaea18111d43efe95f2ac276cd86d770dce10fd99ccc93886 -F src/build.c beb8069bccb2ddd101b384df95f45b300999e70ab1be4061dd8da7bd27fb3579 +F src/build.c c180fbb25a3c3e02c7eacdf1d5911a0f29bbd4b4c5e1b64fe5d8772096419a69 F src/callback.c 3605bbf02bd7ed46c79cd48346db4a32fc51d67624400539c0532f4eead804ad F src/carray.c 3efe3982d5fb323334c29328a4e189ccaef6b95612a6084ad5fa124fd5db1179 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e @@ -876,7 +876,7 @@ F test/attach2.test 6d1e3a457ce260d6fc8e5945c07fba6c76dc2aa90e1c701f067b50ee88f7 F test/attach3.test c59d92791070c59272e00183b7353eeb94915976 F test/attach4.test 00e754484859998d124d144de6d114d920f2ed6ca2f961e6a7f4183c714f885e F test/attachmalloc.test 67309af95c6b765c13e7d2279d7fccbef78e6eb0565d75d51cefd5dc88784549 -F test/auth.test 5b8558a40571ebc55c1581cb7cec3b2348a699542a0a51b83ef21c6a953d95e3 +F test/auth.test 2a01bf5bf3a0f10adf8ae3a3fd2c05af8a8c1b7a52fae227adb4ccd931915b5c F test/auth2.test 9eb7fce9f34bf1f50d3f366fb3e606be5a2000a1 F test/auth3.test 76d20a7fa136d63bcfcf8bcb65c0b1455ed71078d81f22bcd0550d3eb18594ab F test/autoanalyze1.test b9cc3f32a990fa56669b668d237c6d53e983554ae80c0604992e18869a0b2dec @@ -2195,11 +2195,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c -P 078b3162d0d3d3035f4d3ad88664066d99c218a731ac481b9f1172529e26e4eb -R fffd90b5bfe8609c91e78fb9093e3446 -T *branch * reindex-expressions -T *sym-reindex-expressions * -T -sym-trunk * +P df5c5aa26758e0dc00a9ccba29eac83071176d257e121207e0a13c54833b18c0 +R fa2d2188fd0c121dfe90e3d57a7a9881 U drh -Z 9182359fa4b768364b8403c0c94e4fa6 +Z 99001bf01c3f8b11e63f25852a2515f5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 19509a7e47..58dad7f5ef 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -df5c5aa26758e0dc00a9ccba29eac83071176d257e121207e0a13c54833b18c0 +72650dc152279a6da1937f377b1e16b79f4cd664841e12222dc5459838408d22 diff --git a/src/build.c b/src/build.c index 94c3f52624..f7e2806175 100644 --- a/src/build.c +++ b/src/build.c @@ -5523,119 +5523,55 @@ void sqlite3RowidConstraint( } /* -** KEYWORD "expressions". Identify the magic "expressions" collating -** sequence name in collationMatch() by pointer comparison to this -** value. -*/ -static const char zExpressionsKW[] = "expressions"; - -/* -** Check to see if pIndex uses the collating sequence pColl. Return -** true if it does and false if it does not. -** -** If zColl==zExpressionsKW, then match only if index is an expression -** index. +** Return true if any column of pIndex uses the zColl collation */ #ifndef SQLITE_OMIT_REINDEX static int collationMatch(const char *zColl, Index *pIndex){ int i; assert( zColl!=0 ); for(i=0; inColumn; i++){ - const char *z; - i16 iCol = pIndex->aiColumn[i]; - if( iCol==XN_ROWID ) continue; - if( iCol==XN_EXPR ){ - if( zColl==zExpressionsKW ){ - return 1; /* Index on an expression */ - } - continue; - } - z = pIndex->azColl[i]; - assert( z!=0 ); - if( 0==sqlite3StrICmp(z, zColl) ){ - assert( zColl!=zExpressionsKW ); - return 1; /* Index using collating sequence zColl */ - } - if( zColl==zExpressionsKW - && (pIndex->pTable->aCol[iCol].colFlags & COLFLAG_VIRTUAL)!=0 - ){ - return 1; /* Index on a VIRTUAL generated column */ - } + const char *z = pIndex->azColl[i]; + assert( z!=0 || pIndex->aiColumn[i]<0 ); + if( z!=0 && 0==sqlite3StrICmp(z, zColl) ) return 1; } return 0; } #endif -/* -** Recompute all indexes of pTab that use the collating sequence pColl. -** If pColl==0 then recompute all indexes of pTab. -*/ -#ifndef SQLITE_OMIT_REINDEX -static void reindexTable(Parse *pParse, Table *pTab, char const *zColl){ - if( !IsVirtual(pTab) ){ - Index *pIndex; /* An index associated with pTab */ - - for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){ - if( zColl==0 || collationMatch(zColl, pIndex) ){ - int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); - sqlite3BeginWriteOperation(pParse, 0, iDb); - sqlite3RefillIndex(pParse, pIndex, -1); - } - } - } -} -#endif - -/* -** Recompute all indexes of all tables in all databases where the -** indexes use the collating sequence zColl. If zColl==0 then recompute -** all indexes everywhere. -*/ -#ifndef SQLITE_OMIT_REINDEX -static void reindexDatabases(Parse *pParse, char const *zColl){ - Db *pDb; /* A single database */ - int iDb; /* The database index number */ - sqlite3 *db = pParse->db; /* The database connection */ - HashElem *k; /* For looping over tables in pDb */ - Table *pTab; /* A table in the database */ - - assert( sqlite3BtreeHoldsAllMutexes(db) ); /* Needed for schema access */ - for(iDb=0, pDb=db->aDb; iDbnDb; iDb++, pDb++){ - assert( pDb!=0 ); - for(k=sqliteHashFirst(&pDb->pSchema->tblHash); k; k=sqliteHashNext(k)){ - pTab = (Table*)sqliteHashData(k); - reindexTable(pParse, pTab, zColl); - } - } -} -#endif - /* ** Generate code for the REINDEX command. ** ** REINDEX -- 1 ** REINDEX -- 2 -** REINDEX ?.? -- 3 -** REINDEX ?.? -- 4 +** REINDEX ?.? -- 3 +** REINDEX ?.? -- 4 +** REINDEX EXPRESSIONS -- 5 ** ** Form 1 causes all indexes in all attached databases to be rebuilt. ** Form 2 rebuilds all indexes in all databases that use the named ** collating function. Forms 3 and 4 rebuild the named index or all -** indexes associated with the named table. +** indexes associated with the named table, respectively. Form 5 +** rebuilds all expression indexes in addition to all collations, +** indexes, or tables named "EXPRESSIONS". ** -** If the argument to form 2 does not match any known collation, but -** it does match "EXPRESSIONS", then all expression indexes are rebuilt. +** If the name is ambiguous such that it matches two or more of +** forms 2 through 5, then rebuild the union of all matching indexes, +** taken care to avoid rebuilding the same index more than once. */ #ifndef SQLITE_OMIT_REINDEX void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){ CollSeq *pColl; /* Collating sequence to be reindexed, or NULL */ - char *z; /* Name of a table or index or collation */ - const char *zDb; /* Name of the database */ - Table *pTab; /* A table in the database */ - Index *pIndex; /* An index associated with pTab */ - int iDb; /* The database index number */ + char *z = 0; /* Name of a table or index or collation */ + const char *zDb = 0; /* Name of the database */ + int iReDb = -1; /* The database index number */ sqlite3 *db = pParse->db; /* The database connection */ Token *pObjName; /* Name of the table or index to be reindexed */ + int bMatch = 0; /* At least one name match */ + const char *zColl = 0; /* Rebuild indexes using this collation */ + Table *pReTab = 0; /* Rebuild all indexes of this table */ + Index *pReIndex = 0; /* Rebuild this index */ + int isExprIdx = 0; /* Rebuild all expression indexes */ + int bAll = 0; /* Rebuild all indexes */ /* Read the database schema. If an error occurs, leave an error message ** and code in pParse and return NULL. */ @@ -5644,43 +5580,64 @@ void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){ } if( pName1==0 ){ - reindexDatabases(pParse, 0); - return; + /* rebuild all indexes */ + bMatch = 1; + bAll = 1; }else if( NEVER(pName2==0) || pName2->z==0 ){ assert( pName1->z ); z = sqlite3NameFromToken(pParse->db, pName1); if( z==0 ) return; - pColl = sqlite3FindCollSeq(db, ENC(db), z, 0); - if( pColl ){ - reindexDatabases(pParse, z); - goto reindex_done; - } - sqlite3DbFree(db, z); - } - iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pObjName); - if( iDb<0 ) return; - z = sqlite3NameFromToken(db, pObjName); - if( z==0 ) return; - zDb = pName2->n ? db->aDb[iDb].zDbSName : 0; - pTab = sqlite3FindTable(db, z, zDb); - if( pTab ){ - reindexTable(pParse, pTab, 0); - goto reindex_done; + }else{ + iReDb = sqlite3TwoPartName(pParse, pName1, pName2, &pObjName); + if( iReDb<0 ) return; + z = sqlite3NameFromToken(db, pObjName); + if( z==0 ) return; + zDb = pName2->n ? db->aDb[iReDb].zDbSName : 0; } - pIndex = sqlite3FindIndex(db, z, zDb); - if( pIndex ){ - iDb = sqlite3SchemaToIndex(db, pIndex->pTable->pSchema); - sqlite3BeginWriteOperation(pParse, 0, iDb); - sqlite3RefillIndex(pParse, pIndex, -1); - goto reindex_done; + if( !bAll ){ + if( zDb==0 && sqlite3StrICmp(z, "expressions")==0 ){ + isExprIdx = 1; + bMatch = 1; + } + if( zDb==0 && (pColl = sqlite3FindCollSeq(db, ENC(db), z, 0))!=0 ){ + zColl = z; + bMatch = 1; + } + if( zColl==0 && (pReTab = sqlite3FindTable(db, z, zDb))!=0 ){ + bMatch = 1; + } + if( zColl==0 && (pReIndex = sqlite3FindIndex(db, z, zDb))!=0 ){ + bMatch = 1; + } } - if( zDb==0 && sqlite3StrICmp(z,zExpressionsKW)==0 ){ - reindexDatabases(pParse, zExpressionsKW); - goto reindex_done; + if( bMatch ){ + int iDb; + HashElem *k; + Table *pTab; + Index *pIdx; + Db *pDb; + for(iDb=0, pDb=db->aDb; iDbnDb; iDb++, pDb++){ + assert( pDb!=0 ); + if( iReDb>=0 && iReDb!=iDb ) continue; + for(k=sqliteHashFirst(&pDb->pSchema->tblHash); k; k=sqliteHashNext(k)){ + pTab = (Table*)sqliteHashData(k); + if( IsVirtual(pTab) ) continue; + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( bAll + || pTab==pReTab + || pIdx==pReIndex + || (isExprIdx && pIdx->bHasExpr) + || (zColl!=0 && collationMatch(zColl,pIdx)) + ){ + sqlite3BeginWriteOperation(pParse, 0, iDb); + sqlite3RefillIndex(pParse, pIdx, -1); + } + } /* End loop over indexes of pTab */ + } /* End loop over tables of iDb */ + } /* End loop over databases */ + }else{ + sqlite3ErrorMsg(pParse, "unable to identify the object to be reindexed"); } - sqlite3ErrorMsg(pParse, "unable to identify the object to be reindexed"); - -reindex_done: sqlite3DbFree(db, z); return; } diff --git a/test/auth.test b/test/auth.test index 1d56f70343..f378b900af 100644 --- a/test/auth.test +++ b/test/auth.test @@ -1922,8 +1922,8 @@ do_test auth-1.283 { execsql { REINDEX BINARY; } - set ::authargs -} {t3_idx1 {} main {} sqlite_autoindex_t3_1 {} main {}} + lsort -unique $::authargs +} {{} main sqlite_autoindex_t3_1 t3_idx1 t3_idx2} do_test auth-1.284 { set ::authargs {} execsql { @@ -1963,8 +1963,8 @@ ifcapable tempdb { execsql { REINDEX BINARY; } - set ::authargs - } {t3_idx1 {} temp {} sqlite_autoindex_t3_1 {} temp {}} + lsort -unique $::authargs + } {{} sqlite_autoindex_t3_1 t3_idx1 t3_idx2 temp} do_test auth-1.290 { set ::authargs {} execsql { -- 2.47.3