]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Enhance the like optimization so that it works with an ESCAPE clause.
authordrh <drh@noemail.net>
Thu, 27 Jul 2017 22:16:26 +0000 (22:16 +0000)
committerdrh <drh@noemail.net>
Thu, 27 Jul 2017 22:16:26 +0000 (22:16 +0000)
FossilOrigin-Name: 2495acf71017fa2ffada18824590ead593c47dabe2312701a25adc517cbf72eb

manifest
manifest.uuid
src/func.c
src/where.c
test/like.test

index 0758b6353f7331943ff29ce9db9ed08fcc952d46..018011ffcfff8585be30fbf2128de5dea8a1ebfd 100644 (file)
--- 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
index b6d2b127f4c82c949ca03b5135a1ae7274a5dd60..fb813ed81c4e1189263c5562387499b461f4a55a 100644 (file)
@@ -1 +1 @@
-cd6ac0784898609ddca887cfb87c042cf409acbbe4e4e84bc5bc1f8528de74fd
\ No newline at end of file
+2495acf71017fa2ffada18824590ead593c47dabe2312701a25adc517cbf72eb
\ No newline at end of file
index 782a2408840f16e968bc316cdccea9500bf91176..eac575467e1ec637909d85aa2d1be57ab80605f3 100644 (file)
@@ -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
index eb5fb4a2097e2a0529ba260f63683a5e3323c306..c1b4c861e916fa4b8df7bb4af849c54b923a1d1e 100644 (file)
@@ -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; iFrom<cnt; iFrom++){
+          if( zNew[iFrom]==wc[3] ) iFrom++;
+          zNew[iTo++] = zNew[iFrom];
+        }
+        zNew[iTo] = 0;
+      }
       *ppPrefix = pPrefix;
+
+      /* If the RHS pattern is a bound parameter, make arrangements to
+      ** reprepare the statement when that parameter is rebound */
       if( op==TK_VARIABLE ){
         Vdbe *v = pParse->pVdbe;
         sqlite3VdbeSetVarmask(v, pRight->iColumn);
index 18a01dc9967e50108d14e6c6cdd703b611976345..bb564a5d9ed81108c3cb67450abe16c1fe954287 100644 (file)
@@ -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