1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-07 02:42:48 +03:00

Add the ESCAPE clause to the LIKE operator. Not fully tested yet. (CVS 2107)

FossilOrigin-Name: 49268c2b7a84c4c618214dac8bef0f541440fe6b
This commit is contained in:
danielk1977
2004-11-17 16:41:29 +00:00
parent c7dc75334f
commit 7c6303c042
7 changed files with 216 additions and 23 deletions

View File

@@ -1,5 +1,5 @@
C Extra\stests\sand\sresulting\sbugfixes\sfor\sbtree\scursors.\s(CVS\s2106)
D 2004-11-17T10:22:03
C Add\sthe\sESCAPE\sclause\sto\sthe\sLIKE\soperator.\sNot\sfully\stested\syet.\s(CVS\s2107)
D 2004-11-17T16:41:29
F Makefile.in e747bb5ba34ccbdd81f79dcf1b2b33c02817c21d
F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457
F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1
@@ -35,7 +35,7 @@ F src/build.c a95eb1181247368b0ffe2eed121a43735976a964
F src/date.c 65536e7ea04fdde6e0551264fca15966966e171f
F src/delete.c be9d039b819f4a5d0fdfaeceace139ba189ef819
F src/expr.c 4ee3e47358c92a919062255b14057a7a8f641e01
F src/func.c 181ea4b8bbc621457838494a440d2e4e2307ab70
F src/func.c 897c1c130af08b29cdd89dd89f8c1832bab766b4
F src/hash.c a97721a55440b7bea31ffe471bb2f6b4123cddd5
F src/hash.h 1b0c445e1c89ff2aaad9b4605ba61375af001e84
F src/insert.c 9524a6c3e86cbdbae3313f6a083bb9a3e7a2462b
@@ -54,7 +54,7 @@ F src/os_win.c 9482dfc92f289b68205bb2c9315757c7e3946bfb
F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
F src/pager.c ee88fcecb081e3635c281bc09d604e934429e2f5
F src/pager.h 9eba8c53dd91eae7f3f90743b2ee242da02a9862
F src/parse.y 3282026b619e1c7f932fd8ecef9627fa86da048a
F src/parse.y 0a4bdfd7b65d9761b41a862d09a17c90c1f526f7
F src/pragma.c 0b43b8cac4870bfa041bf2ca29d7ce47b76362d6
F src/printf.c 3d20b21cfecadacecac3fb7274e746cb81d3d357
F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3
@@ -122,7 +122,7 @@ F test/diskfull.test e2f6cfd868713ead06dc82b84a4938e868128fc0
F test/enc.test 7a03417a1051fe8bc6c7641cf4c8c3f7e0066d52
F test/enc2.test 6d1a2650e9da43eab499d18ca694a0cb6ec69dee
F test/enc3.test f6a5f0b7b7f3a88f030d3143729b87cd5c86d837
F test/expr.test 8a96b21644b9702cabc3695f2b73ae0861376765
F test/expr.test bf826516ea0ba159eb9680fbcea955148bfe9bc3
F test/fkey1.test 81bb13caaa78f58d7d191d7f535529f7c91d821a
F test/func.test 830d352574c7f5cd15149a9be58a6dcc2b995c05
F test/hook.test f8605cde4c77b2c6a4a73723bf6c507796a64dda
@@ -143,6 +143,7 @@ F test/limit.test f7c06fccd76755e8d083b61c06bc31cf461b9c35
F test/lock.test ba72c211499b0874c56643b9ede1df4018bb20de
F test/lock2.test 59c3dd7d9b24d1bf7ec91b2d1541c37e97939d5f
F test/lock3.test 615111293cf32aa2ed16d01c6611737651c96fb9
F test/lock4.test 07768b4d4e942693d6036f1e6502199a3fa22a4f
F test/main.test 5f9deae11b93336da1ccc5f91cf8be075c91ddf1
F test/malloc.test 769b240d89a7ef3320d88919fdb6765f9395a51f
F test/memdb.test 34ee8598de307a16ccc3ac91b85cee9c668ae5ed
@@ -206,7 +207,7 @@ F tool/lempar.c 1e61d2b6cb9d8affa264a13336bc0c088498caa4
F tool/memleak.awk b744b6109566206c746d826f6ecdba34662216bc
F tool/memleak2.awk 9cc20c8e8f3c675efac71ea0721ee6874a1566e8
F tool/memleak3.tcl 336eb50b0849dbf99b1d5462d9c37291b01b2b43
F tool/mkkeywordhash.c 5f0d8bd4928e84e736469f9c989dae239314138e
F tool/mkkeywordhash.c c2254c191456316ce5d3f72a6b44fbf3c6492816
F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e x
F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c
F tool/report1.txt 9eae07f26a8fc53889b45fc833a66a33daa22816
@@ -258,7 +259,7 @@ F www/tclsqlite.tcl 560ecd6a916b320e59f2917317398f3d59b7cc25
F www/vdbe.tcl 095f106d93875c94b47367384ebc870517431618
F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0
F www/whentouse.tcl fdacb0ba2d39831e8a6240d05a490026ad4c4e4c
P e05f52d907e267b4f9ea204427229e7d7ef58641
R 41b0c2001282ed7479703649a89b1f7a
P e1530854c9004c25f5ffa21f9cfb9c44c83cc7f0
R fc41b89835631b13a4149106e5db9d17
U danielk1977
Z f5df5e3af5cd05954b652b1bc18662f8
Z a4a4e7382bd227dede1f241a7eed092d

