1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-06 15:49:35 +03:00

If a binary operator in a WHERE clause that should be performed with no affinity conversions applied to its operands (see http://www.sqlite.org/datatype3.html) is optimized by index lookup, do not apply any conversions to the key value before looking it up in the index. Fix for 93fb9f89d6.

FossilOrigin-Name: e72186f2d68d28c2e0c32894f9adb28c155b5f63
This commit is contained in:
dan
2009-08-13 19:21:16 +00:00
parent 320b3a7a3a
commit 69f8bb9c72
6 changed files with 92 additions and 47 deletions

View File

@@ -1,8 +1,5 @@
-----BEGIN PGP SIGNED MESSAGE----- C If\sa\sbinary\soperator\sin\sa\sWHERE\sclause\sthat\sshould\sbe\sperformed\swith\sno\saffinity\sconversions\sapplied\sto\sits\soperands\s(see\shttp://www.sqlite.org/datatype3.html)\sis\soptimized\sby\sindex\slookup,\sdo\snot\sapply\sany\sconversions\sto\sthe\skey\svalue\sbefore\slooking\sit\sup\sin\sthe\sindex.\sFix\sfor\s93fb9f89d6.
Hash: SHA1 D 2009-08-13T19:21:17
C Enhancements\sto\sthe\swhereB.test\sto\scheck\smore\saffinity\scorner\scases.
D 2009-08-13T18:14:32
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
F Makefile.in c606c9b502dfde3b9c3b2d23ed49f3737829693b F Makefile.in c606c9b502dfde3b9c3b2d23ed49f3737829693b
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -116,7 +113,7 @@ F src/build.c a15de7c5d020a778b641fca0b2510126843f4b30
F src/callback.c cb68b21b0d4ae7d11ae0e487933bce3323784dcf F src/callback.c cb68b21b0d4ae7d11ae0e487933bce3323784dcf
F src/complete.c 5ad5c6cd4548211867c204c41a126d73a9fbcea0 F src/complete.c 5ad5c6cd4548211867c204c41a126d73a9fbcea0
F src/date.c ab5f7137656652a48434d64f96bdcdc823bb23b3 F src/date.c ab5f7137656652a48434d64f96bdcdc823bb23b3
F src/delete.c f1502d3c210f80eebef475a04891e8ea80099553 F src/delete.c dcf07632d8ca3d4086df8b65ea907a47278e6382
F src/expr.c d069ba1e060f296ea4f18fb85198fafefd00b22f F src/expr.c d069ba1e060f296ea4f18fb85198fafefd00b22f
F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff
F src/func.c 9856373f5315f6b8690d7f07f7191aa9f279ca87 F src/func.c 9856373f5315f6b8690d7f07f7191aa9f279ca87
@@ -124,7 +121,7 @@ F src/global.c 448419c44ce0701104c2121b0e06919b44514c0c
F src/hash.c ebcaa921ffd9d86f7ea5ae16a0a29d1c871130a7 F src/hash.c ebcaa921ffd9d86f7ea5ae16a0a29d1c871130a7
F src/hash.h 35b216c13343d0b4f87d9f21969ac55ad72174e1 F src/hash.h 35b216c13343d0b4f87d9f21969ac55ad72174e1
F src/hwtime.h 4a1d45f4cae1f402ea19686acf24acf4f0cb53cb F src/hwtime.h 4a1d45f4cae1f402ea19686acf24acf4f0cb53cb
F src/insert.c a4bbd811a15f8b24a311753da947d61368685db1 F src/insert.c 95625f99f377a9ef264c289407173b722c7af6e8
F src/journal.c e00df0c0da8413ab6e1bb7d7cab5665d4a9000d0 F src/journal.c e00df0c0da8413ab6e1bb7d7cab5665d4a9000d0
F src/legacy.c 303b4ffcf1ae652fcf5ef635846c563c254564f6 F src/legacy.c 303b4ffcf1ae652fcf5ef635846c563c254564f6
F src/lempar.c 0c4d1ab0a5ef2b0381eb81a732c54f68f27a574d F src/lempar.c 0c4d1ab0a5ef2b0381eb81a732c54f68f27a574d
@@ -166,7 +163,7 @@ F src/select.c 67b0778c9585905c8aa75aaa469e76ef3c1d315a
F src/shell.c db2643650b9268df89a4bedca3f1c6d9e786f1bb F src/shell.c db2643650b9268df89a4bedca3f1c6d9e786f1bb
F src/sqlite.h.in eb42257503a48f6f12ff0b23a81067ba9b5ac1eb F src/sqlite.h.in eb42257503a48f6f12ff0b23a81067ba9b5ac1eb
F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17 F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17
F src/sqliteInt.h 6337542c0eb0e6521991b29f59c8bcbdfda222e7 F src/sqliteInt.h 6a90791138ba3447572d184d0798c24f3cbbec98
F src/sqliteLimit.h ffe93f5a0c4e7bd13e70cd7bf84cfb5c3465f45d F src/sqliteLimit.h ffe93f5a0c4e7bd13e70cd7bf84cfb5c3465f45d
F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76 F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76
F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d
@@ -216,7 +213,7 @@ F src/vdbeblob.c a3f3e0e877fc64ea50165eec2855f5ada4477611
F src/vdbemem.c 364cfce843926224f917ab89ee476be958c864ed F src/vdbemem.c 364cfce843926224f917ab89ee476be958c864ed
F src/vtab.c aedd76e8670d5a5379f93804398d3ba960125547 F src/vtab.c aedd76e8670d5a5379f93804398d3ba960125547
F src/walker.c 1edca756275f158b80f20eb6f104c8d3fcc96a04 F src/walker.c 1edca756275f158b80f20eb6f104c8d3fcc96a04
F src/where.c 53adef2c7b8bc888755cf41fb3449aedb36a429c F src/where.c 7573120c1f2fe6d4c246f138f1e30fbcda3db241
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
F test/all.test 14165b3e32715b700b5f0cbf8f6e3833dda0be45 F test/all.test 14165b3e32715b700b5f0cbf8f6e3833dda0be45
@@ -746,14 +743,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/vdbe-compress.tcl 672f81d693a03f80f5ae60bfefacd8a349e76746 F tool/vdbe-compress.tcl 672f81d693a03f80f5ae60bfefacd8a349e76746
P 149ec24e61437fac2b0dd6239276d3aa543c56cb P 1048459824746307c9e4296cbc21716bf8b5449d
R 1ea36fc2c7e0c32d5b2483eec1ce1a06 R 9cf12527faa6d7b7304f1183ac06fe71
U drh U dan
Z cdd6f1e89b3618add099c3116553394a Z 0b088089cd7635bacda9499fff4849b7
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
iD8DBQFKhFgLoxKgR168RlERAgjMAJ0e/xokwSjX2hsY3q8mvlUGu+555wCfZL+Z
ThH1uVO4nfptbHxSFohZy1U=
=PHPU
-----END PGP SIGNATURE-----

View File

@@ -1 +1 @@
1048459824746307c9e4296cbc21716bf8b5449d e72186f2d68d28c2e0c32894f9adb28c155b5f63

View File

@@ -622,7 +622,7 @@ int sqlite3GenerateIndexKey(
} }
if( doMakeRec ){ if( doMakeRec ){
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol+1, regOut); sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol+1, regOut);
sqlite3IndexAffinityStr(v, pIdx); sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0);
sqlite3ExprCacheAffinityChange(pParse, regBase, nCol+1); sqlite3ExprCacheAffinityChange(pParse, regBase, nCol+1);
} }
sqlite3ReleaseTempRange(pParse, regBase, nCol+1); sqlite3ReleaseTempRange(pParse, regBase, nCol+1);

