From: drh Date: Thu, 27 Jul 2017 22:16:26 +0000 (+0000) Subject: Enhance the like optimization so that it works with an ESCAPE clause. X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=86fbc2a89050c0b889386854085b48c9edc1c02f;p=thirdparty%2Fsqlite.git Enhance the like optimization so that it works with an ESCAPE clause. FossilOrigin-Name: 2495acf71017fa2ffada18824590ead593c47dabe2312701a25adc517cbf72eb --- diff --git a/manifest b/manifest index 0758b6353f..018011ffcf 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Try\sto\spush\srelevant\sWHERE\sclause\sterms\sof\sthe\souter\squery\sdown\ninto\sthe\ssubquery\sin\sorder\sto\shelp\sthe\ssubquery\srun\sfaster\sand/or\s\nuse\sless\smemory.\s\s\sThis\sis\sa\scherry-pick\sof\s[6df18e949d36]\swith\nbug\sfixes. -D 2017-07-17T19:37:23.434 +C Enhance\sthe\slike\soptimization\sso\sthat\sit\sworks\swith\san\sESCAPE\sclause. +D 2017-07-27T22:16:26.706 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 00d12636df7a5b08af09116bcd6c7bfd49b8b3b4 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -185,7 +185,7 @@ F src/delete.c 37964e6c1d73ff49cbea9ff690c9605fb15f600e F src/expr.c d09dac67d53c78880ba31d56e8ba2be3a6490553 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e0444b61bed271a76840cbe6182df93a9baa3f12 -F src/func.c 1414c24c873c48796ad45942257a179a423ba42f +F src/func.c 8028a8f79becc879268a114729263a24733ecdb610e7b7ec6ddb228bc2c13cf1 F src/global.c 4f77cadbc5427d00139ba43d0f3979804cbb700e F src/hash.c 4263fbc955f26c2e8cdc0cf214bc42435aa4e4f5 F src/hash.h c8f3c31722cf3277d03713909761e152a5b81094 @@ -307,7 +307,7 @@ F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb F src/wal.c 878c8e1a51cb2ec45c395d26b7d5cd9e1a098e4a F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804 -F src/where.c 5e7cf96bd85f5e7cee1cce001031bc0ca1af68b8cfd07c781d1d8da7796eab34 +F src/where.c 3508f80b35073fbf384f6465337cd6820f333b1e8ec2ddb1478af43c4eec1eeb F src/whereInt.h 1d1fd0b3b9b56e08f5d3583c70a2c785a3c43941 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -694,7 +694,7 @@ F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63 F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200 -F test/like.test 4f2a71d36a536233727f71995fef900756705e56 +F test/like.test 1908852fc4a7bd2ebbe358648934f0e77b62569eca3ab0692ec2f1fa5ff59d0e F test/like2.test 3b2ee13149ba4a8a60b59756f4e5d345573852da F test/like3.test 7b0525a39e4f25c4fd113de7e2e28eb712dcdedf F test/limit.test 0c99a27a87b14c646a9d583c7c89fd06c352663e @@ -1250,8 +1250,8 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P d227de8ad9cf757f30f5415ca8fccff3b5959621d09244bd1f444d3282c5b2ef adc082c1461e0237cd42653b529fbc136f5899baeff6ee32ee943d76184080c1 -R a908a996bcff610704d1aca2821ce465 -T +closed adc082c1461e0237cd42653b529fbc136f5899baeff6ee32ee943d76184080c1 +P cd6ac0784898609ddca887cfb87c042cf409acbbe4e4e84bc5bc1f8528de74fd +Q +f5d330f495d07a704e115595bbdf5422ddb68fd8191114c5a12c9c873d983f7c +R 772fa82801a81bb4d46b7048641a4ff3 U drh -Z 9d4148dec6ff72e63c10310b4326ef73 +Z c0ef846c292323e54ed21acdd6c2a147 diff --git a/manifest.uuid b/manifest.uuid index b6d2b127f4..fb813ed81c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -cd6ac0784898609ddca887cfb87c042cf409acbbe4e4e84bc5bc1f8528de74fd \ No newline at end of file +2495acf71017fa2ffada18824590ead593c47dabe2312701a25adc517cbf72eb \ No newline at end of file diff --git a/src/func.c b/src/func.c index 782a240884..eac575467e 100644 --- a/src/func.c +++ b/src/func.c @@ -1649,9 +1649,14 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){ /* ** pExpr points to an expression which implements a function. If ** it is appropriate to apply the LIKE optimization to that function -** then set aWc[0] through aWc[2] to the wildcard characters and -** return TRUE. If the function is not a LIKE-style function then -** return FALSE. +** then set aWc[0] through aWc[2] to the wildcard characters and the +** escape character and then return TRUE. If the function is not a +** LIKE-style function then return FALSE. +** +** The expression "a LIKE b ESCAPE c" is only considered a valid LIKE +** operator if c is a string literal that is exactly one byte in length. +** That one byte is stored in aWc[3]. aWc[3] is set to zero if there is +** no ESCAPE clause. ** ** *pIsNocase is set to true if uppercase and lowercase are equivalent for ** the function (default for LIKE). If the function makes the distinction @@ -1660,19 +1665,28 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){ */ int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){ FuncDef *pDef; - if( pExpr->op!=TK_FUNCTION - || !pExpr->x.pList - || pExpr->x.pList->nExpr!=2 - ){ + int nExpr; + if( pExpr->op!=TK_FUNCTION || !pExpr->x.pList ){ return 0; } assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); + nExpr = pExpr->x.pList->nExpr; pDef = sqlite3FindFunction(db, pExpr->u.zToken, sqlite3Strlen30(pExpr->u.zToken), - 2, SQLITE_UTF8, 0); + nExpr, SQLITE_UTF8, 0); if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_FUNC_LIKE)==0 ){ return 0; } + if( nExpr<3 ){ + aWc[3] = 0; + }else{ + Expr *pEscape = pExpr->x.pList->a[2].pExpr; + char *zEscape; + if( pEscape->op!=TK_STRING ) return 0; + zEscape = pEscape->u.zToken; + if( zEscape[0]==0 || zEscape[1]!=0 ) return 0; + aWc[3] = zEscape[0]; + } /* The memcpy() statement assumes that the wildcard characters are ** the first three statements in the compareInfo structure. The diff --git a/src/where.c b/src/where.c index eb5fb4a209..c1b4c861e9 100644 --- a/src/where.c +++ b/src/where.c @@ -657,7 +657,7 @@ static int isLikeOrGlob( ExprList *pList; /* List of operands to the LIKE operator */ int c; /* One character in z[] */ int cnt; /* Number of non-wildcard prefix characters */ - char wc[3]; /* Wildcard characters */ + char wc[4]; /* Wildcard characters */ sqlite3 *db = pParse->db; /* Database connection */ sqlite3_value *pVal = 0; int op; /* Opcode of pRight */ @@ -695,16 +695,43 @@ static int isLikeOrGlob( z = pRight->u.zToken; } if( z ){ + /* Count the number of prefix characters prior to the first wildcard */ cnt = 0; while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){ cnt++; + if( c==wc[3] && z[cnt]!=0 ){ + if( z[cnt++]>0xc0 ) while( (z[cnt]&0xc0)==0x80 ){ cnt++; } + } } + + /* The optimization is possible only if (1) the pattern does not begin + ** with a wildcard and if (2) the non-wildcard prefix does not end with + ** an (illegal 0xff) character. The second condition is necessary so + ** that we can increment the prefix key to find an upper bound for the + ** range search. + */ if( cnt!=0 && 255!=(u8)z[cnt-1] ){ Expr *pPrefix; + + /* A "complete" match if the pattern ends with "*" or "%" */ *pisComplete = c==wc[0] && z[cnt+1]==0; + + /* Get the pattern prefix. Remove all escapes from the prefix. */ pPrefix = sqlite3Expr(db, TK_STRING, z); - if( pPrefix ) pPrefix->u.zToken[cnt] = 0; + if( pPrefix ){ + int iFrom, iTo; + char *zNew = pPrefix->u.zToken; + zNew[cnt] = 0; + for(iFrom=iTo=0; iFrompVdbe; sqlite3VdbeSetVarmask(v, pRight->iColumn); diff --git a/test/like.test b/test/like.test index 18a01dc996..bb564a5d9e 100644 --- a/test/like.test +++ b/test/like.test @@ -949,4 +949,49 @@ do_execsql_test like-12.16 { } {/SCAN/} +# As of 2017-07-27 (3.21.0) the LIKE optimization works with ESCAPE as +# long as the ESCAPE is a single-byte literal. +# +db close +sqlite3 db :memory: +do_execsql_test like-15.100 { + CREATE TABLE t15(x TEXT COLLATE nocase, y, PRIMARY KEY(x)); + INSERT INTO t15(x,y) VALUES + ('abcde',1), ('ab%de',2), ('a_cde',3), + ('uvwxy',11),('uvwx%',12),('uvwx_',13), + ('_bcde',21),('%bcde',22), + ('abcd_',31),('abcd%',32), + ('ab%xy',41); + SELECT y FROM t15 WHERE x LIKE 'ab/%d%' ESCAPE '/'; +} {2} +do_execsql_test like-15.101 { + EXPLAIN QUERY PLAN + SELECT y FROM t15 WHERE x LIKE 'ab/%d%' ESCAPE '/'; +} {/SEARCH/} +do_execsql_test like-15.102 { + EXPLAIN QUERY PLAN + SELECT y FROM t15 WHERE x LIKE 'ab/%d%' ESCAPE '//'; +} {/SCAN/} +do_execsql_test like-15.103 { + EXPLAIN QUERY PLAN + SELECT y FROM t15 WHERE x LIKE 'ab/%d%' ESCAPE ''; +} {/SCAN/} +do_execsql_test like-15.110 { + SELECT y FROM t15 WHERE x LIKE 'abcdx%%' ESCAPE 'x'; +} {32} +do_execsql_test like-15.111 { + SELECT y FROM t15 WHERE x LIKE 'abx%%' ESCAPE 'x' ORDER BY +y +} {2 41} +do_execsql_test like-15.112 { + EXPLAIN QUERY PLAN + SELECT y FROM t15 WHERE x LIKE 'abx%%' ESCAPE 'x' ORDER BY +y +} {/SEARCH/} +do_execsql_test like-15.120 { + SELECT y FROM t15 WHERE x LIKE '/%bc%' ESCAPE '/'; +} {22} +do_execsql_test like-15.121 { + EXPLAIN QUERY PLAN + SELECT y FROM t15 WHERE x LIKE '/%bc%' ESCAPE '/'; +} {/SEARCH/} + finish_test