In partial index scans, if the WHERE clause implies a constant value for a table column, replace occurences of that table column with the constant. This increases the likelihood of the partial index being a covering index.
FossilOrigin-Name: 8d4160910d6512469cb0a060b89c2509dde1c8e33d9358034d7fb39cd494eabe
This commit is contained in:
commit
47b427423d
24
manifest
24
manifest
@ -1,5 +1,5 @@
|
||||
C Enable\s-Wall\sfor\sthe\scompiler\son\s--enable-debug\sbuilds.
|
||||
D 2023-09-25T14:20:07.380
|
||||
C In\spartial\sindex\sscans,\sif\sthe\sWHERE\sclause\simplies\sa\sconstant\svalue\sfor\sa\stable\scolumn,\sreplace\soccurences\sof\sthat\stable\scolumn\swith\sthe\sconstant.\sThis\sincreases\sthe\slikelihood\sof\sthe\spartial\sindex\sbeing\sa\scovering\sindex.
|
||||
D 2023-09-25T17:46:02.262
|
||||
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 79a4edcee69df5f20963d7b11faf0820d25e6f8c11ef3a9f868d14be87834711
|
||||
F src/build.c a08d098ad1bfd5d46a5f3a6e29a822897e9a021ddba7c021c7c1ae5d4366d1a0
|
||||
F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490
|
||||
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
||||
F src/ctime.c db847fac81837ff5e5028a5f7505147ac645ae676104adc5bc08e356f243de40
|
||||
@ -660,7 +660,7 @@ F src/date.c eebc54a00e888d3c56147779e9f361b77d62fd69ff2008c5373946aa1ba1d574
|
||||
F src/dbpage.c f3eea5f7ec47e09ee7da40f42b25092ecbe961fc59566b8e5f705f34335b2387
|
||||
F src/dbstat.c ec92074baa61d883de58c945162d9e666c13cd7cf3a23bc38b4d1c4d0b2c2bef
|
||||
F src/delete.c cb766727c78e715f9fb7ec8a7d03658ed2a3016343ca687acfcec9083cdca500
|
||||
F src/expr.c 9902bebcc9fa2b2c4cc94b7aa5615afe1affc98a986553aa7b239971c54ddea8
|
||||
F src/expr.c 8a5045ac7aae5ecebe7236ec05841693b5a0012af70a4d73933333172546c50d
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c a7fcbf7e66d14dbb73cf49f31489ebf66d0e6006c62b95246924a3bae9f37b36
|
||||
F src/func.c 154f08966f8a3a7cad6c438205df1abf58fb2826961a0683e82e120fa647e84c
|
||||
@ -705,7 +705,7 @@ F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5
|
||||
F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00
|
||||
F src/pragma.c ea85fb64b9481004aaa5d0fa66a57c804074f46145ef2409894d1fc2f4b0cf8d
|
||||
F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7
|
||||
F src/prepare.c 80548297dc0e1fb3139cdebffb5a1bcac3dfac66d791012dd74838e70445072d
|
||||
F src/prepare.c bde74add20fc0e8ce0c4e937a1f70a36d17413afe4f71d3e103f5cb74b17c8d9
|
||||
F src/printf.c e3ba080e2f409f9bfcc8d34724e6fc160e9c718dc92d0548f6b71b8b6f860ce2
|
||||
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
|
||||
F src/resolve.c 0c3046b88901336709cd09f474303a16fc54bce13a2befcab66d0fa6b44ca869
|
||||
@ -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 f2d713fac835f32b131d8a334595b0c471ede3796dab527c705d2b03c32d14e9
|
||||
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 b05f3e60d576a0415948ca1e86754a3c564c0d9e89e3011e35f849cc4d818ef8
|
||||
F src/where.c 8e4f2d16c6fe0f6e6905876096298bc25bf5405e9c937f2c5c4120d519253d71
|
||||
F src/whereInt.h 4b38c5889514e3aead3f27d0ee9a26e47c3f150efc59e2a8b4e3bc8835e4d7a1
|
||||
F src/wherecode.c 5d77db30a2a3dd532492ae882de114edba2fae672622056b1c7fd61f5917a8f1
|
||||
F src/whereexpr.c dc5096eca5ed503999be3bdee8a90c51361289a678d396a220912e9cb73b3c00
|
||||
@ -1249,6 +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 dd9e613f5944c9005192ab2f7cf5fe2ccd9b92d7ed3c6d0c41bdd5ebf4d473ae
|
||||
F test/indexedby.test f21eca4f7a6ffe14c8500a7ad6cd53166666c99e5ccd311842a28bc94a195fe0
|
||||
F test/indexexpr1.test 62558b1cfd7ccbe7bc015849cc6d1a13ef124e80cbd5b3a98dc66c3c9cce0cf4
|
||||
F test/indexexpr2.test 1c382e81ef996d8ae8b834a74f2a9013dddf59214c32201d7c8a656d739f999a
|
||||
@ -2121,8 +2122,9 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 5655c51f103c6dfade799ffb5ca2eb21b4360ada598741f373c952ae732535aa
|
||||
R 1fdce7fde8f7912c67e4883b280923da
|
||||
U drh
|
||||
Z 0b02ef336b3df456883148d78452b947
|
||||
P 63a7b521390001939909d43d908af78f7df7cb92272d2b74f038da918c2bef05 2c805dc68112db7f06aea56a731a33e046e876851bdda3e725ba96f6da1bf2ee
|
||||
R 6be385139a174b4b0100f8c1bdb9ec34
|
||||
T +closed 2c805dc68112db7f06aea56a731a33e046e876851bdda3e725ba96f6da1bf2ee
|
||||
U dan
|
||||
Z 7fdbd4c7ea8abf3c4a48b87d977519f6
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
@ -1 +1 @@
|
||||
63a7b521390001939909d43d908af78f7df7cb92272d2b74f038da918c2bef05
|
||||
8d4160910d6512469cb0a060b89c2509dde1c8e33d9358034d7fb39cd494eabe
|
@ -2302,7 +2302,7 @@ static int isDupColumn(Index *pIdx, int nKey, Index *pPk, int iCol){
|
||||
** The colNotIdxed mask is AND-ed with the SrcList.a[].colUsed mask
|
||||
** to determine if the index is covering index.
|
||||
*/
|
||||
static void recomputeColumnsNotIndexed(Index *pIdx){
|
||||
static void recomputeColumnsNotIndexed(Parse *pParse, Index *pIdx){
|
||||
Bitmask m = 0;
|
||||
int j;
|
||||
Table *pTab = pIdx->pTable;
|
||||
@ -2493,7 +2493,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
}
|
||||
assert( pPk->nColumn==j );
|
||||
assert( pTab->nNVCol<=j );
|
||||
recomputeColumnsNotIndexed(pPk);
|
||||
recomputeColumnsNotIndexed(pParse, pPk);
|
||||
}
|
||||
|
||||
|
||||
@ -4273,7 +4273,7 @@ void sqlite3CreateIndex(
|
||||
** it as a covering index */
|
||||
assert( HasRowid(pTab)
|
||||
|| pTab->iPKey<0 || sqlite3TableColumnToIndex(pIndex, pTab->iPKey)>=0 );
|
||||
recomputeColumnsNotIndexed(pIndex);
|
||||
recomputeColumnsNotIndexed(pParse, pIndex);
|
||||
if( pTblName!=0 && pIndex->nColumn>=pTab->nCol ){
|
||||
pIndex->isCovering = 1;
|
||||
for(j=0; j<pTab->nCol; j++){
|
||||
|
40
src/expr.c
40
src/expr.c
@ -4252,6 +4252,41 @@ static SQLITE_NOINLINE int sqlite3IndexedExprLookup(
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Expresion pExpr is guaranteed to be a TK_COLUMN or equivalent. This
|
||||
** function checks the Parse.pIdxPartExpr list to see if this column
|
||||
** can be replaced with a constant value. If so, it generates code to
|
||||
** put the constant value in a register (ideally, but not necessarily,
|
||||
** register iTarget) and returns the register number.
|
||||
**
|
||||
** Or, if the TK_COLUMN cannot be replaced by a constant, zero is
|
||||
** returned.
|
||||
*/
|
||||
static int exprPartidxExprLookup(Parse *pParse, Expr *pExpr, int iTarget){
|
||||
IndexedExpr *p;
|
||||
for(p=pParse->pIdxPartExpr; p; p=p->pIENext){
|
||||
if( pExpr->iColumn==p->iIdxCol && pExpr->iTable==p->iDataCur ){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int addr = 0;
|
||||
int ret;
|
||||
|
||||
if( p->bMaybeNullRow ){
|
||||
addr = sqlite3VdbeAddOp1(v, OP_IfNullRow, p->iIdxCur);
|
||||
}
|
||||
ret = sqlite3ExprCodeTarget(pParse, p->pExpr, iTarget);
|
||||
sqlite3VdbeAddOp4(pParse->pVdbe, OP_Affinity, ret, 1, 0,
|
||||
(const char*)&p->aff, 1);
|
||||
if( addr ){
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
sqlite3VdbeChangeP3(v, addr, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Generate code into the current Vdbe to evaluate the given
|
||||
** expression. Attempt to store the results in register "target".
|
||||
@ -4409,6 +4444,11 @@ expr_code_doover:
|
||||
iTab = pParse->iSelfTab - 1;
|
||||
}
|
||||
}
|
||||
else if( pParse->pIdxPartExpr
|
||||
&& 0!=(r1 = exprPartidxExprLookup(pParse, pExpr, target))
|
||||
){
|
||||
return r1;
|
||||
}
|
||||
assert( ExprUseYTab(pExpr) );
|
||||
assert( pExpr->y.pTab!=0 );
|
||||
iReg = sqlite3ExprCodeGetColumn(pParse, pExpr->y.pTab,
|
||||
|
@ -598,8 +598,6 @@ void sqlite3ParseObjectReset(Parse *pParse){
|
||||
db->lookaside.sz = db->lookaside.bDisable ? 0 : db->lookaside.szTrue;
|
||||
assert( pParse->db->pParse==pParse );
|
||||
db->pParse = pParse->pOuterParse;
|
||||
pParse->db = 0;
|
||||
pParse->disableLookaside = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3790,6 +3790,7 @@ struct Parse {
|
||||
int *aLabel; /* Space to hold the labels */
|
||||
ExprList *pConstExpr;/* Constant expressions */
|
||||
IndexedExpr *pIdxEpr;/* List of expressions used by active indexes */
|
||||
IndexedExpr *pIdxPartExpr; /* Exprs constrained by index WHERE clauses */
|
||||
Token constraintName;/* Name of the constraint currently being parsed */
|
||||
yDbMask writeMask; /* Start a write transaction on these databases */
|
||||
yDbMask cookieMask; /* Bitmask of schema verified databases */
|
||||
|
130
src/where.c
130
src/where.c
@ -1142,13 +1142,17 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter(
|
||||
WhereLoop *pLoop = pLevel->pWLoop; /* The loop being coded */
|
||||
int iCur; /* Cursor for table getting the filter */
|
||||
IndexedExpr *saved_pIdxEpr; /* saved copy of Parse.pIdxEpr */
|
||||
IndexedExpr *saved_pIdxPartExpr; /* saved copy of Parse.pIdxPartExpr */
|
||||
|
||||
saved_pIdxEpr = pParse->pIdxEpr;
|
||||
saved_pIdxPartExpr = pParse->pIdxPartExpr;
|
||||
pParse->pIdxEpr = 0;
|
||||
pParse->pIdxPartExpr = 0;
|
||||
|
||||
assert( pLoop!=0 );
|
||||
assert( v!=0 );
|
||||
assert( pLoop->wsFlags & WHERE_BLOOMFILTER );
|
||||
assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 );
|
||||
|
||||
addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
|
||||
do{
|
||||
@ -1238,6 +1242,7 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter(
|
||||
}while( iLevel < pWInfo->nLevel );
|
||||
sqlite3VdbeJumpHere(v, addrOnce);
|
||||
pParse->pIdxEpr = saved_pIdxEpr;
|
||||
pParse->pIdxPartExpr = saved_pIdxPartExpr;
|
||||
}
|
||||
|
||||
|
||||
@ -3497,6 +3502,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=<expr>;
|
||||
** SELECT a, b, c FROM t1 WHERE a=<expr> AND b=?;
|
||||
**
|
||||
** The "a" in the select-list may be replaced by <expr>, iff:
|
||||
**
|
||||
** (a) <expr> is a constant expression, and
|
||||
** (b) The (a=<expr>) 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*)sqlite3DbMallocRaw(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
|
||||
@ -3712,6 +3815,11 @@ 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);
|
||||
@ -5667,20 +5775,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){
|
||||
Parse *pParse = (Parse*)pObject;
|
||||
while( pParse->pIdxEpr!=0 ){
|
||||
IndexedExpr *p = pParse->pIdxEpr;
|
||||
pParse->pIdxEpr = 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
|
||||
@ -5742,7 +5836,8 @@ static SQLITE_NOINLINE void whereAddIndexedExpr(
|
||||
#endif
|
||||
pParse->pIdxEpr = p;
|
||||
if( p->pIENext==0 ){
|
||||
sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pParse);
|
||||
void *pArg = (void*)&pParse->pIdxEpr;
|
||||
sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pArg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6354,6 +6449,11 @@ WhereInfo *sqlite3WhereBegin(
|
||||
if( pIx->bHasExpr && OptimizationEnabled(db, SQLITE_IndexedExpr) ){
|
||||
whereAddIndexedExpr(pParse, pIx, iIndexCur, pTabItem);
|
||||
}
|
||||
if( pIx->pPartIdxWhere && (pTabItem->fg.jointype & JT_RIGHT)==0 ){
|
||||
wherePartIdxExpr(
|
||||
pParse, pIx, pIx->pPartIdxWhere, 0, iIndexCur, pTabItem
|
||||
);
|
||||
}
|
||||
}
|
||||
pLevel->iIdxCur = iIndexCur;
|
||||
assert( pIx!=0 );
|
||||
|
326
test/indexA.test
Normal file
326
test/indexA.test
Normal file
@ -0,0 +1,326 @@
|
||||
# 2023 September 23
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix indexA
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a TEXT, b, c);
|
||||
CREATE INDEX i1 ON t1(b, c) WHERE a='abc';
|
||||
INSERT INTO t1 VALUES('abc', 1, 2);
|
||||
}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
SELECT * FROM t1 WHERE a='abc'
|
||||
} {abc 1 2}
|
||||
|
||||
do_eqp_test 1.2 {
|
||||
SELECT * FROM t1 WHERE a='abc'
|
||||
} {USING COVERING INDEX i1}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
CREATE INDEX i2 ON t1(b, c) WHERE a=5;
|
||||
INSERT INTO t1 VALUES(5, 4, 3);
|
||||
|
||||
SELECT a, typeof(a), b, c FROM t1 WHERE a=5;
|
||||
} {5 text 4 3}
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
CREATE TABLE t2(x);
|
||||
INSERT INTO t2 VALUES('v');
|
||||
}
|
||||
|
||||
do_execsql_test 1.5 {
|
||||
SELECT x, a, b, c FROM t2 LEFT JOIN t1 ON (a=5 AND b=x)
|
||||
} {v {} {} {}}
|
||||
|
||||
do_execsql_test 1.6 {
|
||||
SELECT x, a, b, c FROM t2 RIGHT JOIN t1 ON (t1.a=5 AND t1.b=t2.x)
|
||||
} {{} abc 1 2 {} 5 4 3}
|
||||
|
||||
do_eqp_test 1.7 {
|
||||
SELECT x, a, b, c FROM t2 RIGHT JOIN t1 ON (t1.a=5 AND t1.b=t2.x)
|
||||
} {USING INDEX i2}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE x1(a TEXT, b, c);
|
||||
INSERT INTO x1 VALUES('2', 'two', 'ii');
|
||||
INSERT INTO x1 VALUES('2.0', 'twopointoh', 'ii.0');
|
||||
|
||||
CREATE TABLE x2(a NUMERIC, b, c);
|
||||
INSERT INTO x2 VALUES('2', 'two', 'ii');
|
||||
INSERT INTO x2 VALUES('2.0', 'twopointoh', 'ii.0');
|
||||
|
||||
CREATE TABLE x3(a REAL, b, c);
|
||||
INSERT INTO x3 VALUES('2', 'two', 'ii');
|
||||
INSERT INTO x3 VALUES('2.0', 'twopointoh', 'ii.0');
|
||||
}
|
||||
|
||||
foreach {tn idx} {
|
||||
0 {
|
||||
}
|
||||
1 {
|
||||
CREATE INDEX i1 ON x1(b, c) WHERE a=2;
|
||||
CREATE INDEX i2 ON x2(b, c) WHERE a=2;
|
||||
CREATE INDEX i3 ON x3(b, c) WHERE a=2;
|
||||
}
|
||||
2 {
|
||||
CREATE INDEX i1 ON x1(b, c) WHERE a=2.0;
|
||||
CREATE INDEX i2 ON x2(b, c) WHERE a=2.0;
|
||||
CREATE INDEX i3 ON x3(b, c) WHERE a=2.0;
|
||||
}
|
||||
3 {
|
||||
CREATE INDEX i1 ON x1(b, c) WHERE a='2.0';
|
||||
CREATE INDEX i2 ON x2(b, c) WHERE a='2.0';
|
||||
CREATE INDEX i3 ON x3(b, c) WHERE a='2.0';
|
||||
}
|
||||
4 {
|
||||
CREATE INDEX i1 ON x1(b, c) WHERE a='2';
|
||||
CREATE INDEX i2 ON x2(b, c) WHERE a='2';
|
||||
CREATE INDEX i3 ON x3(b, c) WHERE a='2';
|
||||
}
|
||||
} {
|
||||
execsql { DROP INDEX IF EXISTS i1 }
|
||||
execsql { DROP INDEX IF EXISTS i2 }
|
||||
execsql { DROP INDEX IF EXISTS i3 }
|
||||
|
||||
execsql $idx
|
||||
do_execsql_test 2.1.$tn.1 {
|
||||
SELECT *, typeof(a) FROM x1 WHERE a=2
|
||||
} {2 two ii text}
|
||||
do_execsql_test 2.1.$tn.2 {
|
||||
SELECT *, typeof(a) FROM x1 WHERE a=2.0
|
||||
} {2.0 twopointoh ii.0 text}
|
||||
do_execsql_test 2.1.$tn.3 {
|
||||
SELECT *, typeof(a) FROM x1 WHERE a='2'
|
||||
} {2 two ii text}
|
||||
do_execsql_test 2.1.$tn.4 {
|
||||
SELECT *, typeof(a) FROM x1 WHERE a='2.0'
|
||||
} {2.0 twopointoh ii.0 text}
|
||||
|
||||
do_execsql_test 2.1.$tn.5 {
|
||||
SELECT *, typeof(a) FROM x2 WHERE a=2
|
||||
} {2 two ii integer 2 twopointoh ii.0 integer}
|
||||
do_execsql_test 2.1.$tn.6 {
|
||||
SELECT *, typeof(a) FROM x2 WHERE a=2.0
|
||||
} {2 two ii integer 2 twopointoh ii.0 integer}
|
||||
do_execsql_test 2.1.$tn.7 {
|
||||
SELECT *, typeof(a) FROM x2 WHERE a='2'
|
||||
} {2 two ii integer 2 twopointoh ii.0 integer}
|
||||
do_execsql_test 2.1.$tn.8 {
|
||||
SELECT *, typeof(a) FROM x2 WHERE a='2.0'
|
||||
} {2 two ii integer 2 twopointoh ii.0 integer}
|
||||
|
||||
do_execsql_test 2.1.$tn.9 {
|
||||
SELECT *, typeof(a) FROM x3 WHERE a=2
|
||||
} {2.0 two ii real 2.0 twopointoh ii.0 real}
|
||||
do_execsql_test 2.1.$tn.10 {
|
||||
SELECT *, typeof(a) FROM x3 WHERE a=2.0
|
||||
} {2.0 two ii real 2.0 twopointoh ii.0 real}
|
||||
do_execsql_test 2.1.$tn.11 {
|
||||
SELECT *, typeof(a) FROM x3 WHERE a='2'
|
||||
} {2.0 two ii real 2.0 twopointoh ii.0 real}
|
||||
do_execsql_test 2.1.$tn.12 {
|
||||
SELECT *, typeof(a) FROM x3 WHERE a='2.0'
|
||||
} {2.0 two ii real 2.0 twopointoh ii.0 real}
|
||||
|
||||
}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE x1(a TEXT, d PRIMARY KEY, b, c) WITHOUT ROWID;
|
||||
INSERT INTO x1 VALUES('2', 1, 'two', 'ii');
|
||||
INSERT INTO x1 VALUES('2.0', 2, 'twopointoh', 'ii.0');
|
||||
|
||||
CREATE TABLE x2(a NUMERIC, b, c, d PRIMARY KEY) WITHOUT ROWID;
|
||||
INSERT INTO x2 VALUES('2', 'two', 'ii', 1);
|
||||
INSERT INTO x2 VALUES('2.0', 'twopointoh', 'ii.0', 2);
|
||||
|
||||
CREATE TABLE x3(d PRIMARY KEY, a REAL, b, c) WITHOUT ROWID;
|
||||
INSERT INTO x3 VALUES(34, '2', 'two', 'ii');
|
||||
INSERT INTO x3 VALUES(35, '2.0', 'twopointoh', 'ii.0');
|
||||
}
|
||||
|
||||
foreach {tn idx} {
|
||||
0 {
|
||||
}
|
||||
1 {
|
||||
CREATE INDEX i1 ON x1(b, c) WHERE a=2;
|
||||
CREATE INDEX i2 ON x2(b, c) WHERE a=2;
|
||||
CREATE INDEX i3 ON x3(b, c) WHERE a=2;
|
||||
}
|
||||
2 {
|
||||
CREATE INDEX i1 ON x1(b, c) WHERE a=2.0;
|
||||
CREATE INDEX i2 ON x2(b, c) WHERE a=2.0;
|
||||
CREATE INDEX i3 ON x3(b, c) WHERE a=2.0;
|
||||
}
|
||||
3 {
|
||||
CREATE INDEX i1 ON x1(b, c) WHERE a='2.0';
|
||||
CREATE INDEX i2 ON x2(b, c) WHERE a='2.0';
|
||||
CREATE INDEX i3 ON x3(b, c) WHERE a='2.0';
|
||||
}
|
||||
4 {
|
||||
CREATE INDEX i1 ON x1(b, c) WHERE a='2';
|
||||
CREATE INDEX i2 ON x2(b, c) WHERE a='2';
|
||||
CREATE INDEX i3 ON x3(b, c) WHERE a='2';
|
||||
}
|
||||
} {
|
||||
execsql { DROP INDEX IF EXISTS i1 }
|
||||
execsql { DROP INDEX IF EXISTS i2 }
|
||||
execsql { DROP INDEX IF EXISTS i3 }
|
||||
|
||||
execsql $idx
|
||||
do_execsql_test 3.1.$tn.1 {
|
||||
SELECT a, b, c, typeof(a) FROM x1 WHERE a=2
|
||||
} {2 two ii text}
|
||||
do_execsql_test 3.1.$tn.2 {
|
||||
SELECT a, b, c, typeof(a) FROM x1 WHERE a=2.0
|
||||
} {2.0 twopointoh ii.0 text}
|
||||
do_execsql_test 3.1.$tn.3 {
|
||||
SELECT a, b, c, typeof(a) FROM x1 WHERE a='2'
|
||||
} {2 two ii text}
|
||||
do_execsql_test 3.1.$tn.4 {
|
||||
SELECT a, b, c, typeof(a) FROM x1 WHERE a='2.0'
|
||||
} {2.0 twopointoh ii.0 text}
|
||||
|
||||
do_execsql_test 3.1.$tn.5 {
|
||||
SELECT a, b, c, typeof(a) FROM x2 WHERE a=2
|
||||
} {2 two ii integer 2 twopointoh ii.0 integer}
|
||||
do_execsql_test 3.1.$tn.6 {
|
||||
SELECT a, b, c, typeof(a) FROM x2 WHERE a=2.0
|
||||
} {2 two ii integer 2 twopointoh ii.0 integer}
|
||||
do_execsql_test 3.1.$tn.7 {
|
||||
SELECT a, b, c, typeof(a) FROM x2 WHERE a='2'
|
||||
} {2 two ii integer 2 twopointoh ii.0 integer}
|
||||
do_execsql_test 3.1.$tn.8 {
|
||||
SELECT a, b, c, typeof(a) FROM x2 WHERE a='2.0'
|
||||
} {2 two ii integer 2 twopointoh ii.0 integer}
|
||||
|
||||
do_execsql_test 3.1.$tn.9 {
|
||||
SELECT a, b, c, typeof(a) FROM x3 WHERE a=2
|
||||
} {2.0 two ii real 2.0 twopointoh ii.0 real}
|
||||
do_execsql_test 3.1.$tn.10 {
|
||||
SELECT a, b, c, typeof(a) FROM x3 WHERE a=2.0
|
||||
} {2.0 two ii real 2.0 twopointoh ii.0 real}
|
||||
do_execsql_test 3.1.$tn.11 {
|
||||
SELECT a, b, c, typeof(a) FROM x3 WHERE a='2'
|
||||
} {2.0 two ii real 2.0 twopointoh ii.0 real}
|
||||
do_execsql_test 3.1.$tn.12 {
|
||||
SELECT a, b, c, typeof(a) FROM x3 WHERE a='2.0'
|
||||
} {2.0 two ii real 2.0 twopointoh ii.0 real}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE t2(a INTEGER, b TEXT);
|
||||
INSERT INTO t2 VALUES(1, 'two');
|
||||
INSERT INTO t2 VALUES(2, 'two');
|
||||
INSERT INTO t2 VALUES(3, 'two');
|
||||
INSERT INTO t2 VALUES(1, 'three');
|
||||
INSERT INTO t2 VALUES(2, 'three');
|
||||
INSERT INTO t2 VALUES(3, 'three');
|
||||
|
||||
CREATE INDEX t2a_two ON t2(a) WHERE b='two';
|
||||
}
|
||||
|
||||
# explain_i { SELECT sum(a), b FROM t2 WHERE b='two' }
|
||||
do_execsql_test 4.1.1 {
|
||||
SELECT sum(a), b FROM t2 WHERE b='two'
|
||||
} {6 two}
|
||||
do_eqp_test 4.1.2 {
|
||||
SELECT sum(a), b FROM t2 WHERE b='two'
|
||||
} {USING COVERING INDEX t2a_two}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMQRY KEY, b, c);
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||
CREATE TABLE t2(x INTEGER PRIMARY KEY, y INTEGER, z INTEGER);
|
||||
INSERT INTO t1 VALUES(1, 1, 1);
|
||||
INSERT INTO t1 VALUES(2, 1, 2);
|
||||
INSERT INTO t2 VALUES(1, 5, 1);
|
||||
INSERT INTO t2 VALUES(2, 5, 2);
|
||||
|
||||
CREATE INDEX t2z ON t2(z) WHERE y=5;
|
||||
}
|
||||
|
||||
do_execsql_test 6.1 {
|
||||
ANALYZE;
|
||||
UPDATE sqlite_stat1 SET stat = '50 1' WHERE idx='t2z';
|
||||
UPDATE sqlite_stat1 SET stat = '50' WHERE tbl='t2' AND idx IS NULL;
|
||||
UPDATE sqlite_stat1 SET stat = '5000' WHERE tbl='t1' AND idx IS NULL;
|
||||
ANALYZE sqlite_schema;
|
||||
}
|
||||
|
||||
do_execsql_test 6.2 {
|
||||
SELECT * FROM t1, t2 WHERE b=1 AND z=c AND y=5;
|
||||
} {
|
||||
1 1 1 1 5 1
|
||||
2 1 2 2 5 2
|
||||
}
|
||||
|
||||
do_eqp_test 6.3 {
|
||||
SELECT * FROM t1, t2 WHERE b=1 AND z=c AND y=5;
|
||||
} {BLOOM FILTER ON t2}
|
||||
|
||||
do_execsql_test 6.4 {
|
||||
SELECT * FROM t1 LEFT JOIN t2 ON (y=5) WHERE b=1 AND z IS c;
|
||||
} {
|
||||
1 1 1 1 5 1
|
||||
2 1 2 2 5 2
|
||||
}
|
||||
|
||||
do_eqp_test 6.5 {
|
||||
SELECT * FROM t1 LEFT JOIN t2 ON (y=5) WHERE b=1 AND z IS c;
|
||||
} {BLOOM FILTER ON t2}
|
||||
|
||||
do_execsql_test 6.6 {
|
||||
CREATE INDEX t2yz ON t2(y, z) WHERE y=5;
|
||||
}
|
||||
|
||||
do_execsql_test 6.7 {
|
||||
SELECT * FROM t1 LEFT JOIN t2 ON (y=5) WHERE b=1 AND z IS c;
|
||||
} {
|
||||
1 1 1 1 5 1
|
||||
2 1 2 2 5 2
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
Loading…
Reference in New Issue
Block a user