View File

@@ -37,9 +37,9 @@ void sqlite3OpenTable(
} }
/* /*
** Set P4 of the most recently inserted opcode to a column affinity ** Return a pointer to the column affinity string associated with index
** string for index pIdx. A column affinity string has one character ** pIdx. A column affinity string has one character for each column in
** for each column in the table, according to the affinity of the column: ** the table, according to the affinity of the column:
** **
** Character Column affinity ** Character Column affinity
** ------------------------------ ** ------------------------------
@@ -51,8 +51,12 @@ void sqlite3OpenTable(
** **
** An extra 'b' is appended to the end of the string to cover the ** An extra 'b' is appended to the end of the string to cover the
** rowid that appears as the last column in every index. ** rowid that appears as the last column in every index.
**
** Memory for the buffer containing the column index affinity string
** is managed along with the rest of the Index structure. It will be
** released when sqlite3DeleteIndex() is called.
*/ */
void sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){ const char *sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){
if( !pIdx->zColAff ){ if( !pIdx->zColAff ){
/* The first time a column affinity string for a particular index is /* The first time a column affinity string for a particular index is
** required, it is allocated and populated here. It is then stored as ** required, it is allocated and populated here. It is then stored as
@@ -68,7 +72,7 @@ void sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){
pIdx->zColAff = (char *)sqlite3Malloc(pIdx->nColumn+2); pIdx->zColAff = (char *)sqlite3Malloc(pIdx->nColumn+2);
if( !pIdx->zColAff ){ if( !pIdx->zColAff ){
db->mallocFailed = 1; db->mallocFailed = 1;
return; return 0;
} }
for(n=0; n<pIdx->nColumn; n++){ for(n=0; n<pIdx->nColumn; n++){
pIdx->zColAff[n] = pTab->aCol[pIdx->aiColumn[n]].affinity; pIdx->zColAff[n] = pTab->aCol[pIdx->aiColumn[n]].affinity;
@@ -77,7 +81,7 @@ void sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){
pIdx->zColAff[n] = 0; pIdx->zColAff[n] = 0;
} }
sqlite3VdbeChangeP4(v, -1, pIdx->zColAff, 0); return pIdx->zColAff;
} }
/* /*
@@ -1298,7 +1302,7 @@ void sqlite3GenerateConstraintChecks(
} }
sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i); sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn+1, aRegIdx[iCur]); sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn+1, aRegIdx[iCur]);
sqlite3IndexAffinityStr(v, pIdx); sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0);
sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn+1); sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn+1);
/* Find out what action to take in case there is an indexing conflict */ /* Find out what action to take in case there is an indexing conflict */

View File

@@ -2755,7 +2755,7 @@ int sqlite3VarintLen(u64 v);
#define putVarint sqlite3PutVarint #define putVarint sqlite3PutVarint
void sqlite3IndexAffinityStr(Vdbe *, Index *); const char *sqlite3IndexAffinityStr(Vdbe *, Index *);
void sqlite3TableAffinityStr(Vdbe *, Table *); void sqlite3TableAffinityStr(Vdbe *, Table *);
char sqlite3CompareAffinity(Expr *pExpr, char aff2); char sqlite3CompareAffinity(Expr *pExpr, char aff2);
int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity); int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity);

View File

@@ -2275,18 +2275,20 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
} }
/* /*
** Apply the affinities associated with the first n columns of index ** Code an OP_Affinity opcode to apply the column affinity string zAff
** pIdx to the values in the n registers starting at base. ** to the n registers starting at base.
**
** Buffer zAff was allocated using sqlite3DbMalloc(). It is the
** responsibility of this function to arrange for it to be eventually
** freed using sqlite3DbFree().
*/ */
static void codeApplyAffinity(Parse *pParse, int base, int n, Index *pIdx){ static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){
if( n>0 ){
Vdbe *v = pParse->pVdbe; Vdbe *v = pParse->pVdbe;
assert( v!=0 ); assert( v!=0 );
sqlite3VdbeAddOp2(v, OP_Affinity, base, n); sqlite3VdbeAddOp2(v, OP_Affinity, base, n);
sqlite3IndexAffinityStr(v, pIdx); sqlite3VdbeChangeP4(v, -1, zAff, P4_DYNAMIC);
sqlite3ExprCacheAffinityChange(pParse, base, n); sqlite3ExprCacheAffinityChange(pParse, base, n);
} }
}
/* /*
@@ -2376,13 +2378,29 @@ static int codeEqualityTerm(
** key value of the loop. If one or more IN operators appear, then ** key value of the loop. If one or more IN operators appear, then
** this routine allocates an additional nEq memory cells for internal ** this routine allocates an additional nEq memory cells for internal
** use. ** use.
**
** Before returning, *pzAff is set to point to a buffer containing a
** copy of the column affinity string of the index allocated using
** sqlite3DbMalloc(). Except, entries in the copy of the string associated
** with equality constraints that use NONE affinity are set to
** SQLITE_AFF_NONE. This is to deal with SQL such as the following:
**
** CREATE TABLE t1(a TEXT PRIMARY KEY, b);
** SELECT ... FROM t1 AS t2, t1 WHERE t1.a = t2.b;
**
** In the example above, the index on t1(a) has TEXT affinity. But since
** the right hand side of the equality constraint (t2.b) has NONE affinity,
** no conversion should be attempted before using a t2.b value as part of
** a key to search the index. Hence the first byte in the returned affinity
** string in this example would be set to SQLITE_AFF_NONE.
*/ */
static int codeAllEqualityTerms( static int codeAllEqualityTerms(
Parse *pParse, /* Parsing context */ Parse *pParse, /* Parsing context */
WhereLevel *pLevel, /* Which nested loop of the FROM we are coding */ WhereLevel *pLevel, /* Which nested loop of the FROM we are coding */
WhereClause *pWC, /* The WHERE clause */ WhereClause *pWC, /* The WHERE clause */
Bitmask notReady, /* Which parts of FROM have not yet been coded */ Bitmask notReady, /* Which parts of FROM have not yet been coded */
int nExtraReg /* Number of extra registers to allocate */ int nExtraReg, /* Number of extra registers to allocate */
char **pzAff /* OUT: Set to point to affinity string */
){ ){
int nEq = pLevel->plan.nEq; /* The number of == or IN constraints to code */ int nEq = pLevel->plan.nEq; /* The number of == or IN constraints to code */
Vdbe *v = pParse->pVdbe; /* The vm under construction */ Vdbe *v = pParse->pVdbe; /* The vm under construction */
@@ -2392,6 +2410,7 @@ static int codeAllEqualityTerms(
int j; /* Loop counter */ int j; /* Loop counter */
int regBase; /* Base register */ int regBase; /* Base register */
int nReg; /* Number of registers to allocate */ int nReg; /* Number of registers to allocate */
char *zAff; /* Affinity string to return */
/* This module is only called on query plans that use an index. */ /* This module is only called on query plans that use an index. */
assert( pLevel->plan.wsFlags & WHERE_INDEXED ); assert( pLevel->plan.wsFlags & WHERE_INDEXED );
@@ -2403,6 +2422,11 @@ static int codeAllEqualityTerms(
nReg = pLevel->plan.nEq + nExtraReg; nReg = pLevel->plan.nEq + nExtraReg;
pParse->nMem += nReg; pParse->nMem += nReg;
zAff = sqlite3DbStrDup(pParse->db, sqlite3IndexAffinityStr(v, pIdx));
if( !zAff ){
pParse->db->mallocFailed = 1;
}
/* Evaluate the equality constraints /* Evaluate the equality constraints
*/ */
assert( pIdx->nColumn>=nEq ); assert( pIdx->nColumn>=nEq );
@@ -2425,8 +2449,14 @@ static int codeAllEqualityTerms(
testcase( pTerm->eOperator & WO_IN ); testcase( pTerm->eOperator & WO_IN );
if( (pTerm->eOperator & (WO_ISNULL|WO_IN))==0 ){ if( (pTerm->eOperator & (WO_ISNULL|WO_IN))==0 ){
sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk); sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk);
if( zAff
&& sqlite3CompareAffinity(pTerm->pExpr->pRight, zAff[j])==SQLITE_AFF_NONE
){
zAff[j] = SQLITE_AFF_NONE;
} }
} }
}
*pzAff = zAff;
return regBase; return regBase;
} }
@@ -2682,6 +2712,7 @@ static Bitmask codeOneLoopStart(
int iIdxCur; /* The VDBE cursor for the index */ int iIdxCur; /* The VDBE cursor for the index */
int nExtraReg = 0; /* Number of extra registers needed */ int nExtraReg = 0; /* Number of extra registers needed */
int op; /* Instruction opcode */ int op; /* Instruction opcode */
char *zAff;
pIdx = pLevel->plan.u.pIdx; pIdx = pLevel->plan.u.pIdx;
iIdxCur = pLevel->iIdxCur; iIdxCur = pLevel->iIdxCur;
@@ -2721,10 +2752,11 @@ static Bitmask codeOneLoopStart(
** and store the values of those terms in an array of registers ** and store the values of those terms in an array of registers
** starting at regBase. ** starting at regBase.
*/ */
regBase = codeAllEqualityTerms(pParse, pLevel, pWC, notReady, nExtraReg); regBase = codeAllEqualityTerms(
pParse, pLevel, pWC, notReady, nExtraReg, &zAff
);
addrNxt = pLevel->addrNxt; addrNxt = pLevel->addrNxt;
/* If we are doing a reverse order scan on an ascending index, or /* If we are doing a reverse order scan on an ascending index, or
** a forward order scan on a descending index, interchange the ** a forward order scan on a descending index, interchange the
** start and end terms (pRangeStart and pRangeEnd). ** start and end terms (pRangeStart and pRangeEnd).
@@ -2744,8 +2776,17 @@ static Bitmask codeOneLoopStart(
/* Seek the index cursor to the start of the range. */ /* Seek the index cursor to the start of the range. */
nConstraint = nEq; nConstraint = nEq;
if( pRangeStart ){ if( pRangeStart ){
sqlite3ExprCode(pParse, pRangeStart->pExpr->pRight, regBase+nEq); Expr *pRight = pRangeStart->pExpr->pRight;
sqlite3ExprCode(pParse, pRight, regBase+nEq);
sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt); sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt);
if( zAff
&& sqlite3CompareAffinity(pRight, zAff[nConstraint])==SQLITE_AFF_NONE
){
/* Since the comparison is to be performed with no conversions applied
** to the operands, set the affinity to apply to pRight to
** SQLITE_AFF_NONE. */
zAff[nConstraint] = SQLITE_AFF_NONE;
}
nConstraint++; nConstraint++;
}else if( isMinQuery ){ }else if( isMinQuery ){
sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq); sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
@@ -2753,7 +2794,7 @@ static Bitmask codeOneLoopStart(
startEq = 0; startEq = 0;
start_constraints = 1; start_constraints = 1;
} }
codeApplyAffinity(pParse, regBase, nConstraint, pIdx); codeApplyAffinity(pParse, regBase, nConstraint, zAff);
op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev]; op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev];
assert( op!=0 ); assert( op!=0 );
testcase( op==OP_Rewind ); testcase( op==OP_Rewind );
@@ -2770,10 +2811,20 @@ static Bitmask codeOneLoopStart(
*/ */
nConstraint = nEq; nConstraint = nEq;
if( pRangeEnd ){ if( pRangeEnd ){
Expr *pRight = pRangeEnd->pExpr->pRight;
sqlite3ExprCacheRemove(pParse, regBase+nEq); sqlite3ExprCacheRemove(pParse, regBase+nEq);
sqlite3ExprCode(pParse, pRangeEnd->pExpr->pRight, regBase+nEq); sqlite3ExprCode(pParse, pRight, regBase+nEq);
sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt); sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt);
codeApplyAffinity(pParse, regBase, nEq+1, pIdx); zAff = sqlite3DbStrDup(pParse->db, zAff);
if( zAff
&& sqlite3CompareAffinity(pRight, zAff[nConstraint])==SQLITE_AFF_NONE
){
/* Since the comparison is to be performed with no conversions applied
** to the operands, set the affinity to apply to pRight to
** SQLITE_AFF_NONE. */
zAff[nConstraint] = SQLITE_AFF_NONE;
}
codeApplyAffinity(pParse, regBase, nEq+1, zAff);
nConstraint++; nConstraint++;
} }