From: drh <> Date: Fri, 20 Mar 2026 14:37:39 +0000 (+0000) Subject: If there are not collating sequences, tables, or indexes named "EXPRESSIONS" X-Git-Tag: major-release~65^2~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b0a4b69b95e38b268a66936fe2e3044f47a811b9;p=thirdparty%2Fsqlite.git If there are not collating sequences, tables, or indexes named "EXPRESSIONS" then the "REINDEX EXPRESSIONS" command rebuilds all expression indexes. FossilOrigin-Name: df5c5aa26758e0dc00a9ccba29eac83071176d257e121207e0a13c54833b18c0 --- diff --git a/manifest b/manifest index f0c2d5b0ac..73930bf865 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Ensure\sthat\scollation\ssequences\son\snon-PK\scolumns\sof\sa\sWITHOUT\sROWID\stable\sare\sused\scorrectly\swhen\sthey\sare\spart\sof\sa\srow\svalue\scomparison.\sFix\sfor\sforum\spost\s[forum:7a308e933d\s|\s7a308e933d]. -D 2026-03-20T11:35:15.730 +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 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 b6efb70b142fd0f6a88ec1d9d43b905f2e38a59973c6f0e329e5cbe376a7f035 +F src/build.c beb8069bccb2ddd101b384df95f45b300999e70ab1be4061dd8da7bd27fb3579 F src/callback.c 3605bbf02bd7ed46c79cd48346db4a32fc51d67624400539c0532f4eead804ad F src/carray.c 3efe3982d5fb323334c29328a4e189ccaef6b95612a6084ad5fa124fd5db1179 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e @@ -2195,8 +2195,11 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c -P 88a931ea2ae734478928c2a6021b00b0f2be490328260630e176cf3cc7111472 -R 3a8ae62427f2beb0c4936f72e745b41a -U dan -Z 3025019227a8a6014385337b8f60a744 +P 078b3162d0d3d3035f4d3ad88664066d99c218a731ac481b9f1172529e26e4eb +R fffd90b5bfe8609c91e78fb9093e3446 +T *branch * reindex-expressions +T *sym-reindex-expressions * +T -sym-trunk * +U drh +Z 9182359fa4b768364b8403c0c94e4fa6 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.tags b/manifest.tags index bec971799f..270e9f4480 100644 --- a/manifest.tags +++ b/manifest.tags @@ -1,2 +1,2 @@ -branch trunk -tag trunk +branch reindex-expressions +tag reindex-expressions diff --git a/manifest.uuid b/manifest.uuid index 6dd2987495..19509a7e47 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -078b3162d0d3d3035f4d3ad88664066d99c218a731ac481b9f1172529e26e4eb +df5c5aa26758e0dc00a9ccba29eac83071176d257e121207e0a13c54833b18c0 diff --git a/src/build.c b/src/build.c index e357976200..94c3f52624 100644 --- a/src/build.c +++ b/src/build.c @@ -576,7 +576,7 @@ void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){ }else{ Index *p; /* Justification of ALWAYS(); The index must be on the list of - ** indices. */ + ** indexes. */ p = pIndex->pTable->pIndex; while( ALWAYS(p) && p->pNext!=pIndex ){ p = p->pNext; } if( ALWAYS(p && p->pNext==pIndex) ){ @@ -787,7 +787,7 @@ void sqlite3DeleteColumnNames(sqlite3 *db, Table *pTable){ ** ** This routine just deletes the data structure. It does not unlink ** the table data structure from the hash table. But it does destroy -** memory structures of the indices and foreign keys associated with +** memory structures of the indexes and foreign keys associated with ** the table. ** ** The db parameter is optional. It is needed if the Table object @@ -814,7 +814,7 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){ } #endif - /* Delete all indices associated with this table. */ + /* Delete all indexes associated with this table. */ for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){ pNext = pIndex->pNext; assert( pIndex->pSchema==pTable->pSchema @@ -868,7 +868,7 @@ void sqlite3DeleteTableGeneric(sqlite3 *db, void *pTable){ /* ** Unlink the given table from the hash tables and the delete the -** table structure with all its indices and foreign keys. +** table structure with all its indexes and foreign keys. */ void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char *zTabName){ Table *p; @@ -1320,8 +1320,8 @@ void sqlite3StartTable( ** the schema table. Note in particular that we must go ahead ** and allocate the record number for the table entry now. Before any ** PRIMARY KEY or UNIQUE keywords are parsed. Those keywords will cause - ** indices to be created and the table record must come before the - ** indices. Hence, the record number for the table must be allocated + ** indexes to be created and the table record must come before the + ** indexes. Hence, the record number for the table must be allocated ** now. */ if( !db->init.busy && (v = sqlite3GetVdbe(pParse))!=0 ){ @@ -2347,7 +2347,7 @@ static void recomputeColumnsNotIndexed(Index *pIdx){ ** columns are part of KeyInfo.nAllField and are not used for ** sorting or lookup or uniqueness checks. ** (6) Replace the rowid tail on all automatically generated UNIQUE -** indices with the PRIMARY KEY columns. +** indexes with the PRIMARY KEY columns. ** ** For virtual tables, only (1) is performed. */ @@ -2448,7 +2448,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ /* The root page of the PRIMARY KEY is the table root page */ pPk->tnum = pTab->tnum; - /* Update the in-memory representation of all UNIQUE indices by converting + /* Update the in-memory representation of all UNIQUE indexes by converting ** the final rowid column into one or more columns of the PRIMARY KEY. */ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ @@ -2781,7 +2781,7 @@ void sqlite3EndTable( } #endif - /* Estimate the average row size for the table and for all implied indices */ + /* Estimate the average row size for the table and for all implied indexes */ estimateTableWidth(p); for(pIdx=p->pIndex; pIdx; pIdx=pIdx->pNext){ estimateIndexWidth(pIdx); @@ -3241,13 +3241,13 @@ static void sqliteViewResetAll(sqlite3 *db, int idx){ ** to iTo. ** ** Ticket #1728: The symbol table might still contain information -** on tables and/or indices that are the process of being deleted. -** If you are unlucky, one of those deleted indices or tables might +** on tables and/or indexes that are the process of being deleted. +** If you are unlucky, one of those deleted indexes or tables might ** have the same rootpage number as the real table or index that is ** being moved. So we cannot stop searching after the first match -** because the first match might be for one of the deleted indices +** because the first match might be for one of the deleted indexes ** or tables and not the table/index that is actually being moved. -** We must continue looping until all tables and indices with +** We must continue looping until all tables and indexes with ** rootpage==iFrom have been converted to have a rootpage of iTo ** in order to be certain that we got the right one. */ @@ -3307,7 +3307,7 @@ static void destroyRootPage(Parse *pParse, int iTable, int iDb){ } /* -** Write VDBE code to erase table pTab and all associated indices on disk. +** Write VDBE code to erase table pTab and all associated indexes on disk. ** Code to update the sqlite_schema tables and internal schema definitions ** in case a root-page belonging to another table is moved by the btree layer ** is also added (this can happen with an auto-vacuum database). @@ -3946,7 +3946,7 @@ void sqlite3CreateIndex( ExprList *pList, /* A list of columns to be indexed */ int onError, /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ Token *pStart, /* The CREATE token that begins this statement */ - Expr *pPIWhere, /* WHERE clause for partial indices */ + Expr *pPIWhere, /* WHERE clause for partial indexes */ int sortOrder, /* Sort order of primary key when pList==NULL */ int ifNotExist, /* Omit error if index already exists */ u8 idxType /* The index type */ @@ -4060,7 +4060,7 @@ void sqlite3CreateIndex( ** Find the name of the index. Make sure there is not already another ** index or table with the same name. ** - ** Exception: If we are reading the names of permanent indices from the + ** Exception: If we are reading the names of permanent indexes from the ** sqlite_schema table (because some other process changed the schema) and ** one of the index names collides with the name of a temporary table or ** index, then we will continue to process this index. @@ -4323,8 +4323,8 @@ void sqlite3CreateIndex( ** ** Either way, check to see if the table already has such an index. If ** so, don't bother creating this one. This only applies to - ** automatically created indices. Users can do as they wish with - ** explicit indices. + ** automatically created indexes. Users can do as they wish with + ** explicit indexes. ** ** Two UNIQUE or PRIMARY KEY constraints are considered equivalent ** (and thus suppressing the second one) even if they have different @@ -4332,7 +4332,7 @@ void sqlite3CreateIndex( ** ** If there are different collating sequences or if the columns of ** the constraint occur in different orders, then the constraints are - ** considered distinct and both result in separate indices. + ** considered distinct and both result in separate indexes. */ Index *pIdx; for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ @@ -4546,7 +4546,7 @@ exit_create_index: ** ** Apart from that, we have little to go on besides intuition as to ** how aiRowEst[] should be initialized. The numbers generated here -** are based on typical values found in actual indices. +** are based on typical values found in actual indexes. */ void sqlite3DefaultRowEst(Index *pIdx){ /* 10, 9, 8, 7, 6 */ @@ -5522,19 +5522,44 @@ void sqlite3RowidConstraint( P5_ConstraintUnique); } +/* +** 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. */ #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 = pIndex->azColl[i]; - assert( z!=0 || pIndex->aiColumn[i]<0 ); - if( pIndex->aiColumn[i]>=0 && 0==sqlite3StrICmp(z, zColl) ){ - return 1; + 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 */ } } return 0; @@ -5542,8 +5567,8 @@ static int collationMatch(const char *zColl, Index *pIndex){ #endif /* -** Recompute all indices of pTab that use the collating sequence pColl. -** If pColl==0 then recompute all indices of pTab. +** 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){ @@ -5562,9 +5587,9 @@ static void reindexTable(Parse *pParse, Table *pTab, char const *zColl){ #endif /* -** Recompute all indices of all tables in all databases where the -** indices use the collating sequence pColl. If pColl==0 then recompute -** all indices everywhere. +** 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){ @@ -5593,15 +5618,18 @@ static void reindexDatabases(Parse *pParse, char const *zColl){ ** REINDEX ?.? -- 3 ** REINDEX ?.? -- 4 ** -** Form 1 causes all indices in all attached databases to be rebuilt. -** Form 2 rebuilds all indices in all databases that use the named +** 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 -** indices associated with the named table. +** indexes associated with the named table. +** +** If the argument to form 2 does not match any known collation, but +** it does match "EXPRESSIONS", then all expression indexes are rebuilt. */ #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 */ + 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 */ @@ -5619,17 +5647,15 @@ void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){ reindexDatabases(pParse, 0); return; }else if( NEVER(pName2==0) || pName2->z==0 ){ - char *zColl; assert( pName1->z ); - zColl = sqlite3NameFromToken(pParse->db, pName1); - if( !zColl ) return; - pColl = sqlite3FindCollSeq(db, ENC(db), zColl, 0); + z = sqlite3NameFromToken(pParse->db, pName1); + if( z==0 ) return; + pColl = sqlite3FindCollSeq(db, ENC(db), z, 0); if( pColl ){ - reindexDatabases(pParse, zColl); - sqlite3DbFree(db, zColl); - return; + reindexDatabases(pParse, z); + goto reindex_done; } - sqlite3DbFree(db, zColl); + sqlite3DbFree(db, z); } iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pObjName); if( iDb<0 ) return; @@ -5639,18 +5665,24 @@ void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){ pTab = sqlite3FindTable(db, z, zDb); if( pTab ){ reindexTable(pParse, pTab, 0); - sqlite3DbFree(db, z); - return; + goto reindex_done; } pIndex = sqlite3FindIndex(db, z, zDb); - sqlite3DbFree(db, z); if( pIndex ){ iDb = sqlite3SchemaToIndex(db, pIndex->pTable->pSchema); sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3RefillIndex(pParse, pIndex, -1); - return; + goto reindex_done; + } + if( zDb==0 && sqlite3StrICmp(z,zExpressionsKW)==0 ){ + reindexDatabases(pParse, zExpressionsKW); + goto reindex_done; } sqlite3ErrorMsg(pParse, "unable to identify the object to be reindexed"); + +reindex_done: + sqlite3DbFree(db, z); + return; } #endif