From: Alex Khatskevich <avkhatskevich@tarantool.org> To: Nikita Tatunov <n.tatunov@tarantool.org> Cc: tarantool-patches@freelists.org, Alexander Turenko <alexander.turenko@tarantool.org> Subject: [tarantool-patches] Re: [PATCH 2/2] sql: remove GLOB from Tarantool Date: Tue, 11 Sep 2018 15:03:45 +0300 [thread overview] Message-ID: <3f8354cb-4a47-c3de-164b-c776c792b991@tarantool.org> (raw) In-Reply-To: <32D1E5EA-EA21-4E4B-B5F5-80B6578BFBED@tarantool.org> [-- Attachment #1: Type: text/plain, Size: 54451 bytes --] >>>>> >>>>> - "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"]]]] [-- Attachment #2: Type: text/html, Size: 147246 bytes --]
next prev parent reply other threads:[~2018-09-11 12:03 UTC|newest] Thread overview: 46+ messages / expand[flat|nested] mbox.gz Atom feed top 2018-08-16 17:00 [tarantool-patches] [PATCH v2 0/2] sql: pattern comparison fixes & GLOB removal N.Tatunov 2018-08-16 17:00 ` [tarantool-patches] [PATCH 1/2] sql: LIKE & GLOB pattern comparison issue N.Tatunov 2018-08-17 9:23 ` [tarantool-patches] " Alex Khatskevich 2018-08-17 11:17 ` Alexander Turenko 2018-08-17 11:42 ` Alex Khatskevich 2018-09-09 13:33 ` Nikita Tatunov 2018-09-10 22:20 ` Alex Khatskevich 2018-09-11 6:06 ` Nikita Tatunov 2018-09-11 10:06 ` Alex Khatskevich 2018-09-11 13:31 ` Nikita Tatunov 2018-10-18 18:02 ` Nikita Tatunov 2018-10-21 3:51 ` Alexander Turenko 2018-10-26 15:19 ` Nikita Tatunov 2018-10-29 13:01 ` Alexander Turenko 2018-10-31 5:25 ` Nikita Tatunov 2018-11-01 10:30 ` Alexander Turenko 2018-11-14 14:16 ` n.pettik 2018-11-14 17:06 ` Alexander Turenko 2018-08-16 17:00 ` [tarantool-patches] [PATCH 2/2] sql: remove GLOB from Tarantool N.Tatunov 2018-08-17 8:25 ` [tarantool-patches] " Alex Khatskevich 2018-08-17 8:49 ` n.pettik 2018-08-17 9:01 ` Alex Khatskevich 2018-08-17 9:20 ` n.pettik 2018-08-17 9:28 ` Alex Khatskevich [not found] ` <04D02794-07A5-4146-9144-84EE720C8656@corp.mail.ru> 2018-08-17 8:53 ` Alex Khatskevich 2018-08-17 11:26 ` Alexander Turenko 2018-08-17 11:34 ` Alexander Turenko 2018-08-17 13:46 ` Nikita Tatunov 2018-09-09 14:57 ` Nikita Tatunov 2018-09-10 22:06 ` Alex Khatskevich 2018-09-11 7:38 ` Nikita Tatunov 2018-09-11 10:11 ` Alexander Turenko 2018-09-11 10:22 ` Alex Khatskevich 2018-09-11 12:03 ` Alex Khatskevich [this message] 2018-10-18 20:28 ` Nikita Tatunov 2018-10-21 3:48 ` Alexander Turenko 2018-10-26 15:21 ` Nikita Tatunov 2018-10-29 12:15 ` Alexander Turenko 2018-11-08 15:09 ` Nikita Tatunov 2018-11-09 12:18 ` Alexander Turenko 2018-11-10 3:38 ` Nikita Tatunov 2018-11-13 19:23 ` Alexander Turenko 2018-11-14 14:16 ` n.pettik 2018-11-14 17:41 ` Alexander Turenko 2018-11-14 21:48 ` n.pettik 2018-11-15 4:57 ` [tarantool-patches] Re: [PATCH v2 0/2] sql: pattern comparison fixes & GLOB removal Kirill Yukhin
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=3f8354cb-4a47-c3de-164b-c776c792b991@tarantool.org \ --to=avkhatskevich@tarantool.org \ --cc=alexander.turenko@tarantool.org \ --cc=n.tatunov@tarantool.org \ --cc=tarantool-patches@freelists.org \ --subject='[tarantool-patches] Re: [PATCH 2/2] sql: remove GLOB from Tarantool' \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox