From: dan Date: Thu, 13 Aug 2009 19:21:16 +0000 (+0000) Subject: If a binary operator in a WHERE clause that should be performed with no affinity... X-Git-Tag: fts3-refactor~270 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=69f8bb9c72542480122649b16efade413862eac8;p=thirdparty%2Fsqlite.git 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 --- diff --git a/manifest b/manifest index c044cfb58f..b64decd313 100644 --- a/manifest +++ b/manifest @@ -1,8 +1,5 @@ ------BEGIN PGP SIGNED MESSAGE----- -Hash: SHA1 - -C Enhancements\sto\sthe\swhereB.test\sto\scheck\smore\saffinity\scorner\scases. -D 2009-08-13T18:14:32 +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. +D 2009-08-13T19:21:17 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in c606c9b502dfde3b9c3b2d23ed49f3737829693b F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -116,7 +113,7 @@ F src/build.c a15de7c5d020a778b641fca0b2510126843f4b30 F src/callback.c cb68b21b0d4ae7d11ae0e487933bce3323784dcf F src/complete.c 5ad5c6cd4548211867c204c41a126d73a9fbcea0 F src/date.c ab5f7137656652a48434d64f96bdcdc823bb23b3 -F src/delete.c f1502d3c210f80eebef475a04891e8ea80099553 +F src/delete.c dcf07632d8ca3d4086df8b65ea907a47278e6382 F src/expr.c d069ba1e060f296ea4f18fb85198fafefd00b22f F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff F src/func.c 9856373f5315f6b8690d7f07f7191aa9f279ca87 @@ -124,7 +121,7 @@ F src/global.c 448419c44ce0701104c2121b0e06919b44514c0c F src/hash.c ebcaa921ffd9d86f7ea5ae16a0a29d1c871130a7 F src/hash.h 35b216c13343d0b4f87d9f21969ac55ad72174e1 F src/hwtime.h 4a1d45f4cae1f402ea19686acf24acf4f0cb53cb -F src/insert.c a4bbd811a15f8b24a311753da947d61368685db1 +F src/insert.c 95625f99f377a9ef264c289407173b722c7af6e8 F src/journal.c e00df0c0da8413ab6e1bb7d7cab5665d4a9000d0 F src/legacy.c 303b4ffcf1ae652fcf5ef635846c563c254564f6 F src/lempar.c 0c4d1ab0a5ef2b0381eb81a732c54f68f27a574d @@ -166,7 +163,7 @@ F src/select.c 67b0778c9585905c8aa75aaa469e76ef3c1d315a F src/shell.c db2643650b9268df89a4bedca3f1c6d9e786f1bb F src/sqlite.h.in eb42257503a48f6f12ff0b23a81067ba9b5ac1eb F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17 -F src/sqliteInt.h 6337542c0eb0e6521991b29f59c8bcbdfda222e7 +F src/sqliteInt.h 6a90791138ba3447572d184d0798c24f3cbbec98 F src/sqliteLimit.h ffe93f5a0c4e7bd13e70cd7bf84cfb5c3465f45d F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76 F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d @@ -216,7 +213,7 @@ F src/vdbeblob.c a3f3e0e877fc64ea50165eec2855f5ada4477611 F src/vdbemem.c 364cfce843926224f917ab89ee476be958c864ed F src/vtab.c aedd76e8670d5a5379f93804398d3ba960125547 F src/walker.c 1edca756275f158b80f20eb6f104c8d3fcc96a04 -F src/where.c 53adef2c7b8bc888755cf41fb3449aedb36a429c +F src/where.c 7573120c1f2fe6d4c246f138f1e30fbcda3db241 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/all.test 14165b3e32715b700b5f0cbf8f6e3833dda0be45 @@ -746,14 +743,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl 672f81d693a03f80f5ae60bfefacd8a349e76746 -P 149ec24e61437fac2b0dd6239276d3aa543c56cb -R 1ea36fc2c7e0c32d5b2483eec1ce1a06 -U drh -Z cdd6f1e89b3618add099c3116553394a ------BEGIN PGP SIGNATURE----- -Version: GnuPG v1.4.6 (GNU/Linux) - -iD8DBQFKhFgLoxKgR168RlERAgjMAJ0e/xokwSjX2hsY3q8mvlUGu+555wCfZL+Z -ThH1uVO4nfptbHxSFohZy1U= -=PHPU ------END PGP SIGNATURE----- +P 1048459824746307c9e4296cbc21716bf8b5449d +R 9cf12527faa6d7b7304f1183ac06fe71 +U dan +Z 0b088089cd7635bacda9499fff4849b7 diff --git a/manifest.uuid b/manifest.uuid index 74b1ddbe6f..856939186d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1048459824746307c9e4296cbc21716bf8b5449d \ No newline at end of file +e72186f2d68d28c2e0c32894f9adb28c155b5f63 \ No newline at end of file diff --git a/src/delete.c b/src/delete.c index f2d632f9b7..abf7e0c745 100644 --- a/src/delete.c +++ b/src/delete.c @@ -622,7 +622,7 @@ int sqlite3GenerateIndexKey( } if( doMakeRec ){ sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol+1, regOut); - sqlite3IndexAffinityStr(v, pIdx); + sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0); sqlite3ExprCacheAffinityChange(pParse, regBase, nCol+1); } sqlite3ReleaseTempRange(pParse, regBase, nCol+1); diff --git a/src/insert.c b/src/insert.c index 43fbe58515..728c06ed92 100644 --- a/src/insert.c +++ b/src/insert.c @@ -37,9 +37,9 @@ void sqlite3OpenTable( } /* -** Set P4 of the most recently inserted opcode to a column affinity -** string for index pIdx. A column affinity string has one character -** for each column in the table, according to the affinity of the column: +** Return a pointer to the column affinity string associated with index +** pIdx. A column affinity string has one character for each column in +** the table, according to the affinity of the column: ** ** Character Column affinity ** ------------------------------ @@ -51,8 +51,12 @@ void sqlite3OpenTable( ** ** An extra 'b' is appended to the end of the string to cover the ** 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 ){ /* The first time a column affinity string for a particular index is ** 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); if( !pIdx->zColAff ){ db->mallocFailed = 1; - return; + return 0; } for(n=0; nnColumn; n++){ pIdx->zColAff[n] = pTab->aCol[pIdx->aiColumn[n]].affinity; @@ -77,7 +81,7 @@ void sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){ 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); 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); /* Find out what action to take in case there is an indexing conflict */ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 8a3df484e0..a737a12c73 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2755,7 +2755,7 @@ int sqlite3VarintLen(u64 v); #define putVarint sqlite3PutVarint -void sqlite3IndexAffinityStr(Vdbe *, Index *); +const char *sqlite3IndexAffinityStr(Vdbe *, Index *); void sqlite3TableAffinityStr(Vdbe *, Table *); char sqlite3CompareAffinity(Expr *pExpr, char aff2); int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity); diff --git a/src/where.c b/src/where.c index 797559c86f..50a08c952a 100644 --- a/src/where.c +++ b/src/where.c @@ -2275,17 +2275,19 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){ } /* -** Apply the affinities associated with the first n columns of index -** pIdx to the values in the n registers starting at base. +** Code an OP_Affinity opcode to apply the column affinity string zAff +** 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){ - if( n>0 ){ - Vdbe *v = pParse->pVdbe; - assert( v!=0 ); - sqlite3VdbeAddOp2(v, OP_Affinity, base, n); - sqlite3IndexAffinityStr(v, pIdx); - sqlite3ExprCacheAffinityChange(pParse, base, n); - } +static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){ + Vdbe *v = pParse->pVdbe; + assert( v!=0 ); + sqlite3VdbeAddOp2(v, OP_Affinity, base, n); + sqlite3VdbeChangeP4(v, -1, zAff, P4_DYNAMIC); + sqlite3ExprCacheAffinityChange(pParse, base, n); } @@ -2376,13 +2378,29 @@ static int codeEqualityTerm( ** key value of the loop. If one or more IN operators appear, then ** this routine allocates an additional nEq memory cells for internal ** 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( Parse *pParse, /* Parsing context */ WhereLevel *pLevel, /* Which nested loop of the FROM we are coding */ WhereClause *pWC, /* The WHERE clause */ 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 */ Vdbe *v = pParse->pVdbe; /* The vm under construction */ @@ -2392,6 +2410,7 @@ static int codeAllEqualityTerms( int j; /* Loop counter */ int regBase; /* Base register */ 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. */ assert( pLevel->plan.wsFlags & WHERE_INDEXED ); @@ -2403,6 +2422,11 @@ static int codeAllEqualityTerms( nReg = pLevel->plan.nEq + nExtraReg; pParse->nMem += nReg; + zAff = sqlite3DbStrDup(pParse->db, sqlite3IndexAffinityStr(v, pIdx)); + if( !zAff ){ + pParse->db->mallocFailed = 1; + } + /* Evaluate the equality constraints */ assert( pIdx->nColumn>=nEq ); @@ -2425,8 +2449,14 @@ static int codeAllEqualityTerms( testcase( pTerm->eOperator & WO_IN ); if( (pTerm->eOperator & (WO_ISNULL|WO_IN))==0 ){ 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; } @@ -2682,6 +2712,7 @@ static Bitmask codeOneLoopStart( int iIdxCur; /* The VDBE cursor for the index */ int nExtraReg = 0; /* Number of extra registers needed */ int op; /* Instruction opcode */ + char *zAff; pIdx = pLevel->plan.u.pIdx; iIdxCur = pLevel->iIdxCur; @@ -2721,10 +2752,11 @@ static Bitmask codeOneLoopStart( ** and store the values of those terms in an array of registers ** starting at regBase. */ - regBase = codeAllEqualityTerms(pParse, pLevel, pWC, notReady, nExtraReg); + regBase = codeAllEqualityTerms( + pParse, pLevel, pWC, notReady, nExtraReg, &zAff + ); addrNxt = pLevel->addrNxt; - /* If we are doing a reverse order scan on an ascending index, or ** a forward order scan on a descending index, interchange the ** start and end terms (pRangeStart and pRangeEnd). @@ -2744,8 +2776,17 @@ static Bitmask codeOneLoopStart( /* Seek the index cursor to the start of the range. */ nConstraint = nEq; 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); + 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++; }else if( isMinQuery ){ sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq); @@ -2753,7 +2794,7 @@ static Bitmask codeOneLoopStart( startEq = 0; start_constraints = 1; } - codeApplyAffinity(pParse, regBase, nConstraint, pIdx); + codeApplyAffinity(pParse, regBase, nConstraint, zAff); op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev]; assert( op!=0 ); testcase( op==OP_Rewind ); @@ -2770,10 +2811,20 @@ static Bitmask codeOneLoopStart( */ nConstraint = nEq; if( pRangeEnd ){ + Expr *pRight = pRangeEnd->pExpr->pRight; sqlite3ExprCacheRemove(pParse, regBase+nEq); - sqlite3ExprCode(pParse, pRangeEnd->pExpr->pRight, regBase+nEq); + sqlite3ExprCode(pParse, pRight, regBase+nEq); 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++; }