[tarantool-patches] Re: [PATCH 2/2] sql: remove GLOB from Tarantool
Alex Khatskevich
avkhatskevich at tarantool.org
Tue Sep 11 15:03:45 MSK 2018
>>>>>
>>>>> - "ESCAPE expression must be a single character",
>>>>> + "ESCAPE expression must be a"
>>>>> + " single character",
>>>> Do not split error messages at the middle of a sentence. It makes
>>>> errors ungreppable.
>>>> Make it <80 somehow different.
>>>>
>>>
>>> Have already been discussed in this thread.
>> I suppose that we concluded to try to fit into 80 and split the
>> string only
>> in case it is impossible.
>
> I don’t think so. Anyways, Alexander could you please give your thoughts?
Discussed with Nikita, Alexander, Vladimir, Kirill... Conclusion: use
`const char temp variable` if possible.
like this:
```
const char *const err_msg =
"ESCAPE expression must be a single character";
if (sqlite3Utf8CharLen((char *)zEsc, -1) != 1) {
sqlite3_result_error(context,
err_msg,
-1);
return;
```
If it does not help (but it is) split the message.
>>> diff --git a/test/sql-tap/e_expr.test.lua b/test/sql-tap/e_expr.test.lua
>>> index 162026845..0d69e8535 100755
>>> --- a/test/sql-tap/e_expr.test.lua
>>> +++ b/test/sql-tap/e_expr.test.lua
>>> @@ -1,6 +1,6 @@
>>> #!/usr/bin/env tarantool
>>> test = require("sqltester")
>>> -test:plan(11521)
>>> +test:plan(10647)
>>> --!./tcltestrunner.lua
>>> -- 2010 July 16
>>> @@ -77,7 +77,7 @@ local operations = {
>>> {"<>", "ne1"},
>>> {"!=", "ne2"},
>>> {"IS", "is"},
>>> - {"LIKE", "like"},
>>> +-- {"LIKE", "like"},
>>> {"AND", "and"},
>>> {"OR", "or"},
>>> {"MATCH", "match"},
>>> @@ -96,8 +96,9 @@ operations = {
>>> {"<<", ">>", "&", "|"},
>>> {"<", "<=", ">", ">="},
>>> -- Another NOTE: MATCH & REGEXP aren't supported in Tarantool &
>>> --- are waiting for their hour.
>>> - {"=", "==", "!=", "<>", "LIKE"}, --"MATCH", "REGEXP"},
>>> +-- are waiting for their hour, don't confuse them
>>> +-- being commented with commenting of "LIKE".
>>> + {"=", "==", "!=", "<>"}, --"LIKE"}, --"MATCH", "REGEXP"},
>> Delete Like. No one returns here.
>
> It’s a table of all of the operators thus I think it still worth
> leaving it there.
> Who knows, it may be that someone will revive tests for LIKE.
Delete Like. No one returns here.
We do not need a garbage. Like is not relevant anymore for this test.
>
> diff --git a/extra/mkkeywordhash.c b/extra/mkkeywordhash.c
> index 990c4199f..1fee3a7f2 100644
> --- a/extra/mkkeywordhash.c
> +++ b/extra/mkkeywordhash.c
> @@ -159,7 +159,6 @@ static Keyword aKeywordTable[] = {
> { "FOR", "TK_FOR", TRIGGER,
> true },
> { "FOREIGN", "TK_FOREIGN", FKEY, true },
> { "FROM", "TK_FROM", ALWAYS,
> true },
> - { "GLOB", "TK_LIKE_KW", ALWAYS,
> false },
> { "GROUP", "TK_GROUP", ALWAYS,
> true },
> { "HAVING", "TK_HAVING", ALWAYS,
> true },
> { "IF", "TK_IF", ALWAYS,
> true },
> diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
> index 5f73f026e..fc7588c3f 100644
> --- a/src/box/sql/analyze.c
> +++ b/src/box/sql/analyze.c
> @@ -829,7 +829,7 @@ analyzeOneTable(Parse * pParse,/* Parser context */
> return;
> }
> assert(pTab->tnum != 0);
> -if (sqlite3_strlike("\\_%", pTab->def->name, '\\') == 0) {
> +if (sql_strlike_ci("\\_%", pTab->def->name, '\\') == 0) {
> /* Do not gather statistics on system tables */
> return;
> }
> @@ -1333,11 +1333,10 @@ analysis_loader(void *data, int argc, char
> **argv, char **unused)
> /* Position ptr at the end of stat string. */
> for (; *z == ' ' || (*z >= '0' && *z <= '9'); ++z);
> while (z[0]) {
> -if (sqlite3_strglob("unordered*", z) == 0) {
> +if (sql_strlike_cs("unordered%", z, '[') == 0)
> index->def->opts.stat->is_unordered = true;
> -} else if (sqlite3_strglob("noskipscan*", z) == 0) {
> +else if (sql_strlike_cs("noskipscan%", z, '[') == 0)
> index->def->opts.stat->skip_scan_enabled = false;
> -}
> while (z[0] != 0 && z[0] != ' ')
> z++;
> while (z[0] == ' ')
> diff --git a/src/box/sql/func.c b/src/box/sql/func.c
> index 66cae17b5..28b435ae3 100644
> --- a/src/box/sql/func.c
> +++ b/src/box/sql/func.c
> @@ -607,41 +607,38 @@ total_changes(sqlite3_context * context, int
> NotUsed, sqlite3_value ** NotUsed2)
> sqlite3_result_int(context, sqlite3_total_changes(db));
> }
> -/*
> - * A structure defining how to do GLOB-style comparisons.
> - */
> -struct compareInfo {
> -u8 matchAll;/* "*" or "%" */
> -u8 matchOne;/* "?" or "_" */
> -u8 matchSet;/* "[" or 0 */
> -u8 noCase;/* true to ignore case differences */
> -};
> -
> /**
> - * Providing there are symbols in string s this
> - * macro returns UTF-8 code of character and
> - * promotes pointer to the next symbol in the string.
> - * Otherwise return code is SQL_END_OF_STRING.
> + * Providing there are symbols in string s this macro returns
> + * UTF-8 code of character and promotes pointer to the next
> + * symbol in the string. If s points to an invalid UTF-8 symbol
> + * return code is SQL_INVALID_UTF8_SYMBOL. If there're no symbols
> + * left in string s return code is SQL_END_OF_STRING.
> */
> #define Utf8Read(s, e) ucnv_getNextUChar(pUtf8conv, &(s), (e), &(status))
> #define SQL_END_OF_STRING 0xffff
> #define SQL_INVALID_UTF8_SYMBOL 0xfffd
> -static const struct compareInfo globInfo = { '*', '?', '[', 0 };
> +/**
> + * If SQLITE_CASE_SENSITIVE_LIKE is not defined, then the LIKE
> + * operator is not case sensitive.
> + */
> +static const int case_insensitive_like = 1;
> -/* The correct SQL-92 behavior is for the LIKE operator to ignore
> - * case. Thus 'a' LIKE 'A' would be true.
> +/**
> + * If SQLITE_CASE_SENSITIVE_LIKE is defined, then the LIKE
> + * operator is case sensitive causing 'a' LIKE 'A' to be false.
> */
> -static const struct compareInfo likeInfoNorm = { '%', '_', 0, 1 };
> +static const int case_sensitive_like = 0;
> -/* If SQLITE_CASE_SENSITIVE_LIKE is defined, then the LIKE operator
> - * is case sensitive causing 'a' LIKE 'A' to be false
> +/**
> + * Wildcards.
> */
> -static const struct compareInfo likeInfoAlt = { '%', '_', 0, 0 };
> +#define match_one '_'
> +#define match_all '%'
> -/*
> - * Possible error returns from sql_utf8_pattern_compare()
> +/**
> + * Possible error returns from sql_utf8_pattern_compare().
> */
> #define SQL_MATCH 0
> #define SQL_NOMATCH 1
> @@ -650,138 +647,91 @@ static const struct compareInfo likeInfoAlt = {
> '%', '_', 0, 0 };
> /**
> * Compare two UTF-8 strings for equality where the first string
> - * is a GLOB or LIKE expression.
> - *
> - * Globbing rules:
> - *
> - * '*' Matches any sequence of zero or more characters.
> - *
> - * '?' Matches exactly one character.
> - *
> - * [...] Matches one character from the enclosed list of
> - * characters.
> - *
> - * [^...] Matches one character not in the enclosed list.
> - *
> - * With the [...] and [^...] matching, a ']' character can be
> - * included in the list by making it the first character after
> - * '[' or '^'. A range of characters can be specified using '-'.
> - * Example: "[a-z]" matches any single lower-case letter.
> - * To match a '-', make it the last character in the list.
> + * is a LIKE expression.
> *
> * Like matching rules:
> *
> - * '%' Matches any sequence of zero or more characters.
> + * '%' Matches any sequence of zero or more
> + * characters.
> *
> * '_' Matches any one character.
> *
> - * Ec Where E is the "esc" character and c is any other
> - * character, including '%', '_', and esc, match
> - * exactly c.
> - *
> - * The comments within this routine usually assume glob matching.
> + * Ec Where E is the "esc" character and c is any
> + * other character, including '%', '_', and esc,
> + * match exactly c.
> *
> * This routine is usually quick, but can be N**2 in the worst
> * case.
> *
> * @param pattern String containing comparison pattern.
> * @param string String being compared.
> - * @param compareInfo Information about how to compare.
> - * @param matchOther The escape char (LIKE) or '[' (GLOB).
> + * @param is_like_ci true if LIKE is case insensitive.
> + * @param match_other The escape char for LIKE.
> *
> * @retval SQL_MATCH: Match.
> * SQL_NOMATCH: No match.
> - * SQL_NOWILDCARDMATCH: No match in spite of having *
> - * or % wildcards.
> + * SQL_NOWILDCARDMATCH: No match in spite of having %
> + * wildcard.
> * SQL_INVALID_PATTERN: Pattern contains invalid
> * symbol.
> */
> static int
> sql_utf8_pattern_compare(const char *pattern,
> const char *string,
> -const struct compareInfo *pInfo,
> -UChar32 matchOther)
> +const int is_like_ci,
> +UChar32 match_other)
> {
> /* Next pattern and input string chars */
> UChar32 c, c2;
> -/* "?" or "_" */
> -UChar32 matchOne = pInfo->matchOne;
> -/* "*" or "%" */
> -UChar32 matchAll = pInfo->matchAll;
> -/* True if uppercase==lowercase */
> -UChar32 noCase = pInfo->noCase;
> /* One past the last escaped input char */
> const char *zEscaped = 0;
> -const char * pattern_end = pattern + strlen(pattern);
> -const char * string_end = string + strlen(string);
> +const char *pattern_end = pattern + strlen(pattern);
> +const char *string_end = string + strlen(string);
> UErrorCode status = U_ZERO_ERROR;
> while (pattern < pattern_end) {
> c = Utf8Read(pattern, pattern_end);
> if (c == SQL_INVALID_UTF8_SYMBOL)
> return SQL_INVALID_PATTERN;
> -if (c == matchAll) {/* Match "*" */
> -/* Skip over multiple "*" characters in
> -* the pattern. If there are also "?"
> +if (c == match_all) {
> +/**
> +* Skip over multiple "%" characters in
> +* the pattern. If there are also "_"
> * characters, skip those as well, but
> * consume a single character of the
> -* input string for each "?" skipped.
> +* input string for each "_" skipped.
> */
> while ((c = Utf8Read(pattern, pattern_end)) !=
> SQL_END_OF_STRING) {
> if (c == SQL_INVALID_UTF8_SYMBOL)
> return SQL_INVALID_PATTERN;
> -if (c != matchAll && c != matchOne)
> +if (c != match_all && c != match_one)
> break;
> -if (c == matchOne &&
> +if (c == match_one &&
> (c2 = Utf8Read(string, string_end)) ==
> SQL_END_OF_STRING)
> return SQL_NOWILDCARDMATCH;
> if (c2 == SQL_INVALID_UTF8_SYMBOL)
> return SQL_NOMATCH;
> }
> -/*
> -* "*" at the end of the pattern matches.
> +/**
> +* "%" at the end of the pattern matches.
> */
> if (c == SQL_END_OF_STRING) {
> return SQL_MATCH;
> }
> -if (c == matchOther) {
> -if (pInfo->matchSet == 0) {
> -c = Utf8Read(pattern, pattern_end);
> -if (c == SQL_INVALID_UTF8_SYMBOL)
> -return SQL_INVALID_PATTERN;
> -if (c == SQL_END_OF_STRING)
> -return SQL_NOWILDCARDMATCH;
> -} else {
> -/* "[...]" immediately
> -* follows the "*". We
> -* have to do a slow
> -* recursive search in
> -* this case, but it is
> -* an unusual case.
> -*/
> -assert(matchOther < 0x80);
> -while (string < string_end) {
> -int bMatch =
> - sql_utf8_pattern_compare(
> -&pattern[-1],
> -string,
> -pInfo,
> -matchOther);
> -if (bMatch != SQL_NOMATCH)
> -return bMatch;
> -c = Utf8Read(string, string_end);
> -if (c == SQL_INVALID_UTF8_SYMBOL)
> -return SQL_NOMATCH;
> -}
> +if (c == match_other) {
> +c = Utf8Read(pattern, pattern_end);
> +if (c == SQL_INVALID_UTF8_SYMBOL)
> +return SQL_INVALID_PATTERN;
> +if (c == SQL_END_OF_STRING)
> return SQL_NOWILDCARDMATCH;
> -}
> }
> -/* At this point variable c contains the
> +/**
> +* At this point variable c contains the
> * first character of the pattern string
> -* past the "*". Search in the input
> +* past the "%". Search in the input
> * string for the first matching
> * character and recursively continue the
> * match from that point.
> @@ -793,7 +743,7 @@ sql_utf8_pattern_compare(const char *pattern,
> */
> int bMatch;
> -if (noCase)
> +if (is_like_ci)
> c = u_tolower(c);
> while (string < string_end){
> /**
> @@ -809,7 +759,7 @@ sql_utf8_pattern_compare(const char *pattern,
> c2 = Utf8Read(string, string_end);
> if (c2 == SQL_INVALID_UTF8_SYMBOL)
> return SQL_NOMATCH;
> -if (!noCase) {
> +if (!is_like_ci) {
> if (c2 != c)
> continue;
> } else {
> @@ -818,79 +768,27 @@ sql_utf8_pattern_compare(const char *pattern,
> }
> bMatch = sql_utf8_pattern_compare(pattern,
> string,
> - pInfo,
> - matchOther);
> + is_like_ci,
> + match_other);
> if (bMatch != SQL_NOMATCH)
> return bMatch;
> }
> return SQL_NOWILDCARDMATCH;
> }
> -if (c == matchOther) {
> -if (pInfo->matchSet == 0) {
> -c = Utf8Read(pattern, pattern_end);
> -if (c == SQL_INVALID_UTF8_SYMBOL)
> -return SQL_INVALID_PATTERN;
> -if (c == SQL_END_OF_STRING)
> -return SQL_NOMATCH;
> -zEscaped = pattern;
> -} else {
> -UChar32 prior_c = 0;
> -int seen = 0;
> -int invert = 0;
> -c = Utf8Read(string, string_end);
> -if (c == SQL_INVALID_UTF8_SYMBOL)
> -return SQL_NOMATCH;
> -if (string == string_end)
> -return SQL_NOMATCH;
> -c2 = Utf8Read(pattern, pattern_end);
> -if (c2 == SQL_INVALID_UTF8_SYMBOL)
> -return SQL_INVALID_PATTERN;
> -if (c2 == '^') {
> -invert = 1;
> -c2 = Utf8Read(pattern, pattern_end);
> -if (c2 == SQL_INVALID_UTF8_SYMBOL)
> -return SQL_INVALID_PATTERN;
> -}
> -if (c2 == ']') {
> -if (c == ']')
> -seen = 1;
> -c2 = Utf8Read(pattern, pattern_end);
> -if (c2 == SQL_INVALID_UTF8_SYMBOL)
> -return SQL_INVALID_PATTERN;
> -}
> -while (c2 != SQL_END_OF_STRING && c2 != ']') {
> -if (c2 == '-' && pattern[0] != ']'
> - && pattern < pattern_end
> - && prior_c > 0) {
> -c2 = Utf8Read(pattern, pattern_end);
> -if (c2 == SQL_INVALID_UTF8_SYMBOL)
> -return SQL_INVALID_PATTERN;
> -if (c >= prior_c && c <= c2)
> -seen = 1;
> -prior_c = 0;
> -} else {
> -if (c == c2) {
> -seen = 1;
> -}
> -prior_c = c2;
> -}
> -c2 = Utf8Read(pattern, pattern_end);
> -if (c2 == SQL_INVALID_UTF8_SYMBOL)
> -return SQL_INVALID_PATTERN;
> -}
> -if (pattern == pattern_end ||
> - (seen ^ invert) == 0) {
> -return SQL_NOMATCH;
> -}
> -continue;
> -}
> +if (c == match_other) {
> +c = Utf8Read(pattern, pattern_end);
> +if (c == SQL_INVALID_UTF8_SYMBOL)
> +return SQL_INVALID_PATTERN;
> +if (c == SQL_END_OF_STRING)
> +return SQL_NOMATCH;
> +zEscaped = pattern;
> }
> c2 = Utf8Read(string, string_end);
> if (c2 == SQL_INVALID_UTF8_SYMBOL)
> return SQL_NOMATCH;
> if (c == c2)
> continue;
> -if (noCase){
> +if (is_like_ci) {
> /**
> * Small optimisation. Reduce number of
> * calls to u_tolower function. SQL
> @@ -903,7 +801,7 @@ sql_utf8_pattern_compare(const char *pattern,
> c == u_tolower(c2))
> continue;
> }
> -if (c == matchOne && pattern != zEscaped &&
> +if (c == match_one && pattern != zEscaped &&
> c2 != SQL_END_OF_STRING)
> continue;
> return SQL_NOMATCH;
> @@ -911,55 +809,52 @@ sql_utf8_pattern_compare(const char *pattern,
> return string == string_end ? SQL_MATCH : SQL_NOMATCH;
> }
> -/*
> - * The sqlite3_strglob() interface. Return 0 on a match (like
> strcmp()) and
> - * non-zero if there is no match.
> +/**
> + * Compare two UTF-8 strings for equality using case sensitive
> + * sql_utf8_pattern_compare.
> */
> int
> -sqlite3_strglob(const char *zGlobPattern, const char *zString)
> +sql_strlike_cs(const char *zPattern, const char *zStr, unsigned int esc)
> {
> -return sql_utf8_pattern_compare(zGlobPattern, zString, &globInfo, '[');
> +return sql_utf8_pattern_compare(zPattern, zStr, case_sensitive_like,
> esc);
> }
> -/*
> - * The sqlite3_strlike() interface. Return 0 on a match and non-zero for
> - * a miss - like strcmp().
> +/**
> + * Compare two UTF-8 strings for equality using case insensitive
> + * sql_utf8_pattern_compare.
> */
> int
> -sqlite3_strlike(const char *zPattern, const char *zStr, unsigned int esc)
> +sql_strlike_ci(const char *zPattern, const char *zStr, unsigned int esc)
> {
> -return sql_utf8_pattern_compare(zPattern, zStr, &likeInfoNorm, esc);
> +return sql_utf8_pattern_compare(zPattern, zStr,
> case_insensitive_like, esc);
> }
> -/*
> - * Count the number of times that the LIKE operator (or GLOB which is
> - * just a variation of LIKE) gets called. This is used for testing
> - * only.
> +/**
> + * Count the number of times that the LIKE operator gets called.
> + * This is used for testing only.
> */
> #ifdef SQLITE_TEST
> int sqlite3_like_count = 0;
> #endif
> -/*
> - * Implementation of the like() SQL function. This function implements
> - * the build-in LIKE operator. The first argument to the function is the
> - * pattern and the second argument is the string. So, the SQL
> statements:
> +/**
> + * Implementation of the like() SQL function. This function
> + * implements the built-in LIKE operator. The first argument to
> + * the function is the pattern and the second argument is the
> + * string. So, the SQL statements of the following type:
> *
> * A LIKE B
> *
> - * is implemented as like(B,A).
> - *
> - * This same function (with a different compareInfo structure) computes
> - * the GLOB operator.
> + * are implemented as like(B,A).
> */
> static void
> -likeFunc(sqlite3_context * context, int argc, sqlite3_value ** argv)
> +likeFunc(sqlite3_context *context, int argc, sqlite3_value **argv)
> {
> const char *zA, *zB;
> u32 escape;
> int nPat;
> sqlite3 *db = sqlite3_context_db_handle(context);
> -struct compareInfo *pInfo = sqlite3_user_data(context);
> +int *is_like_ci = sqlite3_user_data(context);
> #ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS
> if (sqlite3_value_type(argv[0]) == SQLITE_BLOB
> @@ -974,8 +869,9 @@ likeFunc(sqlite3_context * context, int argc,
> sqlite3_value ** argv)
> zB = (const char *) sqlite3_value_text(argv[0]);
> zA = (const char *) sqlite3_value_text(argv[1]);
> -/* Limit the length of the LIKE or GLOB pattern to avoid
> -* problems of deep recursion and N*N behavior in
> +/**
> +* Limit the length of the LIKE pattern to avoid problems
> +* of deep recursion and N*N behavior in
> * sql_utf8_pattern_compare().
> */
> nPat = sqlite3_value_bytes(argv[0]);
> @@ -983,28 +879,29 @@ likeFunc(sqlite3_context * context, int argc,
> sqlite3_value ** argv)
> testcase(nPat == db->aLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH] + 1);
> if (nPat > db->aLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]) {
> sqlite3_result_error(context,
> - "LIKE or GLOB pattern too complex", -1);
> + "LIKE pattern is too complex", -1);
> return;
> }
> /* Encoding did not change */
> assert(zB == (const char *) sqlite3_value_text(argv[0]));
> if (argc == 3) {
> -/* The escape character string must consist of a single UTF-8 character.
> -* Otherwise, return an error.
> +/**
> +* 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 (zEsc == 0)
> return;
> if (sqlite3Utf8CharLen((char *)zEsc, -1) != 1) {
> sqlite3_result_error(context,
> - "ESCAPE expression must be a single character",
> + "ESCAPE expression must be a"
> + " single character",
> -1);
> return;
> }
> escape = sqlite3Utf8Read(&zEsc);
> -} else {
> -escape = pInfo->matchSet;
> }
> if (!zA || !zB)
> return;
> @@ -1012,10 +909,10 @@ likeFunc(sqlite3_context * context, int argc,
> sqlite3_value ** argv)
> sqlite3_like_count++;
> #endif
> int res;
> -res = sql_utf8_pattern_compare(zB, zA, pInfo, escape);
> +res = sql_utf8_pattern_compare(zB, zA, *is_like_ci, escape);
> if (res == SQL_INVALID_PATTERN) {
> -sqlite3_result_error(context, "LIKE or GLOB pattern can only"
> - " contain UTF-8 characters", -1);
> +sqlite3_result_error(context, "LIKE pattern can only contain"
> + " UTF-8 characters", -1);
> return;
> }
> sqlite3_result_int(context, res == SQL_MATCH);
> @@ -1811,64 +1708,54 @@ setLikeOptFlag(sqlite3 * db, const char
> *zName, u8 flagVal)
> }
> }
> -/*
> - * Register the built-in LIKE and GLOB functions. The caseSensitive
> - * parameter determines whether or not the LIKE operator is case
> - * sensitive. GLOB is always case sensitive.
> +/**
> + * Register the built-in LIKE function.
> + *
> + * @param db database structure.
> + * @param is_case_sensitive whether like should be case sensitive
> + * or not.
> + *
> + * @retval none.
> */
> void
> -sqlite3RegisterLikeFunctions(sqlite3 * db, int caseSensitive)
> +sqlite3RegisterLikeFunctions(sqlite3 *db, int is_case_sensitive)
> {
> -struct compareInfo *pInfo;
> -if (caseSensitive) {
> -pInfo = (struct compareInfo *)&likeInfoAlt;
> -} else {
> -pInfo = (struct compareInfo *)&likeInfoNorm;
> -}
> -sqlite3CreateFunc(db, "LIKE", 2, 0, pInfo, likeFunc, 0, 0, 0);
> -sqlite3CreateFunc(db, "LIKE", 3, 0, pInfo, likeFunc, 0, 0, 0);
> -sqlite3CreateFunc(db, "GLOB", 2, 0, (struct compareInfo *)&globInfo,
> likeFunc, 0, 0, 0);
> -setLikeOptFlag(db, "GLOB", SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE);
> +int *is_like_ci;
> +if (is_case_sensitive)
> +is_like_ci = (int *)&case_sensitive_like;
> +else
> +is_like_ci = (int *)&case_insensitive_like;
> +sqlite3CreateFunc(db, "LIKE", 2, 0, is_like_ci, likeFunc, 0, 0, 0);
> +sqlite3CreateFunc(db, "LIKE", 3, 0, is_like_ci, likeFunc, 0, 0, 0);
> setLikeOptFlag(db, "LIKE",
> - caseSensitive ? (SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE) :
> - SQLITE_FUNC_LIKE);
> + is_case_sensitive ? (SQLITE_FUNC_LIKE |
> + SQLITE_FUNC_CASE) : SQLITE_FUNC_LIKE);
> }
> -/*
> - * 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.
> +/**
> + * Check if the function implements LIKE-style comparison & if it
> + * is appropriate to apply a LIKE query optimization.
> + *
> + * @param db database structure.
> + * @param pExpr pointer to a function-implementing expression.
> + * @param is_like_ci true if LIKE is case insensitive.
> *
> - * *pIsNocase is set to true if uppercase and lowercase are
> equivalent for
> - * the function (default for LIKE). If the function makes the
> distinction
> - * between uppercase and lowercase (as does GLOB) then *pIsNocase is
> set to
> - * false.
> + * @retval 0 if it's appropriate to apply optimization.
> + * 1 if it's not.
> */
> int
> -sqlite3IsLikeFunction(sqlite3 * db, Expr * pExpr, int *pIsNocase,
> char *aWc)
> +sql_is_like_func(sqlite3 *db, Expr *pExpr, int *is_like_ci)
> {
> FuncDef *pDef;
> -if (pExpr->op != TK_FUNCTION
> - || !pExpr->x.pList || pExpr->x.pList->nExpr != 2) {
> +if (pExpr->op != TK_FUNCTION || !pExpr->x.pList ||
> + pExpr->x.pList->nExpr != 2)
> return 0;
> -}
> assert(!ExprHasProperty(pExpr, EP_xIsSelect));
> pDef = sqlite3FindFunction(db, pExpr->u.zToken, 2, 0);
> if (NEVER(pDef == 0) || (pDef->funcFlags & SQLITE_FUNC_LIKE) == 0) {
> return 0;
> }
> -
> -/* The memcpy() statement assumes that the wildcard characters are
> -* the first three statements in the compareInfo structure. The
> -* asserts() that follow verify that assumption
> -*/
> -memcpy(aWc, pDef->pUserData, 3);
> -assert((char *)&likeInfoAlt == (char *)&likeInfoAlt.matchAll);
> -assert(&((char *)&likeInfoAlt)[1] == (char *)&likeInfoAlt.matchOne);
> -assert(&((char *)&likeInfoAlt)[2] == (char *)&likeInfoAlt.matchSet);
> -*pIsNocase = (pDef->funcFlags & SQLITE_FUNC_CASE) == 0;
> +*is_like_ci = (pDef->funcFlags & SQLITE_FUNC_CASE) == 0;
> return 1;
> }
> @@ -1962,16 +1849,14 @@ sqlite3RegisterBuiltinFunctions(void)
> AGGREGATE(group_concat, 2, 0, 0, groupConcatStep,
> groupConcatFinalize),
> -LIKEFUNC(glob, 2, &globInfo,
> -SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE),
> #ifdef SQLITE_CASE_SENSITIVE_LIKE
> -LIKEFUNC(like, 2, &likeInfoAlt,
> +LIKEFUNC(like, 2, &case_sensitive_like,
> SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE),
> -LIKEFUNC(like, 3, &likeInfoAlt,
> +LIKEFUNC(like, 3, &case_sensitive_like,
> SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE),
> #else
> -LIKEFUNC(like, 2, &likeInfoNorm, SQLITE_FUNC_LIKE),
> -LIKEFUNC(like, 3, &likeInfoNorm, SQLITE_FUNC_LIKE),
> +LIKEFUNC(like, 2, &case_insensitive_like, SQLITE_FUNC_LIKE),
> +LIKEFUNC(like, 3, &case_insensitive_like, SQLITE_FUNC_LIKE),
> #endif
> #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
> FUNCTION(unknown, -1, 0, 0, unknownFunc),
> diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
> index 5fb29c75c..26a602b23 100644
> --- a/src/box/sql/pragma.c
> +++ b/src/box/sql/pragma.c
> @@ -771,8 +771,10 @@ sqlite3Pragma(Parse * pParse, Token * pId,/*
> First part of [schema.]id field */
> }
> #endif
> -/* Reinstall the LIKE and GLOB functions. The variant of LIKE *
> -* used will be case sensitive or not depending on the RHS.
> +/**
> +* Reinstall the LIKE and functions. The variant
> +* of LIKE * used will be case sensitive or not
> +* depending on the RHS.
> */
> case PragTyp_CASE_SENSITIVE_LIKE:{
> if (zRight) {
> diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
> index e7a02dc1d..a805adf22 100644
> --- a/src/box/sql/sqliteInt.h
> +++ b/src/box/sql/sqliteInt.h
> @@ -565,16 +565,15 @@ char *
> sqlite3_vsnprintf(int, char *, const char *, va_list);
> int
> -sqlite3_strlike(const char *zGlob, const char *zStr,
> -unsigned int cEsc);
> +sql_strlike_cs(const char *zLike, const char *zStr, unsigned int cEsc);
> +
> +int
> +sql_strlike_ci(const char *zLike, const char *zStr, unsigned int cEsc);
> typedef void (*sqlite3_destructor_type) (void *);
> #define SQLITE_STATIC ((sqlite3_destructor_type)0)
> #define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1)
> -int
> -sqlite3_strglob(const char *zGlob, const char *zStr);
> -
> int
> sqlite3_prepare(sqlite3 * db,/* Database handle */
> const char *zSql,/* SQL statement, UTF-8 encoded */
> @@ -701,9 +700,6 @@ struct on_conflict {
> enum on_conflict_action optimized_action;
> };
> -void *
> -sqlite3_user_data(sqlite3_context *);
> -
> void
> sqlite3_randomness(int N, void *P);
> @@ -2355,7 +2351,7 @@ struct Expr {
> #define EP_Distinct 0x000010/* Aggregate function with DISTINCT
> keyword */
> #define EP_VarSelect 0x000020/* pSelect is correlated, not constant */
> #define EP_DblQuoted 0x000040/* token.z was originally in "..." */
> -#define EP_InfixFunc 0x000080/* True for an infix function: LIKE,
> GLOB, etc */
> +#define EP_InfixFunc 0x000080/* True for an infix function: LIKE, etc */
> #define EP_Collate 0x000100/* Tree contains a TK_COLLATE operator */
> #define EP_Generic 0x000200/* Ignore COLLATE or affinity on this
> tree */
> #define EP_IntValue 0x000400/* Integer value contained in u.iValue */
> @@ -4378,7 +4374,7 @@ index_column_count(const Index *);
> bool
> index_is_unique_not_null(const Index *);
> void sqlite3RegisterLikeFunctions(sqlite3 *, int);
> -int sqlite3IsLikeFunction(sqlite3 *, Expr *, int *, char *);
> +int sql_is_like_func(sqlite3 *db, Expr *pExpr, int *is_case_insensitive);
> void sqlite3SchemaClear(sqlite3 *);
> Schema *sqlite3SchemaCreate(sqlite3 *);
> int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *,
> diff --git a/src/box/sql/sqliteLimit.h b/src/box/sql/sqliteLimit.h
> index b88c9c6d3..e76353aff 100644
> --- a/src/box/sql/sqliteLimit.h
> +++ b/src/box/sql/sqliteLimit.h
> @@ -164,8 +164,7 @@ enum {
> #endif
> /*
> - * Maximum length (in bytes) of the pattern in a LIKE or GLOB
> - * operator.
> + * Maximum length (in bytes) of the pattern in a LIKE operator.
> */
> #ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH
> #define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000
> diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
> index 0c978142d..3f10f4d68 100644
> --- a/src/box/sql/vdbe.c
> +++ b/src/box/sql/vdbe.c
> @@ -5521,7 +5521,7 @@ vdbe_return:
> testcase( nVmStep>0);
> p->aCounter[SQLITE_STMTSTATUS_VM_STEP] += (int)nVmStep;
> assert(rc!=SQLITE_OK || nExtraDelete==0
> -|| sqlite3_strlike("DELETE%",p->zSql,0)!=0
> +|| sql_strlike_ci("DELETE%", p->zSql, 0) != 0
> );
> return rc;
> diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
> index c35c25ac4..f864ea7fa 100644
> --- a/src/box/sql/wherecode.c
> +++ b/src/box/sql/wherecode.c
> @@ -339,7 +339,7 @@ sqlite3WhereAddScanStatus(Vdbe * v,/* Vdbe to add
> scanstatus entry to */
> * automatically disabled. In this way, terms get disabled if derived
> * virtual terms are tested first. For example:
> *
> - * x GLOB 'abc*' AND x>='abc' AND x<'acd'
> + * x LIKE 'abc%' AND x>='abc' AND x<'acd'
> * \___________/ \______/ \_____/
> * parent child1 child2
> *
> diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c
> index 612868695..2d9fb6453 100644
> --- a/src/box/sql/whereexpr.c
> +++ b/src/box/sql/whereexpr.c
> @@ -218,38 +218,61 @@ operatorMask(int op)
> return c;
> }
> +/**
> + * Wildcard characters.
> + */
> +#define match_one '_'
> +#define match_all '%'
If possible - move the define to a header which both (whereexpr and
func) file include.
This also require to give more descriptive name, e.g. LIKE_MATCH_ONE.
> +
> #ifndef SQLITE_OMIT_LIKE_OPTIMIZATION
> -/*
> - * Check to see if the given expression is a LIKE or GLOB operator that
> - * can be optimized using inequality constraints. Return TRUE if it is
> - * so and false if not.
> +/**
> + * Check to see if the given expression is a LIKE operator that
> + * can be optimized using inequality constraints.
> *
> - * In order for the operator to be optimizible, the RHS must be a string
> - * literal that does not begin with a wildcard. The LHS must be a column
> - * that may only be NULL, a string, or a BLOB, never a number. The
> - * collating sequence for the column on the LHS must be appropriate for
> - * the operator.
> + * In order for the operator to be optimizible, the RHS must be a
> + * string literal that does not begin with a wildcard. The LHS
> + * must be a column that may only be NULL, a string, or a BLOB,
> + * never a number. The collating sequence for the column on the
> + * LHS must be appropriate for the operator.
> + *
> + * @param pParse Parsing and code generating context.
> + * @param pExpr Test this expression.
> + * @param ppPrefix Pointer to TK_STRING expression with
> + * pattern prefix.
> + * @param pisComplete True if the only wildcard is '%' in the
> + * last character.
> + * @param pnoCase True if case insensitive.
> + *
> + * @retval True if the given expr is a LIKE operator & is
> + * optimizable using inequality constraints.
> + * False if not.
> */
> static int
> -isLikeOrGlob(Parse * pParse,/* Parsing and code generating context */
> - Expr * pExpr,/* Test this expression */
> - Expr ** ppPrefix,/* Pointer to TK_STRING expression with pattern
> prefix */
> - int *pisComplete,/* True if the only wildcard is % in the last
> character */
> - int *pnoCase/* True if uppercase is equivalent to lowercase */
> - )
> +is_like(Parse *pParse,
> +Expr *pExpr,
> +Expr **ppPrefix,
> +int *pisComplete,
> +int *pnoCase)
> {
> -const char *z = 0;/* String on RHS of LIKE operator */
> -Expr *pRight, *pLeft;/* Right and left size of LIKE operator */
> -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 */
> -sqlite3 *db = pParse->db;/* Database connection */
> +/* String on RHS of LIKE operator */
> +const char *z = 0;
> +/* Right and left size of LIKE operator */
> +Expr *pRight, *pLeft;
> +/* List of operands to the LIKE operator */
> +ExprList *pList;
> +/* One character in z[] */
> +int c;
> +/* Number of non-wildcard prefix characters */
> +int cnt;
> +/* Database connection */
> +sqlite3 *db = pParse->db;
> sqlite3_value *pVal = 0;
> -int op;/* Opcode of pRight */
> -int rc;/* Result code to return */
> +/* Opcode of pRight */
> +int op;
> +/* Result code to return */
> +int rc;
> -if (!sqlite3IsLikeFunction(db, pExpr, pnoCase, wc)) {
> +if (!sql_is_like_func(db, pExpr, pnoCase)) {
> return 0;
> }
> pList = pExpr->x.pList;
> @@ -257,8 +280,9 @@ isLikeOrGlob(Parse * pParse,/* Parsing and code
> generating context */
> /* Value might be numeric */
> if (pLeft->op != TK_COLUMN ||
> sqlite3ExprAffinity(pLeft) != AFFINITY_TEXT) {
> -/* IMP: R-02065-49465 The left-hand side of the LIKE or GLOB operator
> must
> -* be the name of an indexed column with TEXT affinity.
> +/* IMP: R-02065-49465 The left-hand side of the
> +* LIKE operator must be the name of an indexed
> +* column with TEXT affinity.
> */
> return 0;
> }
> @@ -281,13 +305,11 @@ isLikeOrGlob(Parse * pParse,/* Parsing and code
> generating context */
> }
> if (z) {
> cnt = 0;
> -while ((c = z[cnt]) != 0 && c != wc[0] && c != wc[1]
> - && c != wc[2]) {
> +while ((c = z[cnt]) != 0 && c != match_one && c != match_all)
> cnt++;
> -}
> if (cnt != 0 && 255 != (u8) z[cnt - 1]) {
> Expr *pPrefix;
> -*pisComplete = c == wc[0] && z[cnt + 1] == 0;
> +*pisComplete = c == match_all && z[cnt + 1] == 0;
> pPrefix = sqlite3Expr(db, TK_STRING, z);
> if (pPrefix)
> pPrefix->u.zToken[cnt] = 0;
> @@ -943,19 +965,32 @@ exprAnalyze(SrcList * pSrc,/* the FROM clause */
> int idxTerm/* Index of the term to be analyzed */
> )
> {
> -WhereInfo *pWInfo = pWC->pWInfo;/* WHERE clause processing context */
> -WhereTerm *pTerm;/* The term to be analyzed */
> -WhereMaskSet *pMaskSet;/* Set of table index masks */
> -Expr *pExpr;/* The expression to be analyzed */
> -Bitmask prereqLeft;/* Prerequesites of the pExpr->pLeft */
> -Bitmask prereqAll;/* Prerequesites of pExpr */
> -Bitmask extraRight = 0;/* Extra dependencies on LEFT JOIN */
> -Expr *pStr1 = 0;/* RHS of LIKE/GLOB operator */
> -int isComplete = 0;/* RHS of LIKE/GLOB ends with wildcard */
> -int noCase = 0;/* uppercase equivalent to lowercase */
> -int op;/* Top-level operator. pExpr->op */
> -Parse *pParse = pWInfo->pParse;/* Parsing context */
> -sqlite3 *db = pParse->db;/* Database connection */
> +/* WHERE clause processing context */
> +WhereInfo *pWInfo = pWC->pWInfo;
> +/* The term to be analyzed */
> +WhereTerm *pTerm;
> +/* Set of table index masks */
> +WhereMaskSet *pMaskSet;
> +/* The expression to be analyzed */
> +Expr *pExpr;
> +/* Prerequesites of the pExpr->pLeft */
> +Bitmask prereqLeft;
> +/* Prerequesites of pExpr */
> +Bitmask prereqAll;
> +/* Extra dependencies on LEFT JOIN */
> +Bitmask extraRight = 0;
> +/* RHS of LIKE operator */
> +Expr *pStr1 = 0;
> +/* RHS of LIKE ends with wildcard */
> +int isComplete = 0;
> +/* uppercase equivalent to lowercase */
> +int noCase = 0;
> +/* Top-level operator. pExpr->op */
> +int op;
> +/* Parsing context */
> +Parse *pParse = pWInfo->pParse;
> +/* Database connection */
> +sqlite3 *db = pParse->db;
> if (db->mallocFailed) {
> return;
> @@ -1111,37 +1146,44 @@ exprAnalyze(SrcList * pSrc,/* the FROM clause */
> #endif/* SQLITE_OMIT_OR_OPTIMIZATION */
> #ifndef SQLITE_OMIT_LIKE_OPTIMIZATION
> -/* Add constraints to reduce the search space on a LIKE or GLOB
> +/**
> +* Add constraints to reduce the search space on a LIKE
> * operator.
> *
> -* A like pattern of the form "x LIKE 'aBc%'" is changed into constraints
> +* A like pattern of the form "x LIKE 'aBc%'" is changed
> +* into constraints:
> *
> * x>='ABC' AND x<'abd' AND x LIKE 'aBc%'
> *
> -* The last character of the prefix "abc" is incremented to form the
> -* termination condition "abd". If case is not significant (the default
> -* for LIKE) then the lower-bound is made all uppercase and the upper-
> -* bound is made all lowercase so that the bounds also work when comparing
> -* BLOBs.
> +* The last character of the prefix "abc" is incremented
> +* to form the termination condition "abd". If case is
> +* not significant (the default for LIKE) then the
> +* lower-bound is made all uppercase and the upper-bound
> +* is made all lowercase so that the bounds also work
> +* when comparing BLOBs.
> */
> if (pWC->op == TK_AND
> - && isLikeOrGlob(pParse, pExpr, &pStr1, &isComplete, &noCase)
> - ) {
> -Expr *pLeft;/* LHS of LIKE/GLOB operator */
> -Expr *pStr2;/* Copy of pStr1 - RHS of LIKE/GLOB operator */
> + && is_like(pParse, pExpr, &pStr1, &isComplete, &noCase)) {
> +/* LHS of LIKE operator */
> +Expr *pLeft;
> +/* Copy of pStr1 - RHS of LIKE operator */
> +Expr *pStr2;
> Expr *pNewExpr1;
> Expr *pNewExpr2;
> int idxNew1;
> int idxNew2;
> -const char *zCollSeqName;/* Name of collating sequence */
> +/* Name of collating sequence */
> +const char *zCollSeqName;
> const u16 wtFlags = TERM_LIKEOPT | TERM_VIRTUAL | TERM_DYNAMIC;
> pLeft = pExpr->x.pList->a[1].pExpr;
> pStr2 = sqlite3ExprDup(db, pStr1, 0);
> -/* Convert the lower bound to upper-case and the upper bound to
> -* lower-case (upper-case is less than lower-case in ASCII) so that
> -* the range constraints also work for BLOBs
> +/**
> +* Convert the lower bound to upper-case and the
> +* upper bound to lower-case (upper-case is less
> +* than lower-case in ASCII) so that the range
> +* constraints also work for BLOBs
> */
> if (noCase && !pParse->db->mallocFailed) {
> int i;
> diff --git a/test/sql-tap/alter.test.lua b/test/sql-tap/alter.test.lua
> index cfe280121..98338c493 100755
> --- a/test/sql-tap/alter.test.lua
> +++ b/test/sql-tap/alter.test.lua
> @@ -232,7 +232,7 @@ test:do_execsql_test(
> [[
> CREATE TABLE xyz(x PRIMARY KEY);
> ALTER TABLE xyz RENAME TO "xyz1234abc";
> - SELECT "name" FROM "_space" WHERE "name" GLOB 'xyz*';
> + SELECT "name" FROM "_space" WHERE "name" = 'xyz1234abc';
> ]], {
> -- <alter-5.1>
> "xyz1234abc"
> @@ -243,7 +243,7 @@ test:do_execsql_test(
> "alter-5.2",
> [[
> ALTER TABLE "xyz1234abc" RENAME TO xyzabc;
> - SELECT "name" FROM "_space" WHERE "name" GLOB 'XYZ*';
> + SELECT "name" FROM "_space" WHERE "name" = 'XYZABC';
> ]], {
> -- <alter-5.2>
> "XYZABC"
> diff --git a/test/sql-tap/analyze9.test.lua
> b/test/sql-tap/analyze9.test.lua
> index 3b3d52f67..ec3e545d8 100755
> --- a/test/sql-tap/analyze9.test.lua
> +++ b/test/sql-tap/analyze9.test.lua
> @@ -206,10 +206,10 @@ test:do_execsql_test(
> INSERT INTO t1 VALUES(81, 1, 'one-i');
> INSERT INTO t1 VALUES(91, 1, 'one-j');
> INSERT INTO t1 SELECT a+1,2,'two' || substr(c,4) FROM t1;
> - INSERT INTO t1 SELECT a+2,3,'three'||substr(c,4) FROM t1
> WHERE c GLOB 'one-*';
> - INSERT INTO t1 SELECT a+3,4,'four'||substr(c,4) FROM t1 WHERE
> c GLOB 'one-*';
> - INSERT INTO t1 SELECT a+4,5,'five'||substr(c,4) FROM t1 WHERE
> c GLOB 'one-*';
> - INSERT INTO t1 SELECT a+5,6,'six'||substr(c,4) FROM t1 WHERE
> c GLOB 'one-*';
> + INSERT INTO t1 SELECT a+2,3,'three'||substr(c,4) FROM t1
> WHERE c LIKE 'one-%';
> + INSERT INTO t1 SELECT a+3,4,'four'||substr(c,4) FROM t1 WHERE
> c LIKE 'one-%';
> + INSERT INTO t1 SELECT a+4,5,'five'||substr(c,4) FROM t1 WHERE
> c LIKE 'one-%';
> + INSERT INTO t1 SELECT a+5,6,'six'||substr(c,4) FROM t1 WHERE
> c LIKE 'one-%';
> CREATE INDEX t1b ON t1(b);
> ANALYZE;
> SELECT c FROM t1 WHERE b=3 AND a BETWEEN 30 AND 60;
> diff --git a/test/sql-tap/e_expr.test.lua b/test/sql-tap/e_expr.test.lua
> index 9780d2cf9..0d69e8535 100755
> --- a/test/sql-tap/e_expr.test.lua
> +++ b/test/sql-tap/e_expr.test.lua
> @@ -1,6 +1,6 @@
> #!/usr/bin/env tarantool
> test = require("sqltester")
> -test:plan(10665)
> +test:plan(10647)
> --!./tcltestrunner.lua
> -- 2010 July 16
> @@ -77,10 +77,7 @@ local operations = {
> {"<>", "ne1"},
> {"!=", "ne2"},
> {"IS", "is"},
> --- NOTE: This test needs refactoring after deletion of GLOB &
> ---type restrictions for LIKE. (See #3572)
> -- {"LIKE", "like"},
> --- {"GLOB", "glob"},
> {"AND", "and"},
> {"OR", "or"},
> {"MATCH", "match"},
> @@ -98,12 +95,10 @@ operations = {
> {"+", "-"},
> {"<<", ">>", "&", "|"},
> {"<", "<=", ">", ">="},
> --- NOTE: This test needs refactoring after deletion of GLOB &
> ---type restrictions for LIKE. (See #3572)
> -- Another NOTE: MATCH & REGEXP aren't supported in Tarantool &
> --- are waiting for their hour, don't confuse them
> ---being commented with ticket above.
> - {"=", "==", "!=", "<>"}, --"LIKE", "GLOB"}, --"MATCH", "REGEXP"},
> +-- are waiting for their hour, don't confuse them
> +-- being commented with commenting of "LIKE".
> + {"=", "==", "!=", "<>"}, --"LIKE"}, --"MATCH", "REGEXP"},
> {"AND"},
> {"OR"},
> }
> @@ -128,7 +123,7 @@ end
> -- EVIDENCE-OF: R-15514-65163 SQLite understands the following binary
> -- operators, in order from highest to lowest precedence: || * / % + -
> -- << >> & | < <= > >= = == != <> IS IS
> --- NOT IN LIKE GLOB MATCH REGEXP AND OR
> +-- NOT IN LIKE MATCH REGEXP AND OR
> --
> -- EVIDENCE-OF: R-38759-38789 Operators IS and IS NOT have the same
> -- precedence as =.
> @@ -482,7 +477,6 @@ for _, op in ipairs(oplist) do
> end
> end
> end
> -
> ---------------------------------------------------------------------------
> -- Test the IS and IS NOT operators.
> --
> @@ -1303,11 +1297,11 @@ end
> test:execsql [[
> CREATE TABLE tblname(cname PRIMARY KEY);
> ]]
> +
> local function glob(args)
> return 1
> end
> -box.internal.sql_create_function("GLOB", glob)
> box.internal.sql_create_function("MATCH", glob)
> box.internal.sql_create_function("REGEXP", glob)
> local test_cases12 ={
> @@ -1369,47 +1363,43 @@ local test_cases12 ={
> {47, "EXPR1 LIKE EXPR2"},
> {48, "EXPR1 LIKE EXPR2 ESCAPE EXPR"},
> - {49, "EXPR1 GLOB EXPR2"},
> - {50, "EXPR1 GLOB EXPR2 ESCAPE EXPR"},
> - {51, "EXPR1 REGEXP EXPR2"},
> - {52, "EXPR1 REGEXP EXPR2 ESCAPE EXPR"},
> - {53, "EXPR1 MATCH EXPR2"},
> - {54, "EXPR1 MATCH EXPR2 ESCAPE EXPR"},
> - {55, "EXPR1 NOT LIKE EXPR2"},
> - {56, "EXPR1 NOT LIKE EXPR2 ESCAPE EXPR"},
> - {57, "EXPR1 NOT GLOB EXPR2"},
> - {58, "EXPR1 NOT GLOB EXPR2 ESCAPE EXPR"},
> - {59, "EXPR1 NOT REGEXP EXPR2"},
> - {60, "EXPR1 NOT REGEXP EXPR2 ESCAPE EXPR"},
> - {61, "EXPR1 NOT MATCH EXPR2"},
> - {62, "EXPR1 NOT MATCH EXPR2 ESCAPE EXPR"},
> -
> - {63, "EXPR IS NULL"},
> - {64, "EXPR IS NOT NULL"},
> -
> - {65, "EXPR NOT BETWEEN EXPR1 AND EXPR2"},
> - {66, "EXPR BETWEEN EXPR1 AND EXPR2"},
> -
> - {67, "EXPR NOT IN (SELECT cname FROM tblname)"},
> - {68, "EXPR NOT IN (1)"},
> - {69, "EXPR NOT IN (1, 2, 3)"},
> - {70, "EXPR NOT IN tblname"},
> - {71, "EXPR IN (SELECT cname FROM tblname)"},
> - {72, "EXPR IN (1)"},
> - {73, "EXPR IN (1, 2, 3)"},
> - {74, "EXPR IN tblname"},
> -
> - {75, "EXISTS (SELECT cname FROM tblname)"},
> - {76, "NOT EXISTS (SELECT cname FROM tblname)"},
> -
> - {77, "CASE EXPR WHEN EXPR1 THEN EXPR2 ELSE EXPR END"},
> - {78, "CASE EXPR WHEN EXPR1 THEN EXPR2 END"},
> - {79, "CASE EXPR WHEN EXPR1 THEN EXPR2 WHEN EXPR THEN EXPR1 ELSE
> EXPR2 END"},
> - {80, "CASE EXPR WHEN EXPR1 THEN EXPR2 WHEN EXPR THEN EXPR1 END"},
> - {81, "CASE WHEN EXPR1 THEN EXPR2 ELSE EXPR END"},
> - {82, "CASE WHEN EXPR1 THEN EXPR2 END"},
> - {83, "CASE WHEN EXPR1 THEN EXPR2 WHEN EXPR THEN EXPR1 ELSE EXPR2
> END"},
> - {84, "CASE WHEN EXPR1 THEN EXPR2 WHEN EXPR THEN EXPR1 END"},
> + {49, "EXPR1 REGEXP EXPR2"},
> + {50, "EXPR1 REGEXP EXPR2 ESCAPE EXPR"},
> + {51, "EXPR1 MATCH EXPR2"},
> + {52, "EXPR1 MATCH EXPR2 ESCAPE EXPR"},
> + {53, "EXPR1 NOT LIKE EXPR2"},
> + {54, "EXPR1 NOT LIKE EXPR2 ESCAPE EXPR"},
> + {55, "EXPR1 NOT REGEXP EXPR2"},
> + {56, "EXPR1 NOT REGEXP EXPR2 ESCAPE EXPR"},
> + {57, "EXPR1 NOT MATCH EXPR2"},
> + {58, "EXPR1 NOT MATCH EXPR2 ESCAPE EXPR"},
> +
> + {59, "EXPR IS NULL"},
> + {60, "EXPR IS NOT NULL"},
> +
> + {61, "EXPR NOT BETWEEN EXPR1 AND EXPR2"},
> + {62, "EXPR BETWEEN EXPR1 AND EXPR2"},
> +
> + {63, "EXPR NOT IN (SELECT cname FROM tblname)"},
> + {64, "EXPR NOT IN (1)"},
> + {65, "EXPR NOT IN (1, 2, 3)"},
> + {66, "EXPR NOT IN tblname"},
> + {67, "EXPR IN (SELECT cname FROM tblname)"},
> + {68, "EXPR IN (1)"},
> + {69, "EXPR IN (1, 2, 3)"},
> + {70, "EXPR IN tblname"},
> +
> + {71, "EXISTS (SELECT cname FROM tblname)"},
> + {72, "NOT EXISTS (SELECT cname FROM tblname)"},
> +
> + {73, "CASE EXPR WHEN EXPR1 THEN EXPR2 ELSE EXPR END"},
> + {74, "CASE EXPR WHEN EXPR1 THEN EXPR2 END"},
> + {75, "CASE EXPR WHEN EXPR1 THEN EXPR2 WHEN EXPR THEN EXPR1 ELSE
> EXPR2 END"},
> + {76, "CASE EXPR WHEN EXPR1 THEN EXPR2 WHEN EXPR THEN EXPR1 END"},
> + {77, "CASE WHEN EXPR1 THEN EXPR2 ELSE EXPR END"},
> + {78, "CASE WHEN EXPR1 THEN EXPR2 END"},
> + {79, "CASE WHEN EXPR1 THEN EXPR2 WHEN EXPR THEN EXPR1 ELSE EXPR2
> END"},
> + {80, "CASE WHEN EXPR1 THEN EXPR2 WHEN EXPR THEN EXPR1 END"},
> }
> for _, val in ipairs(test_cases12) do
> @@ -1802,7 +1792,7 @@ test:do_execsql_test(
> })
> ---------------------------------------------------------------------------
> --- Test the statements related to the LIKE and GLOB operators.
> +-- Test the statements related to the LIKE operator.
> --
> -- EVIDENCE-OF: R-16584-60189 The LIKE operator does a pattern matching
> -- comparison.
> @@ -2274,102 +2264,38 @@ test:do_execsql_test(
> -- </e_expr-16.1.7>
> })
> --- EVIDENCE-OF: R-52087-12043 The GLOB operator is similar to LIKE but
> --- uses the Unix file globbing syntax for its wildcards.
> ---
> --- EVIDENCE-OF: R-09813-17279 Also, GLOB is case sensitive, unlike LIKE.
> +-- EVIDENCE-OF: R-39616-20555 LIKE may be preceded by the
> +-- NOT keyword to invert the sense of the test.
> --
> test:do_execsql_test(
> - "e_expr-17.1.1",
> - [[
> - SELECT 'abcxyz' GLOB 'abc%'
> - ]], {
> - -- <e_expr-17.1.1>
> - 0
> - -- </e_expr-17.1.1>
> - })
> -
> -test:do_execsql_test(
> - "e_expr-17.1.2",
> - [[
> - SELECT 'abcxyz' GLOB 'abc*'
> - ]], {
> - -- <e_expr-17.1.2>
> - 1
> - -- </e_expr-17.1.2>
> - })
> -
> -test:do_execsql_test(
> - "e_expr-17.1.3",
> - [[
> - SELECT 'abcxyz' GLOB 'abc___'
> - ]], {
> - -- <e_expr-17.1.3>
> - 0
> - -- </e_expr-17.1.3>
> - })
> -
> -test:do_execsql_test(
> - "e_expr-17.1.4",
> - [[
> - SELECT 'abcxyz' GLOB 'abc???'
> - ]], {
> - -- <e_expr-17.1.4>
> - 1
> - -- </e_expr-17.1.4>
> - })
> -
> -test:do_execsql_test(
> - "e_expr-17.1.5",
> + "e_expr-17.2.0",
> [[
> - SELECT 'abcxyz' GLOB 'abc*'
> + PRAGMA case_sensitive_like = 1;
> + SELECT 'abcxyz' NOT LIKE 'ABC%';
> ]], {
> - -- <e_expr-17.1.5>
> + -- <e_expr-17.2.0>
> 1
> - -- </e_expr-17.1.5>
> - })
> -
> -test:do_execsql_test(
> - "e_expr-17.1.6",
> - [[
> - SELECT 'ABCxyz' GLOB 'abc*'
> - ]], {
> - -- <e_expr-17.1.6>
> - 0
> - -- </e_expr-17.1.6>
> - })
> -
> -test:do_execsql_test(
> - "e_expr-17.1.7",
> - [[
> - SELECT 'abcxyz' GLOB 'ABC*'
> - ]], {
> - -- <e_expr-17.1.7>
> - 0
> - -- </e_expr-17.1.7>
> + -- </e_expr-17.2.0>
> })
> --- EVIDENCE-OF: R-39616-20555 Both GLOB and LIKE may be preceded by the
> --- NOT keyword to invert the sense of the test.
> ---
> test:do_execsql_test(
> "e_expr-17.2.1",
> [[
> - SELECT 'abcxyz' NOT GLOB 'ABC*'
> + SELECT 'abcxyz' NOT LIKE 'abc%'
> ]], {
> -- <e_expr-17.2.1>
> - 1
> + 0
> -- </e_expr-17.2.1>
> })
> test:do_execsql_test(
> "e_expr-17.2.2",
> [[
> - SELECT 'abcxyz' NOT GLOB 'abc*'
> + PRAGMA case_sensitive_like = 0
> ]], {
delete 17.2.1 17.2.2 (it was creaget for glob, like tested below), and move
`PRAGMA case_sensitive_like = 0` out of the test (it is not a test) (use
just test:execsql or box.exequte)
> -- <e_expr-17.2.2>
> - 0
> - -- </e_expr-17.2.2>
> +
> + -- <e_expr-17.2.2>
> })
> test:do_execsql_test(
> @@ -2448,62 +2374,6 @@ if 0>0 then
> db("nullvalue", "")
> end
> --- EVIDENCE-OF: R-39414-35489 The infix GLOB operator is implemented by
> --- calling the function glob(Y,X) and can be modified by overriding that
> --- function.
> -
> -local globargs = {}
> -local function globfunc(...)
> - local args = {...}
> - for i, v in ipairs(args) do
> - table.insert(globargs, v)
> - end
> - return 1
> -end
> -box.internal.sql_create_function("GLOB", globfunc, 2)
> ---db("func", "glob", "-argcount", 2, "globfunc")
Do not delete this test. Rewrite it for like.
> -
> -test:do_execsql_test(
> - "e_expr-17.3.1",
> - [[
> - SELECT 'abc' GLOB 'def'
> - ]], {
> - -- <e_expr-17.3.1>
> - 1
> - -- </e_expr-17.3.1>
> - })
> -
> -test:do_test(
> - "e_expr-17.3.2",
> - function()
> - return globargs
> - end, {
> - -- <e_expr-17.3.2>
> - "def", "abc"
> - -- </e_expr-17.3.2>
> - })
> -
> -globargs = { }
> -test:do_execsql_test(
> - "e_expr-17.3.3",
> - [[
> - SELECT 'X' NOT GLOB 'Y'
> - ]], {
> - -- <e_expr-17.3.3>
> - 0
> - -- </e_expr-17.3.3>
> - })
> -
> -test:do_test(
> - "e_expr-17.3.4",
> - function()
> - return globargs
> - end, {
> - -- <e_expr-17.3.4>
> - "Y", "X"
> - -- </e_expr-17.3.4>
> - })
> -
> --sqlite3("db", "test.db")
> -- EVIDENCE-OF: R-41650-20872 No regexp() user function is defined by
> -- default and so use of the REGEXP operator will normally result in an
> diff --git a/test/sql-tap/gh-3251-string-pattern-comparison.test.lua
> b/test/sql-tap/gh-3251-string-pattern-comparison.test.lua
> index addf0e36d..a6d822ccd 100755
> --- a/test/sql-tap/gh-3251-string-pattern-comparison.test.lua
> +++ b/test/sql-tap/gh-3251-string-pattern-comparison.test.lua
> @@ -142,17 +142,17 @@ for i, tested_string in ipairs(invalid_testcases) do
> local test_name = prefix .. "2." .. tostring(i)
> local test_itself = "SELECT 'abc' LIKE 'ab" .. tested_string .. "';"
> test:do_catchsql_test(test_name, test_itself,
> - {1, "LIKE or GLOB pattern can only contain
> UTF-8 characters"})
> + {1, "LIKE pattern can only contain UTF-8
> characters"})
> test_name = prefix .. "3." .. tostring(i)
> test_itself = "SELECT 'abc' LIKE 'abc" .. tested_string .. "';"
> test:do_catchsql_test(test_name, test_itself,
> - {1, "LIKE or GLOB pattern can only contain
> UTF-8 characters"})
> + {1, "LIKE pattern can only contain UTF-8
> characters"})
> test_name = prefix .. "4." .. tostring(i)
> test_itself = "SELECT 'abc' LIKE 'ab" .. tested_string .. "c';"
> test:do_catchsql_test(test_name, test_itself,
> - {1, "LIKE or GLOB pattern can only contain
> UTF-8 characters"})
> + {1, "LIKE pattern can only contain UTF-8
> characters"})
> -- Just skipping if row value predicand contains invalid character.
> @@ -185,7 +185,7 @@ local valid_testcases = {
> -- Valid testcases.
> for i, tested_string in ipairs(valid_testcases) do
> - test_name = prefix .. "8." .. tostring(i)
> + local test_name = prefix .. "8." .. tostring(i)
> local test_itself = "SELECT 'abc' LIKE 'ab" .. tested_string .. "';"
> test:do_execsql_test(test_name, test_itself, {0})
> diff --git a/test/sql-tap/like2.test.lua b/test/sql-tap/like2.test.lua
> index abcac39fb..c6c81cb4d 100755
> --- a/test/sql-tap/like2.test.lua
> +++ b/test/sql-tap/like2.test.lua
> @@ -12,11 +12,11 @@ test:plan(282)
> -- 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 testing the LIKE and GLOB operators and
> --- in particular the optimizations that occur to help those operators
> --- run faster.
> +-----------------------------------------------------------------
> +-- This file implements regression tests for SQLite library. The
> +-- focus of this file is testing the LIKE operator and
> +-- in particular the optimizations that occur to help this
> +-- operator run faster.
> --
> -- $Id: like2.test,v 1.1 2008/05/26 18:33:41 drh Exp $
> -- ["set","testdir",[["file","dirname",["argv0"]]]]
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.tarantool.org/pipermail/tarantool-patches/attachments/20180911/96332d5e/attachment.html>
More information about the Tarantool-patches
mailing list