From: dan Date: Sat, 23 Sep 2023 19:25:03 +0000 (+0000) Subject: Avoid an error when parsing a schema that contains indexes with WHERE clauses contain... X-Git-Tag: version-3.44.0~182^2~4 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7a9bbfe1fae1e2155e3f972c2a3fa866b11c382e;p=thirdparty%2Fsqlite.git Avoid an error when parsing a schema that contains indexes with WHERE clauses containing unknown collation sequences. FossilOrigin-Name: d5dd39e37ab994ce5d75688a40c6494c17807431529e85eb2b11d58f367fe232 --- diff --git a/manifest b/manifest index c2647a59bf..fabc239389 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sfailing\sassert()\scaused\sby\schanges\son\sthis\sbranch. -D 2023-09-23T18:52:22.726 +C Avoid\san\serror\swhen\sparsing\sa\sschema\sthat\scontains\sindexes\swith\sWHERE\sclauses\scontaining\sunknown\scollation\ssequences. +D 2023-09-23T19:25:03.559 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -652,7 +652,7 @@ F src/btmutex.c 79a43670447eacc651519a429f6ece9fd638563cf95b469d6891185ddae2b522 F src/btree.c d2e73513d382e6e4829b823fb41b5f2eddd9c5984b1492a7a6333cd91be15601 F src/btree.h 03e3356f5208bcab8eed4e094240fdac4a7f9f5ddf5e91045ce589f67d47c240 F src/btreeInt.h 91a9e0c41a0e71fa91a742ec285c63dd8dcb38b73d14fae0ed7209174ff0fdc1 -F src/build.c a52c1e9a1747a5650aa9eb16c417bb9be07d2e66473431e73bbbd1b24047ed61 +F src/build.c a08d098ad1bfd5d46a5f3a6e29a822897e9a021ddba7c021c7c1ae5d4366d1a0 F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/ctime.c db847fac81837ff5e5028a5f7505147ac645ae676104adc5bc08e356f243de40 @@ -715,7 +715,7 @@ F src/shell.c.in 62708bea44d4e43aa7b1270ed422d1d29e82297924d4e0f223c39336a3f582f F src/sqlite.h.in 931a58d119d5cf87110648f39fa0bb9f1738b0068cb68250d893304a471bd6c0 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 2f30b2671f4c03cd27a43f039e11251391066c97d11385f5f963bb40b03038ac -F src/sqliteInt.h 876f5ffc0eaa876a55b2b220fff6cc3e8250fa207b864ab10b07949f1fba9b57 +F src/sqliteInt.h 1fc2a2ee3016dd3ce433db8432bef50b8266d017527d38ccb8abed87c8305d76 F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -795,7 +795,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c 01e051a1e713d9eabdb25df38602837cec8f4c2cae448ce2cf6accc87af903e9 F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452 F src/walker.c 7c7ea0115345851c3da4e04e2e239a29983b61fb5b038b94eede6aba462640e2 -F src/where.c e2ce695f5963620d4c403fb97e51216ca364247090e806bec938c82af9573eee +F src/where.c c68648edade39a91687f2c63aacb07cb0f6a823814289af35b0aa1d43c62fed2 F src/whereInt.h 4b38c5889514e3aead3f27d0ee9a26e47c3f150efc59e2a8b4e3bc8835e4d7a1 F src/wherecode.c 5d77db30a2a3dd532492ae882de114edba2fae672622056b1c7fd61f5917a8f1 F src/whereexpr.c dc5096eca5ed503999be3bdee8a90c51361289a678d396a220912e9cb73b3c00 @@ -1249,7 +1249,7 @@ F test/index6.test b376a648e85aa71c50074382784e6cb0c126ec46e43d1ad15af9a4d234c52 F test/index7.test b238344318e0b4e42126717f6554f0e7dfd0b39cecad4b736039b43e1e3b6eb3 F test/index8.test caa097735c91dbc23d8a402f5e63a2a03c83840ba3928733ed7f9a03f8a912a3 F test/index9.test 2ac891806a4136ef3e91280477e23114e67575207dc331e6797fa0ed9379f997 -F test/indexA.test 0f2a0f67f49a437714f374b304f924c692319c1cb56b46d2de67b735a3d62eb2 +F test/indexA.test 3ae5e98564c3a36d68942b218e53126a6996a48b95a5eb38b9aa7adf19784949 F test/indexedby.test f21eca4f7a6ffe14c8500a7ad6cd53166666c99e5ccd311842a28bc94a195fe0 F test/indexexpr1.test 62558b1cfd7ccbe7bc015849cc6d1a13ef124e80cbd5b3a98dc66c3c9cce0cf4 F test/indexexpr2.test 1c382e81ef996d8ae8b834a74f2a9013dddf59214c32201d7c8a656d739f999a @@ -2122,8 +2122,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 7c4210253b660b385d14de3ae7ab30f038036308e1164ec17b40e2805b9b2235 -R 79f7b7ac29c4d246921dcdd8df4ce062 +P d0e21f20bd8643d21c4b88fb75b83939d68e9b7118f7289a164656f11d2ee555 +R 2173ec5daddfa5c65583f3ffe032fd88 U dan -Z 1c7c371de6fd21faea295aab95777c4c +Z 5d069b0b0f770f8e55162243a5db6bc9 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b92702d6d8..a1484a68c3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d0e21f20bd8643d21c4b88fb75b83939d68e9b7118f7289a164656f11d2ee555 \ No newline at end of file +d5dd39e37ab994ce5d75688a40c6494c17807431529e85eb2b11d58f367fe232 \ No newline at end of file diff --git a/src/build.c b/src/build.c index b3e963974f..3e011c625d 100644 --- a/src/build.c +++ b/src/build.c @@ -2315,9 +2315,6 @@ static void recomputeColumnsNotIndexed(Parse *pParse, Index *pIdx){ } } pIdx->colNotIdxed = ~m; - if( pIdx->pPartIdxWhere ){ - sqlite3WherePartIdxExpr(pParse, pIdx, pIdx->pPartIdxWhere, 0, 0); - } assert( (pIdx->colNotIdxed>>63)==1 ); /* See note-20221022-a */ } @@ -4277,7 +4274,6 @@ void sqlite3CreateIndex( assert( HasRowid(pTab) || pTab->iPKey<0 || sqlite3TableColumnToIndex(pIndex, pTab->iPKey)>=0 ); recomputeColumnsNotIndexed(pParse, pIndex); - if( pParse->nErr ) goto exit_create_index; if( pTblName!=0 && pIndex->nColumn>=pTab->nCol ){ pIndex->isCovering = 1; for(j=0; jnCol; j++){ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 35864de5e4..7221974991 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4940,7 +4940,6 @@ int sqlite3WhereOkOnePass(WhereInfo*, int*); #define ONEPASS_SINGLE 1 /* ONEPASS valid for a single row update */ #define ONEPASS_MULTI 2 /* ONEPASS is valid for multiple rows */ int sqlite3WhereUsesDeferredSeek(WhereInfo*); -void sqlite3WherePartIdxExpr(Parse*, Index*, Expr*, int, SrcItem*); void sqlite3ExprCodeLoadIndexColumn(Parse*, Index*, int, int, int); int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8); void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int); diff --git a/src/where.c b/src/where.c index 65f7f5acdf..777e49854a 100644 --- a/src/where.c +++ b/src/where.c @@ -3498,6 +3498,104 @@ static SQLITE_NOINLINE u32 whereIsCoveringIndex( return rc; } +/* +** This is an sqlite3ParserAddCleanup() callback that is invoked to +** free the Parse->pIdxEpr list when the Parse object is destroyed. +*/ +static void whereIndexedExprCleanup(sqlite3 *db, void *pObject){ + IndexedExpr **pp = (IndexedExpr**)pObject; + while( *pp!=0 ){ + IndexedExpr *p = *pp; + *pp = p->pIENext; + sqlite3ExprDelete(db, p->pExpr); + sqlite3DbFreeNN(db, p); + } +} + +/* +** This function is called for a partial index - one with a WHERE clause - in +** two scenarios. In both cases, it determines whether or not the WHERE +** clause on the index implies that a column of the table may be safely +** replaced by a constant expression. For example, in the following +** SELECT: +** +** CREATE INDEX i1 ON t1(b, c) WHERE a=; +** SELECT a, b, c FROM t1 WHERE a= AND b=?; +** +** The "a" in the select-list may be replaced by , iff: +** +** (a) is a constant expression, and +** (b) The (a=) comparison uses the BINARY collation sequence, and +** (c) Column "a" has an affinity other than NONE or BLOB. +** +** If argument pItem is NULL, then pMask must not be NULL. In this case this +** function is being called as part of determining whether or not pIdx +** is a covering index. This function clears any bits in (*pMask) +** corresponding to columns that may be replaced by constants as described +** above. +** +** Otherwise, if pItem is not NULL, then this function is being called +** as part of coding a loop that uses index pIdx. In this case, add entries +** to the Parse.pIdxPartExpr list for each column that can be replaced +** by a constant. +*/ +static void wherePartIdxExpr( + Parse *pParse, /* Parse context */ + Index *pIdx, /* Partial index being processed */ + Expr *pPart, /* WHERE clause being processed */ + Bitmask *pMask, /* Mask to clear bits in */ + int iIdxCur, /* Cursor number for index */ + SrcItem *pItem /* The FROM clause entry for the table */ +){ + assert( pItem==0 || (pItem->fg.jointype & JT_RIGHT)==0 ); + assert( (pItem==0 || pMask==0) && (pMask!=0 || pItem!=0) ); + + if( pPart->op==TK_AND ){ + wherePartIdxExpr(pParse, pIdx, pPart->pRight, pMask, iIdxCur, pItem); + pPart = pPart->pLeft; + } + + if( (pPart->op==TK_EQ || pPart->op==TK_IS) ){ + Expr *pLeft = pPart->pLeft; + Expr *pRight = pPart->pRight; + u8 aff; + + if( pRight->op==TK_COLUMN ){ + SWAP(Expr*, pLeft, pRight); + } + + if( pLeft->op!=TK_COLUMN ) return; + if( !sqlite3ExprIsConstant(pRight) ) return; + if( !sqlite3IsBinary(sqlite3ExprCompareCollSeq(pParse, pPart)) ) return; + if( pLeft->iColumn<0 ) return; + aff = pIdx->pTable->aCol[pLeft->iColumn].affinity; + if( aff>=SQLITE_AFF_TEXT ){ + if( pItem ){ + sqlite3 *db = pParse->db; + IndexedExpr *p = (IndexedExpr*)sqlite3DbMallocZero(db, sizeof(*p)); + if( p ){ + int bNullRow = (pItem->fg.jointype&(JT_LEFT|JT_LTORJ))!=0; + p->pExpr = sqlite3ExprDup(db, pRight, 0); + p->iDataCur = pItem->iCursor; + p->iIdxCur = iIdxCur; + p->iIdxCol = pLeft->iColumn; + p->bMaybeNullRow = bNullRow; + p->pIENext = pParse->pIdxPartExpr; + p->aff = aff; + pParse->pIdxPartExpr = p; + if( p->pIENext==0 ){ + void *pArg = (void*)&pParse->pIdxPartExpr; + sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pArg); + } + } + }else if( pLeft->iColumn<(BMS-1) ){ + *pMask &= ~((Bitmask)1 << pLeft->iColumn); + } + } + } +} + + /* ** Add all WhereLoop objects for a single table of the join where the table ** is identified by pBuilder->pNew->iTab. That table is guaranteed to be @@ -3713,6 +3811,9 @@ static int whereLoopAddBtree( pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED; }else{ m = pSrc->colUsed & pProbe->colNotIdxed; + if( pProbe->pPartIdxWhere ){ + wherePartIdxExpr(pWInfo->pParse, pProbe, pProbe->pPartIdxWhere, &m, 0, 0); + } pNew->wsFlags = WHERE_INDEXED; if( m==TOPBIT || (pProbe->bHasExpr && !pProbe->bHasVCol && m!=0) ){ u32 isCov = whereIsCoveringIndex(pWInfo, pProbe, pSrc->iCursor); @@ -5668,20 +5769,6 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful( } } -/* -** This is an sqlite3ParserAddCleanup() callback that is invoked to -** free the Parse->pIdxEpr list when the Parse object is destroyed. -*/ -static void whereIndexedExprCleanup(sqlite3 *db, void *pObject){ - IndexedExpr **pp = (IndexedExpr**)pObject; - while( *pp!=0 ){ - IndexedExpr *p = *pp; - *pp = p->pIENext; - sqlite3ExprDelete(db, p->pExpr); - sqlite3DbFreeNN(db, p); - } -} - /* ** The index pIdx is used by a query and contains one or more expressions. ** In other words pIdx is an index on an expression. iIdxCur is the cursor @@ -5749,85 +5836,6 @@ static SQLITE_NOINLINE void whereAddIndexedExpr( } } -/* -** This function is called for a partial index - one with a WHERE clause - in -** two scenarios. In both cases, it determines whether or not the WHERE -** clause on the index implies that a column of the table may be safely -** replaced by a constant expression. For example, in the following -** SELECT: -** -** CREATE INDEX i1 ON t1(b, c) WHERE a=; -** SELECT a, b, c FROM t1 WHERE a= AND b=?; -** -** The "a" in the select-list may be replaced by , iff: -** -** (a) is a constant expression, and -** (b) The (a=) comparison uses the BINARY collation sequence, and -** (c) Column "a" has an affinity other than NONE or BLOB. -** -** If argument pTabItem is NULL, then this function is being called as part -** of parsing the CREATE INDEX statement. In that case the Index.colNotIdxed -** mask is updated to mark any columns that will be replaced by constant -** values as indexed. -** -** Otherwise, if pTabItem is not NULL, then this function is being called -** as part of coding a loop that uses index pIdx. In this case, add entries -** to the Parse.pIdxPartExpr list for each column that can be replaced -** by a constant. -*/ -void sqlite3WherePartIdxExpr( - Parse *pParse, /* Parse context */ - Index *pIdx, /* Partial index being processed */ - Expr *pPart, /* WHERE clause being processed */ - int iIdxCur, /* Cursor number for index */ - SrcItem *pTabItem /* The FROM clause entry for the table */ -){ - assert( pTabItem==0 || (pTabItem->fg.jointype & JT_RIGHT)==0 ); - if( pPart->op==TK_AND ){ - sqlite3WherePartIdxExpr(pParse, pIdx, pPart->pRight, iIdxCur, pTabItem); - pPart = pPart->pLeft; - } - - if( (pPart->op==TK_EQ || pPart->op==TK_IS) ){ - Expr *pLeft = pPart->pLeft; - Expr *pRight = pPart->pRight; - u8 aff; - - if( pRight->op==TK_COLUMN ){ - SWAP(Expr*, pLeft, pRight); - } - - if( pLeft->op!=TK_COLUMN ) return; - if( !sqlite3ExprIsConstant(pRight) ) return; - if( !sqlite3IsBinary(sqlite3ExprCompareCollSeq(pParse, pPart)) ) return; - if( pLeft->iColumn<0 ) return; - aff = pIdx->pTable->aCol[pLeft->iColumn].affinity; - if( aff>=SQLITE_AFF_TEXT ){ - if( pTabItem ){ - sqlite3 *db = pParse->db; - IndexedExpr *p = (IndexedExpr*)sqlite3DbMallocZero(db, sizeof(*p)); - if( p ){ - int bNullRow = (pTabItem->fg.jointype&(JT_LEFT|JT_LTORJ))!=0; - p->pExpr = sqlite3ExprDup(db, pRight, 0); - p->iDataCur = pTabItem->iCursor; - p->iIdxCur = iIdxCur; - p->iIdxCol = pLeft->iColumn; - p->bMaybeNullRow = bNullRow; - p->pIENext = pParse->pIdxPartExpr; - p->aff = aff; - pParse->pIdxPartExpr = p; - if( p->pIENext==0 ){ - void *pArg = (void*)&pParse->pIdxPartExpr; - sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pArg); - } - } - }else if( pLeft->iColumn<(BMS-1) ){ - pIdx->colNotIdxed &= ~((Bitmask)1 << pLeft->iColumn); - } - } - } -} - /* ** Set the reverse-scan order mask to one for all tables in the query ** with the exception of MATERIALIZED common table expressions that have @@ -6436,8 +6444,8 @@ WhereInfo *sqlite3WhereBegin( whereAddIndexedExpr(pParse, pIx, iIndexCur, pTabItem); } if( pIx->pPartIdxWhere && (pTabItem->fg.jointype & JT_RIGHT)==0 ){ - sqlite3WherePartIdxExpr( - pParse, pIx, pIx->pPartIdxWhere, iIndexCur, pTabItem + wherePartIdxExpr( + pParse, pIx, pIx->pPartIdxWhere, 0, iIndexCur, pTabItem ); } } diff --git a/test/indexA.test b/test/indexA.test index 2e67359f3b..d8823ccd95 100644 --- a/test/indexA.test +++ b/test/indexA.test @@ -255,18 +255,18 @@ do_catchsql_test 5.1 { CREATE INDEX ex1 ON t1(c) WHERE b IS 'abc' COLLATE g; } {1 {no such collation sequence: g}} -#proc xyz {lhs rhs} { -# return [string compare $lhs $rhs] -#} -#db collate xyz xyz -#do_execsql_test 5.2 { -# CREATE INDEX ex1 ON t1(c) WHERE b IS 'abc' COLLATE xyz; -#} -#db close -#sqlite3 db test.db -#do_execsql_test 5.3 { -# SELECT * FROM t1 -#} +proc xyz {lhs rhs} { + return [string compare $lhs $rhs] +} +db collate xyz xyz +do_execsql_test 5.2 { + CREATE INDEX ex1 ON t1(c) WHERE b IS 'abc' COLLATE xyz; +} +db close +sqlite3 db test.db +do_execsql_test 5.3 { + SELECT * FROM t1 +}