View File

@@ -1 +1 @@
e1530854c9004c25f5ffa21f9cfb9c44c83cc7f0
49268c2b7a84c4c618214dac8bef0f541440fe6b

View File

@@ -16,7 +16,7 @@
** sqliteRegisterBuildinFunctions() found at the bottom of the file.
** All other code has file scope.
**
** $Id: func.c,v 1.87 2004/11/14 21:56:30 drh Exp $
** $Id: func.c,v 1.88 2004/11/17 16:41:30 danielk1977 Exp $
*/
#include <ctype.h>
#include <math.h>
@@ -347,10 +347,11 @@ static const struct compareInfo likeInfo = { '%', '_', 0, 1 };
**
** abc[*]xyz Matches "abc*xyz" only
*/
int patternCompare(
static int patternCompare(
const u8 *zPattern, /* The glob pattern */
const u8 *zString, /* The string to compare against the glob */
const struct compareInfo *pInfo /* Information about how to do the compare */
const struct compareInfo *pInfo, /* Information about how to do the compare */
const int esc /* The escape character */
){
register int c;
int invert;
@@ -360,9 +361,10 @@ int patternCompare(
u8 matchAll = pInfo->matchAll;
u8 matchSet = pInfo->matchSet;
u8 noCase = pInfo->noCase;
int prevEscape = 0; /* True if the previous character was 'escape' */
while( (c = *zPattern)!=0 ){
if( c==matchAll ){
if( !prevEscape && c==matchAll ){
while( (c=zPattern[1]) == matchAll || c == matchOne ){
if( c==matchOne ){
if( *zString==0 ) return 0;
@@ -370,9 +372,15 @@ int patternCompare(
}
zPattern++;
}
if( c && sqlite3ReadUtf8(&zPattern[1])==esc ){
u8 const *zTemp = &zPattern[1];
sqliteNextChar(zTemp);
c = *zTemp;
}
if( c==0 ) return 1;
if( c==matchSet ){
while( *zString && patternCompare(&zPattern[1],zString,pInfo)==0 ){
assert( esc==0 ); /* This is GLOB, not LIKE */
while( *zString && patternCompare(&zPattern[1],zString,pInfo,esc)==0 ){
sqliteNextChar(zString);
}
return *zString!=0;
@@ -386,17 +394,18 @@ int patternCompare(
while( c2 != 0 && c2 != c ){ c2 = *++zString; }
}
if( c2==0 ) return 0;
if( patternCompare(&zPattern[1],zString,pInfo) ) return 1;
if( patternCompare(&zPattern[1],zString,pInfo,esc) ) return 1;
sqliteNextChar(zString);
}
return 0;
}
}else if( c==matchOne ){
}else if( !prevEscape && c==matchOne ){
if( *zString==0 ) return 0;
sqliteNextChar(zString);
zPattern++;
}else if( c==matchSet ){
int prior_c = 0;
assert( esc==0 ); /* This only occurs for GLOB, not LIKE */
seen = 0;
invert = 0;
c = sqliteCharVal(zString);
@@ -424,6 +433,9 @@ int patternCompare(
if( c2==0 || (seen ^ invert)==0 ) return 0;
sqliteNextChar(zString);
zPattern++;
}else if( !prevEscape && sqlite3ReadUtf8(zPattern)==esc){
prevEscape = 1;
sqliteNextChar(zPattern);
}else{
if( noCase ){
if( sqlite3UpperToLower[c] != sqlite3UpperToLower[*zString] ) return 0;
@@ -432,6 +444,7 @@ int patternCompare(
}
zPattern++;
zString++;
prevEscape = 0;
}
}
return *zString==0;
@@ -457,8 +470,21 @@ static void likeFunc(
){
const unsigned char *zA = sqlite3_value_text(argv[0]);
const unsigned char *zB = sqlite3_value_text(argv[1]);
int escape = 0;
if( argc==3 ){
/* The escape character string must consist of a single UTF-8 character.
** Otherwise, return an error.
*/
const unsigned char *zEsc = sqlite3_value_text(argv[2]);
if( sqlite3utf8CharLen(zEsc, -1)!=1 ){
sqlite3_result_error(context,
"ESCAPE expression must be a single character", -1);
return;
}
escape = sqlite3ReadUtf8(zEsc);
}
if( zA && zB ){
sqlite3_result_int(context, patternCompare(zA, zB, &likeInfo));
sqlite3_result_int(context, patternCompare(zA, zB, &likeInfo, escape));
}
}
@@ -469,13 +495,13 @@ static void likeFunc(
**
** A GLOB B
**
** is implemented as glob(A,B).
** is implemented as glob(B,A).
*/
static void globFunc(sqlite3_context *context, int arg, sqlite3_value **argv){
const unsigned char *zA = sqlite3_value_text(argv[0]);
const unsigned char *zB = sqlite3_value_text(argv[1]);
if( zA && zB ){
sqlite3_result_int(context, patternCompare(zA, zB, &globInfo));
sqlite3_result_int(context, patternCompare(zA, zB, &globInfo, 0));
}
}
@@ -992,6 +1018,7 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
{ "ifnull", 2, 0, SQLITE_UTF8, 1, ifnullFunc },
{ "random", -1, 0, SQLITE_UTF8, 0, randomFunc },
{ "like", 2, 0, SQLITE_UTF8, 0, likeFunc },
{ "like", 3, 0, SQLITE_UTF8, 0, likeFunc },
{ "glob", 2, 0, SQLITE_UTF8, 0, globFunc },
{ "nullif", 2, 0, SQLITE_UTF8, 1, nullifFunc },
{ "sqlite_version", 0, 0, SQLITE_UTF8, 0, versionFunc},

View File

@@ -14,7 +14,7 @@
** the parser. Lemon will also generate a header file containing
** numeric codes for all of the tokens.
**
** @(#) $Id: parse.y,v 1.156 2004/11/13 15:59:15 drh Exp $
** @(#) $Id: parse.y,v 1.157 2004/11/17 16:41:30 danielk1977 Exp $
*/
%token_prefix TK_
%token_type {Token}
@@ -169,6 +169,7 @@ id(A) ::= ID(X). {A = X;}
%right NOT.
%left IS LIKE GLOB BETWEEN IN ISNULL NOTNULL NE EQ.
%left GT LE LT GE.
%right ESCAPE.
%left BITAND BITOR LSHIFT RSHIFT.
%left PLUS MINUS.
%left STAR SLASH REM.
@@ -637,14 +638,21 @@ likeop(A) ::= LIKE. {A.opcode = TK_LIKE; A.not = 0;}
likeop(A) ::= GLOB. {A.opcode = TK_GLOB; A.not = 0;}
likeop(A) ::= NOT LIKE. {A.opcode = TK_LIKE; A.not = 1;}
likeop(A) ::= NOT GLOB. {A.opcode = TK_GLOB; A.not = 1;}
expr(A) ::= expr(X) likeop(OP) expr(Y). [LIKE] {
%type escape {Expr*}
escape(X) ::= ESCAPE expr(A). [ESCAPE] {X = A;}
escape(X) ::= . [ESCAPE] {X = 0;}
expr(A) ::= expr(X) likeop(OP) expr(Y) escape(E). [LIKE] {
ExprList *pList = sqlite3ExprListAppend(0, Y, 0);
pList = sqlite3ExprListAppend(pList, X, 0);
if( E ){
pList = sqlite3ExprListAppend(pList, E, 0);
}
A = sqlite3ExprFunction(pList, 0);
if( A ) A->op = OP.opcode;
if( OP.not ) A = sqlite3Expr(TK_NOT, A, 0, 0);
sqlite3ExprSpan(A, &X->span, &Y->span);
}
expr(A) ::= expr(X) ISNULL(E). {
A = sqlite3Expr(TK_ISNULL, X, 0, 0);
sqlite3ExprSpan(A,&X->span,&E);

View File

@@ -11,7 +11,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing expressions.
#
# $Id: expr.test,v 1.39 2004/11/15 01:40:48 drh Exp $
# $Id: expr.test,v 1.40 2004/11/17 16:41:29 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -267,6 +267,45 @@ test_expr expr-5.55 {t1='abc', t2=NULL} {t1 NOT LIKE t2} {{}}
test_expr expr-5.56 {t1='abc', t2=NULL} {t2 LIKE t1} {{}}
test_expr expr-5.57 {t1='abc', t2=NULL} {t2 NOT LIKE t1} {{}}
# LIKE expressions that use ESCAPE characters.
test_expr expr-5.58 {t1='abc', t2='A_C'} {t1 LIKE t2 ESCAPE '7'} 1
test_expr expr-5.59 {t1='a_c', t2='A7_C'} {t1 LIKE t2 ESCAPE '7'} 1
test_expr expr-5.60 {t1='abc', t2='A7_C'} {t1 LIKE t2 ESCAPE '7'} 0
test_expr expr-5.61 {t1='a7Xc', t2='A7_C'} {t1 LIKE t2 ESCAPE '7'} 0
test_expr expr-5.62 {t1='abcde', t2='A%E'} {t1 LIKE t2 ESCAPE '7'} 1
test_expr expr-5.63 {t1='abcde', t2='A7%E'} {t1 LIKE t2 ESCAPE '7'} 0
test_expr expr-5.64 {t1='a7cde', t2='A7%E'} {t1 LIKE t2 ESCAPE '7'} 0
test_expr expr-5.65 {t1='a7cde', t2='A77%E'} {t1 LIKE t2 ESCAPE '7'} 1
test_expr expr-5.66 {t1='abc7', t2='A%77'} {t1 LIKE t2 ESCAPE '7'} 1
test_expr expr-5.67 {t1='abc_', t2='A%7_'} {t1 LIKE t2 ESCAPE '7'} 1
test_expr expr-5.68 {t1='abc7', t2='A%7_'} {t1 LIKE t2 ESCAPE '7'} 0
# These are the same test as the block above, but using a multi-byte
# character as the escape character.
if {"\u1234"!="u1234"} {
test_expr expr-5.69 "t1='abc', t2='A_C'" \
"t1 LIKE t2 ESCAPE '\u1234'" 1
test_expr expr-5.70 "t1='a_c', t2='A\u1234_C'" \
"t1 LIKE t2 ESCAPE '\u1234'" 1
test_expr expr-5.71 "t1='abc', t2='A\u1234_C'" \
"t1 LIKE t2 ESCAPE '\u1234'" 0
test_expr expr-5.72 "t1='a\u1234Xc', t2='A\u1234_C'" \
"t1 LIKE t2 ESCAPE '\u1234'" 0
test_expr expr-5.73 "t1='abcde', t2='A%E'" \
"t1 LIKE t2 ESCAPE '\u1234'" 1
test_expr expr-5.74 "t1='abcde', t2='A\u1234%E'" \
"t1 LIKE t2 ESCAPE '\u1234'" 0
test_expr expr-5.75 "t1='a\u1234cde', t2='A\u1234%E'" \
"t1 LIKE t2 ESCAPE '\u1234'" 0
test_expr expr-5.76 "t1='a\u1234cde', t2='A\u1234\u1234%E'" \
"t1 LIKE t2 ESCAPE '\u1234'" 1
test_expr expr-5.77 "t1='abc\u1234', t2='A%\u1234\u1234'" \
"t1 LIKE t2 ESCAPE '\u1234'" 1
test_expr expr-5.78 "t1='abc_', t2='A%\u1234_'" \
"t1 LIKE t2 ESCAPE '\u1234'" 1
test_expr expr-5.79 "t1='abc\u1234', t2='A%\u1234_'" \
"t1 LIKE t2 ESCAPE '\u1234'" 0
}
test_expr expr-6.1 {t1='abc', t2='xyz'} {t1 GLOB t2} 0
test_expr expr-6.2 {t1='abc', t2='ABC'} {t1 GLOB t2} 0

117
test/lock4.test Normal file
View File

@@ -0,0 +1,117 @@
# 2001 September 15
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The focus
# of this file is modifications made to tables while SELECT queries are
# active on the tables. Using this capability in a program is tricky
# because results can be difficult to predict, but can be useful.
#
# $Id: lock4.test,v 1.1 2004/11/17 16:41:29 danielk1977 Exp $
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
do_test lock4-1.0 {
execsql {
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 2);
}
} {}
# Check that we can INSERT into a table while doing a SELECT on it.
do_test lock4-1.1 {
db eval {SELECT * FROM t1} {
if {$a<5} {
execsql "INSERT INTO t1 VALUES($a+1, ($a+1)*2)"
}
}
} {}
do_test lock4-1.2 {
execsql {
SELECT * FROM t1
}
} {1 2 2 4 3 6 4 8 5 10}
# Check that we can UPDATE a table while doing a SELECT on it.
do_test lock4-1.3 {
db eval {SELECT * FROM t1 WHERE (a%2)=0} {
execsql "UPDATE t1 SET b = b/2 WHERE a = $a"
}
} {}
do_test lock4-1.4 {
execsql {
SELECT * FROM t1
}
} {1 2 2 2 3 6 4 4 5 10}
# Check that we can DELETE from a table while doing a SELECT on it.
do_test lock4-1.5 {
db eval {SELECT * FROM t1 WHERE (a%2)=0} {
execsql "DELETE FROM t1 WHERE a = $a"
}
} {}
do_test lock4-1.6 {
execsql {
SELECT * FROM t1
}
} {1 2 3 6 5 10}
# Check what happens when a row is deleted while a cursor is still using
# the row (because of a SELECT that does a join).
do_test lock4-2.0 {
execsql {
CREATE TABLE t2(c);
INSERT INTO t2 VALUES('one');
INSERT INTO t2 VALUES('two');
}
} {}
do_test lock4-2.1 {
set res [list]
db eval {SELECT a, b, c FROM t1, t2} {
lappend res $a $b $c
if {0==[string compare $c one]} {
execsql "DELETE FROM t1 WHERE a = $a"
}
}
set res
} {1 2 one 1 2 two 3 6 one 3 6 two 5 10 one 5 10 two}
do_test lock4-2.2 {
execsql {
SELECT * FROM t1;
}
} {}
# do_test lock4-2.3 {
# execsql "
# INSERT INTO t1 VALUES('[string repeat 1 750]', '[string repeat 2 750]')
# "
# } {}
# do_test lock4-2.4 {
# set res [list]
# db eval {SELECT a, b, c FROM t1, t2} {
# lappend res $a $b $c
# if {0==[string compare $c one]} {
# execsql "DELETE FROM t1 WHERE a = '$a'"
# }
# }
# set res
# } [list \
# [string repeat 1 750] [string repeat 2 750] one \
# [string repeat 1 750] [string repeat 2 750] two
# ]
# do_test lock4-2.5 {
# execsql {
# SELECT * FROM t1;
# }
# } {}
finish_test

View File

@@ -133,6 +133,7 @@ static Keyword aKeywordTable[] = {
{ "END", "TK_END", ALWAYS },
{ "EACH", "TK_EACH", TRIGGER },
{ "ELSE", "TK_ELSE", ALWAYS },
{ "ESCAPE", "TK_ESCAPE", ALWAYS },
{ "EXCEPT", "TK_EXCEPT", COMPOUND },
{ "EXCLUSIVE", "TK_EXCLUSIVE", ALWAYS },
{ "EXPLAIN", "TK_EXPLAIN", EXPLAIN },