]> 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 20:24:29 +0000 (20:24 +0000)
committerdrh <drh@noemail.net>
Thu, 27 Jul 2017 20:24:29 +0000 (20:24 +0000)
FossilOrigin-Name: f5d330f495d07a704e115595bbdf5422ddb68fd8191114c5a12c9c873d983f7c

manifest
manifest.uuid
src/func.c
src/wherecode.c
src/whereexpr.c
test/like.test

index 371b463ff1262792089a2e08cf7899b321c261c9..80a229dc9ab72a05be3ebfa07be7d700f8176a50 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Increase\sthe\sversion\snumber\sto\s3.21.0\sin\santicipation\sfor\schanges\sto\sgo\ninto\sthe\snext\srelease.
-D 2017-07-27T19:59:37.092
+C Enhance\sthe\slike\soptimization\sso\sthat\sit\sworks\swith\san\sESCAPE\sclause.
+D 2017-07-27T20:24:29.258
 F Makefile.in d9873c9925917cca9990ee24be17eb9613a668012c85a343aef7e5536ae266e8
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc 02b469e9dcd5b7ee63fc1fb05babc174260ee4cfa4e0ef2e48c3c6801567a016
@@ -408,7 +408,7 @@ F src/delete.c 939bd15e6b54b82b951e1c0ffc2ff2b4ab579196780a1f6d394e47bd6f799b6c
 F src/expr.c fdb2fc465cabbf372fecad1fc2b291758bec74150b4db0fb945332e09df28a0e
 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
 F src/fkey.c 5ff2c895fe087756d8085dc1a9bc229b5670e2a65c3929dd87c71e43649af333
-F src/func.c e2854b19386b93ad6b498a3f3b7d6baa98ec14cfe84530fb12fce4414263d871
+F src/func.c ed8888ae80b39f5a5d403954e4a05e0a38303523dff8143161439c142d31dec1
 F src/global.c 8a6ab6b4d91effb96ffa81b39f0d70c862abca157f8aaa194600a4a8b7923344
 F src/hash.c a12580e143f10301ed5166ea4964ae2853d3905a511d4e0c44497245c7ce1f7a
 F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4
@@ -536,8 +536,8 @@ F src/wal.h 06b2a0b599cc0f53ea97f497cf8c6b758c999f71
 F src/walker.c a7ca64ce08a83a20d32186fbe06bca9234e348cfcf07959ee322fdc3e8a6173a
 F src/where.c cbe8ddffbcec7ce86f7a800fe8fd10aee412c76c87e0dd3732a1682e68d74cd9
 F src/whereInt.h 93bb90b77d39901eda31b44d8e90da1351193ccfe96876f89b58a93a33b84c3d
-F src/wherecode.c c0c4c31573486cd14bb2cbfc63e41eda591609e5190416261999f211bf97abc1
-F src/whereexpr.c bf983d2d33e325cd63a36c40b8de289fd3d7b4d9f2db9052fb8f59f7161a34a0
+F src/wherecode.c e7be3b7f4c11908500cdf02b299d190d3742659533f58e0f4047962fdb5a48da
+F src/whereexpr.c 35d8b33afeedb8009fcd7211e1c848587578b345da93dbed69edc88dffe64c35
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
 F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
@@ -978,7 +978,7 @@ F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff
 F test/kvtest.c d2b8cfc91047ebf6cac4f3a04f19c3a864e4ecfd683bbb65c395df450b8dc79c
 F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63
 F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200
-F test/like.test 3d26ae14d7042a0e96f7b0b9e9ad2c8ca6ed122772439c6b1c691fe167e15a37
+F test/like.test 67d7431c9b664254febce9e90fd2f47c7c75c8b38444e2a50ef9ec2776b84ca8
 F test/like2.test 3b2ee13149ba4a8a60b59756f4e5d345573852da
 F test/like3.test 3608a2042b6f922f900fbfd5d3ce4e7eca57f7c4
 F test/limit.test 0c99a27a87b14c646a9d583c7c89fd06c352663e
@@ -1637,7 +1637,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P c63903a4c5d52a490e3f26707aa85fb54d4e2e8a3ce31ca26a9c615fe7a51e97
-R 5a42c475618e9f2714e570b08be1a322
+P 0645f25c79c1b2af1fd3a02b44090329d456e373d91f6c284b8fbcb929e03a5d
+R a1b40de685cc5a3c342ce6c1e88c465d
 U drh
-Z 1ea8d09db2863a7a57318d9127cb53ba
+Z 1c2383ec54a238a2bc71d05955866da3
index 410ad546a23a5ca59ef51c9329e9ffb6757126eb..fd98f738db4093683ba32cf6f9d8d4060ded1b84 100644 (file)
@@ -1 +1 @@
-0645f25c79c1b2af1fd3a02b44090329d456e373d91f6c284b8fbcb929e03a5d
\ No newline at end of file
+f5d330f495d07a704e115595bbdf5422ddb68fd8191114c5a12c9c873d983f7c
\ No newline at end of file
index fd5458ad3d454f54cb72f180bfd401e88365fef0..2bb9a91de678f55fa991b522a917df0879dfb309 100644 (file)
@@ -1706,9 +1706,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
@@ -1717,17 +1722,26 @@ 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) );
-  pDef = sqlite3FindFunction(db, pExpr->u.zToken, 2, SQLITE_UTF8, 0);
+  nExpr = pExpr->x.pList->nExpr;
+  pDef = sqlite3FindFunction(db, pExpr->u.zToken, 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 d577f1d3f67abe6c9e95c27bb31fe8263015d2f2..528aeec2b027b0127c8cafe5529261019fc003da 100644 (file)
@@ -794,7 +794,7 @@ static int codeCursorHintIsOrFunction(Walker *pWalker, Expr *pExpr){
     pWalker->eCode = 1;
   }else if( pExpr->op==TK_FUNCTION ){
     int d1;
-    char d2[3];
+    char d2[4];
     if( 0==sqlite3IsLikeFunction(pWalker->pParse->db, pExpr, &d1, d2) ){
       pWalker->eCode = 1;
     }
index 461c14af191a76e13240cf2fbf1a3101493039be..7a70b54451ef27079b26be187ca269379784edc7 100644 (file)
@@ -199,7 +199,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 */
@@ -246,16 +246,44 @@ static int isLikeOrGlob(
         return 0;
       }
     }
+
+    /* 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 bae770b076bb78047591bef60240a4e968f5b4ad..1702dde7147fb9d321e709f7ef23e35f9cb14072 100644 (file)
@@ -207,7 +207,7 @@ do_test like-3.3.100 {
     SELECT x FROM t1 WHERE x LIKE 'abc%' ORDER BY 1;
   }
 } {abc abcd nosort {} i1}
-do_test like-3.3.101 {
+do_test like-3.3.100.cnt {
   set sqlite_like_count
 } 0
 
@@ -1048,4 +1048,52 @@ ifcapable !icu {
   } {1}
 }
 
+# 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