mirror of
https://github.com/sqlite/sqlite.git
synced 2026-01-06 08:01:16 +03:00
Enhance the like optimization so that it works with an ESCAPE clause.
FossilOrigin-Name: f5d330f495d07a704e115595bbdf5422ddb68fd8191114c5a12c9c873d983f7c
This commit is contained in:
18
manifest
18
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
|
||||
|
||||
@@ -1 +1 @@
|
||||
0645f25c79c1b2af1fd3a02b44090329d456e373d91f6c284b8fbcb929e03a5d
|
||||
f5d330f495d07a704e115595bbdf5422ddb68fd8191114c5a12c9c873d983f7c
|
||||
30
src/func.c
30
src/func.c
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user