diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index c06e3bd..7f93ef6 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -617,13 +617,17 @@ struct compareInfo {
u8 noCase; /* true to ignore case differences */
};
-/*
- * For LIKE and GLOB matching on EBCDIC machines, assume that every
- * character is exactly one byte in size. Also, provde the Utf8Read()
- * macro for fast reading of the next character in the common case where
- * the next character is ASCII.
+/**
+ * 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.
*/
-#define Utf8Read(s, e) ucnv_getNextUChar(pUtf8conv, &s, e, &status)
+#define Utf8Read(s, e) (((s) < (e)) ? \
+ ucnv_getNextUChar(pUtf8conv, &(s), (e), &(status)) : 0)
+
+#define SQL_END_OF_STRING 0
+#define SQL_INVALID_UTF8_SYMBOL 0xfffd
static const struct compareInfo globInfo = { '*', '?', '[', 0 };
@@ -638,19 +642,16 @@ static const struct compareInfo likeInfoNorm = { '%', '_', 0, 1 };
static const struct compareInfo likeInfoAlt = { '%', '_', 0, 0 };
/*
- * Possible error returns from patternMatch()
+ * Possible error returns from sql_utf8_pattern_compare()
*/
#define SQLITE_MATCH 0
#define SQLITE_NOMATCH 1
#define SQLITE_NOWILDCARDMATCH 2
+#define SQL_PROHIBITED_PATTERN 3
-/*
- * Compare two UTF-8 strings for equality where the first string is
- * a GLOB or LIKE expression. Return values:
- *
- * SQLITE_MATCH: Match
- * SQLITE_NOMATCH: No match
- * SQLITE_NOWILDCARDMATCH: No match in spite of having * or % wildcards.
+/**
+ * Compare two UTF-8 strings for equality where the first string
+ * is a GLOB or LIKE expression.
*
* Globbing rules:
*
@@ -663,92 +664,136 @@ static const struct compareInfo likeInfoAlt = { '%', '_', 0, 0 };
*
* [^...] 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.
+ * 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.
*
* Like matching rules:
*
- * '%' Matches any sequence of zero or more characters
+ * '%' Matches any sequence of zero or more characters.
*
- ** '_' Matches any one character
+ ** '_' Matches any one character.
*
* Ec Where E is the "esc" character and c is any other
- * character, including '%', '_', and esc, match exactly c.
+ * character, including '%', '_', and esc, match
+ * exactly c.
*
* The comments within this routine usually assume glob matching.
*
- * This routine is usually quick, but can be N**2 in the worst case.
+ * 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).
+ *
+ * @retval SQLITE_MATCH: Match.
+ * SQLITE_NOMATCH: No match.
+ * SQLITE_NOWILDCARDMATCH: No match in spite of having *
+ * or % wildcards.
+ * SQL_PROHIBITED_PATTERN: Pattern contains invalid
+ * symbol.
*/
static int
-patternCompare(const char * pattern, /* The glob pattern */
- const char * string, /* The string to compare against the glob */
- const struct compareInfo *pInfo, /* Information about how to do the compare */
- UChar32 matchOther /* The escape char (LIKE) or '[' (GLOB) */
- )
+sql_utf8_pattern_compare(const char * pattern,
+ const char * string,
+ const struct compareInfo *pInfo,
+ UChar32 matchOther)
{
- UChar32 c, c2; /* Next pattern and input string chars */
- UChar32 matchOne = pInfo->matchOne; /* "?" or "_" */
- UChar32 matchAll = pInfo->matchAll; /* "*" or "%" */
- UChar32 noCase = pInfo->noCase; /* True if uppercase==lowercase */
- const char *zEscaped = 0; /* One past the last escaped input char */
+ /* 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);
UErrorCode status = U_ZERO_ERROR;
- while (pattern < pattern_end){
- c = Utf8Read(pattern, pattern_end);
+ while ((c = Utf8Read(pattern, pattern_end)) != SQL_END_OF_STRING) {
+ if (c == SQL_INVALID_UTF8_SYMBOL)
+ return SQL_PROHIBITED_PATTERN;
if (c == matchAll) { /* Match "*" */
- /* 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
+ /* 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.
*/
- while (pattern < pattern_end){
- c = Utf8Read(pattern, pattern_end);
+ while ((c = Utf8Read(pattern, pattern_end)) !=
+ SQL_END_OF_STRING) {
+ if (c == SQL_INVALID_UTF8_SYMBOL)
+ return SQL_PROHIBITED_PATTERN;
if (c != matchAll && c != matchOne)
break;
- if (c == matchOne
- && Utf8Read(string, string_end) == 0) {
+ if (c == matchOne &&
+ (c2 = Utf8Read(string, string_end)) ==
+ SQL_END_OF_STRING)
return SQLITE_NOWILDCARDMATCH;
- }
+ if (c2 == SQL_INVALID_UTF8_SYMBOL)
+ return SQLITE_NOMATCH;
}
- /* "*" at the end of the pattern matches */
- if (pattern == pattern_end)
+ /*
+ * "*" at the end of the pattern matches.
+ */
+ if (c == SQL_END_OF_STRING) {
+ while ((c2 = Utf8Read(string, string_end)) !=
+ SQL_END_OF_STRING)
+ if (c2 == SQL_INVALID_UTF8_SYMBOL)
+ return SQLITE_NOMATCH;
return SQLITE_MATCH;
+ }
if (c == matchOther) {
if (pInfo->matchSet == 0) {
c = Utf8Read(pattern, pattern_end);
- if (c == 0)
+ if (c == SQL_INVALID_UTF8_SYMBOL)
+ return SQL_PROHIBITED_PATTERN;
+ if (c == SQL_END_OF_STRING)
return SQLITE_NOWILDCARDMATCH;
} else {
- /* "[...]" immediately follows the "*". We have to do a slow
- * recursive search in this case, but it is an unusual case.
+ /* "[...]" immediately
+ * follows the "*". We
+ * have to do a slow
+ * recursive search in
+ * this case, but it is
+ * an unusual case.
*/
- assert(matchOther < 0x80); /* '[' is a single-byte character */
+ assert(matchOther < 0x80);
while (string < string_end) {
int bMatch =
- patternCompare(&pattern[-1],
- string,
- pInfo,
- matchOther);
+ sql_utf8_pattern_compare(
+ &pattern[-1],
+ string,
+ pInfo,
+ matchOther);
if (bMatch != SQLITE_NOMATCH)
return bMatch;
- Utf8Read(string, string_end);
+ c = Utf8Read(string, string_end);
+ if (c == SQL_INVALID_UTF8_SYMBOL)
+ return SQLITE_NOMATCH;
}
return SQLITE_NOWILDCARDMATCH;
}
}
- /* At this point variable c contains the first character of the
- * pattern string past the "*". Search in the input string for the
- * first matching character and recursively continue the match from
- * that point.
+ /* At this point variable c contains the
+ * first character of the pattern string
+ * past the "*". Search in the input
+ * string for the first matching
+ * character and recursively continue the
+ * match from that point.
*
- * For a case-insensitive search, set variable cx to be the same as
- * c but in the other case and search the input string for either
- * c or cx.
+ * For a case-insensitive search, set
+ * variable cx to be the same as c but in
+ * the other case and search the input
+ * string for either c or cx.
*/
int bMatch;
@@ -756,14 +801,18 @@ patternCompare(const char * pattern, /* The glob pattern */
c = u_tolower(c);
while (string < string_end){
/**
- * This loop could have been implemented
- * without if converting c2 to lower case
- * (by holding c_upper and c_lower), however
- * it is implemented this way because lower
- * works better with German and Turkish
- * languages.
+ * This loop could have been
+ * implemented without if
+ * converting c2 to lower case
+ * by holding c_upper and
+ * c_lower,however it is
+ * implemented this way because
+ * lower works better with German
+ * and Turkish languages.
*/
c2 = Utf8Read(string, string_end);
+ if (c2 == SQL_INVALID_UTF8_SYMBOL)
+ return SQLITE_NOMATCH;
if (!noCase) {
if (c2 != c)
continue;
@@ -771,9 +820,10 @@ patternCompare(const char * pattern, /* The glob pattern */
if (c2 != c && u_tolower(c2) != c)
continue;
}
- bMatch =
- patternCompare(pattern, string,
- pInfo, matchOther);
+ bMatch = sql_utf8_pattern_compare(pattern,
+ string,
+ pInfo,
+ matchOther);
if (bMatch != SQLITE_NOMATCH)
return bMatch;
}
@@ -782,7 +832,9 @@ patternCompare(const char * pattern, /* The glob pattern */
if (c == matchOther) {
if (pInfo->matchSet == 0) {
c = Utf8Read(pattern, pattern_end);
- if (c == 0)
+ if (c == SQL_INVALID_UTF8_SYMBOL)
+ return SQL_PROHIBITED_PATTERN;
+ if (c == SQL_END_OF_STRING)
return SQLITE_NOMATCH;
zEscaped = pattern;
} else {
@@ -790,23 +842,33 @@ patternCompare(const char * pattern, /* The glob pattern */
int seen = 0;
int invert = 0;
c = Utf8Read(string, string_end);
+ if (c == SQL_INVALID_UTF8_SYMBOL)
+ return SQLITE_NOMATCH;
if (string == string_end)
return SQLITE_NOMATCH;
c2 = Utf8Read(pattern, pattern_end);
+ if (c2 == SQL_INVALID_UTF8_SYMBOL)
+ return SQL_PROHIBITED_PATTERN;
if (c2 == '^') {
invert = 1;
c2 = Utf8Read(pattern, pattern_end);
+ if (c2 == SQL_INVALID_UTF8_SYMBOL)
+ return SQL_PROHIBITED_PATTERN;
}
if (c2 == ']') {
if (c == ']')
seen = 1;
c2 = Utf8Read(pattern, pattern_end);
+ if (c2 == SQL_INVALID_UTF8_SYMBOL)
+ return SQL_PROHIBITED_PATTERN;
}
- while (c2 && c2 != ']') {
+ 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_PROHIBITED_PATTERN;
if (c >= prior_c && c <= c2)
seen = 1;
prior_c = 0;
@@ -817,29 +879,36 @@ patternCompare(const char * pattern, /* The glob pattern */
prior_c = c2;
}
c2 = Utf8Read(pattern, pattern_end);
+ if (c2 == SQL_INVALID_UTF8_SYMBOL)
+ return SQL_PROHIBITED_PATTERN;
}
- if (pattern == pattern_end || (seen ^ invert) == 0) {
+ if (pattern == pattern_end ||
+ (seen ^ invert) == 0) {
return SQLITE_NOMATCH;
}
continue;
}
}
c2 = Utf8Read(string, string_end);
+ if (c2 == SQL_INVALID_UTF8_SYMBOL)
+ return SQLITE_NOMATCH;
if (c == c2)
continue;
if (noCase){
/**
- * Small optimisation. Reduce number of calls
- * to u_tolower function.
- * SQL standards suggest use to_upper for symbol
- * normalisation. However, using to_lower allows to
- * respect Turkish 'İ' in default locale.
+ * Small optimisation. Reduce number of
+ * calls to u_tolower function. SQL
+ * standards suggest use to_upper for
+ * symbol normalisation. However, using
+ * to_lower allows to respect Turkish 'İ'
+ * in default locale.
*/
if (u_tolower(c) == c2 ||
c == u_tolower(c2))
continue;
}
- if (c == matchOne && pattern != zEscaped && c2 != 0)
+ if (c == matchOne && pattern != zEscaped &&
+ c2 != SQL_END_OF_STRING)
continue;
return SQLITE_NOMATCH;
}
@@ -853,8 +922,7 @@ patternCompare(const char * pattern, /* The glob pattern */
int
sqlite3_strglob(const char *zGlobPattern, const char *zString)
{
- return patternCompare(zGlobPattern, zString, &globInfo,
- '[');
+ return sql_utf8_pattern_compare(zGlobPattern, zString, &globInfo, '[');
}
/*
@@ -864,7 +932,7 @@ sqlite3_strglob(const char *zGlobPattern, const char *zString)
int
sqlite3_strlike(const char *zPattern, const char *zStr, unsigned int esc)
{
- return patternCompare(zPattern, zStr, &likeInfoNorm, esc);
+ return sql_utf8_pattern_compare(zPattern, zStr, &likeInfoNorm, esc);
}
/*
@@ -910,8 +978,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 patternCompare().
+ /* Limit the length of the LIKE or GLOB pattern to avoid
+ * problems of deep recursion and N*N behavior in
+ * sql_utf8_pattern_compare().
*/
nPat = sqlite3_value_bytes(argv[0]);
testcase(nPat == db->aLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]);
@@ -947,7 +1016,12 @@ likeFunc(sqlite3_context * context, int argc, sqlite3_value ** argv)
sqlite3_like_count++;
#endif
int res;
- res = patternCompare(zB, zA, pInfo, escape);
+ res = sql_utf8_pattern_compare(zB, zA, pInfo, escape);
+ if (res == SQL_PROHIBITED_PATTERN) {
+ sqlite3_result_error(context, "LIKE or GLOB pattern can only"
+ " contain UTF-8 characters", -1);
+ return;
+ }
sqlite3_result_int(context, res == SQLITE_MATCH);
}
diff --git a/test-run b/test-run
index 77e9327..95562e9 160000
--- a/test-run
+++ b/test-run
@@ -1 +1 @@
-Subproject commit 77e93279210f8c5c1fd0ed03416fa19a184f0b6d
+Subproject commit 95562e95401fef4e0b755ab0bb430974b5d1a29a
diff --git a/test/sql-tap/e_expr.test.lua b/test/sql-tap/e_expr.test.lua
index 13d3a96..9780d2c 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(12431)
+test:plan(10665)
--!./tcltestrunner.lua
-- 2010 July 16
@@ -77,8 +77,10 @@ local operations = {
{"<>", "ne1"},
{"!=", "ne2"},
{"IS", "is"},
- {"LIKE", "like"},
- {"GLOB", "glob"},
+-- 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"},
@@ -96,7 +98,12 @@ operations = {
{"+", "-"},
{"<<", ">>", "&", "|"},
{"<", "<=", ">", ">="},
- {"=", "==", "!=", "<>", "LIKE", "GLOB"}, --"MATCH", "REGEXP"},
+-- 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"},
{"AND"},
{"OR"},
}
@@ -475,6 +482,7 @@ for _, op in ipairs(oplist) do
end
end
end
+
---------------------------------------------------------------------------
-- Test the IS and IS NOT operators.
--
diff --git a/test/sql-tap/gh-3251-string-pattern-comparison.test.lua b/test/sql-tap/gh-3251-string-pattern-comparison.test.lua
new file mode 100755
index 0000000..2a787f2
--- /dev/null
+++ b/test/sql-tap/gh-3251-string-pattern-comparison.test.lua
@@ -0,0 +1,213 @@
+#!/usr/bin/env tarantool
+test = require("sqltester")
+test:plan(128)
+
+local prefix = "like-test-"
+
+-- Unicode byte sequences.
+local valid_testcases = {
+ '\x01',
+ '\x09',
+ '\x1F',
+ '\x7F',
+ '\xC2\x80',
+ '\xC2\x90',
+ '\xC2\x9F',
+ '\xE2\x80\xA8',
+ '\x20\x0B',
+ '\xE2\x80\xA9',
+}
+
+-- Non-Unicode byte sequences.
+local invalid_testcases = {
+ '\xE2\x80',
+ '\xFE\xFF',
+ '\xC2',
+ '\xED\xB0\x80',
+ '\xD0',
+}
+
+local like_test_cases =
+{
+ {"1.1",
+ "SELECT 'AB' LIKE '_B';",
+ {0, {1}} },
+ {"1.2",
+ "SELECT 'CD' LIKE '_B';",
+ {0, {0}} },
+ {"1.3",
+ "SELECT '' LIKE '_B';",
+ {0, {0}} },
+ {"1.4",
+ "SELECT 'AB' LIKE '%B';",
+ {0, {1}} },
+ {"1.5",
+ "SELECT 'CD' LIKE '%B';",
+ {0, {0}} },
+ {"1.6",
+ "SELECT '' LIKE '%B';",
+ {0, {0}} },
+ {"1.7",
+ "SELECT 'AB' LIKE 'A__';",
+ {0, {0}} },
+ {"1.8",
+ "SELECT 'CD' LIKE 'A__';",
+ {0, {0}} },
+ {"1.9",
+ "SELECT '' LIKE 'A__';",
+ {0, {0}} },
+ {"1.10",
+ "SELECT 'AB' LIKE 'A_';",
+ {0, {1}} },
+ {"1.11",
+ "SELECT 'CD' LIKE 'A_';",
+ {0, {0}} },
+ {"1.12",
+ "SELECT '' LIKE 'A_';",
+ {0, {0}} },
+ {"1.13",
+ "SELECT 'AB' LIKE 'A';",
+ {0, {0}} },
+ {"1.14",
+ "SELECT 'CD' LIKE 'A';",
+ {0, {0}} },
+ {"1.15",
+ "SELECT '' LIKE 'A';",
+ {0, {0}} },
+ {"1.16",
+ "SELECT 'AB' LIKE '_';",
+ {0, {0}} },
+ {"1.17",
+ "SELECT 'CD' LIKE '_';",
+ {0, {0}} },
+ {"1.18",
+ "SELECT '' LIKE '_';",
+ {0, {0}} },
+ {"1.19",
+ "SELECT 'AB' LIKE '__';",
+ {0, {1}} },
+ {"1.20",
+ "SELECT 'CD' LIKE '__';",
+ {0, {1}} },
+ {"1.21",
+ "SELECT '' LIKE '__';",
+ {0, {0}} },
+ {"1.22",
+ "SELECT 'AB' LIKE '%A';",
+ {0, {0}} },
+ {"1.23",
+ "SELECT 'AB' LIKE '%C';",
+ {0, {0}} },
+ {"1.24",
+ "SELECT 'ab' LIKE '%df';",
+ {0, {0}} },
+ {"1.25",
+ "SELECT 'abCDF' LIKE '%df';",
+ {0, {1}} },
+ {"1.26",
+ "SELECT 'CDF' LIKE '%df';",
+ {0, {1}} },
+ {"1.27",
+ "SELECT 'ab' LIKE 'a_';",
+ {0, {1}} },
+ {"1.28",
+ "SELECT 'abCDF' LIKE 'a_';",
+ {0, {0}} },
+ {"1.29",
+ "SELECT 'CDF' LIKE 'a_';",
+ {0, {0}} },
+ {"1.30",
+ "SELECT 'ab' LIKE 'ab%';",
+ {0, {1}} },
+ {"1.31",
+ "SELECT 'abCDF' LIKE 'ab%';",
+ {0, {1}} },
+ {"1.32",
+ "SELECT 'CDF' LIKE 'ab%';",
+ {0, {0}} },
+ {"1.33",
+ "SELECT 'ab' LIKE 'abC%';",
+ {0, {0}} },
+ {"1.34",
+ "SELECT 'abCDF' LIKE 'abC%';",
+ {0, {1}} },
+ {"1.35",
+ "SELECT 'CDF' LIKE 'abC%';",
+ {0, {0}} },
+ {"1.36",
+ "SELECT 'ab' LIKE 'a_%';",
+ {0, {1}} },
+ {"1.37",
+ "SELECT 'abCDF' LIKE 'a_%';",
+ {0, {1}} },
+ {"1.38",
+ "SELECT 'CDF' LIKE 'a_%';",
+ {0, {0}} },
+}
+
+test:do_catchsql_set_test(like_test_cases, prefix)
+
+-- Invalid testcases.
+for i, tested_string in ipairs(invalid_testcases) do
+
+ -- We should raise an error in case
+ -- pattern contains invalid characters.
+
+ 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"})
+
+ 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"})
+
+ 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"})
+
+ -- Just skipping if row value predicand contains invalid character.
+
+ test_name = prefix .. "5." .. tostring(i)
+ test_itself = "SELECT 'ab" .. tested_string .. "' LIKE 'abc';"
+ test:do_execsql_test(test_name, test_itself, {0})
+
+ test_name = prefix .. "6." .. tostring(i)
+ test_itself = "SELECT 'abc" .. tested_string .. "' LIKE 'abc';"
+ test:do_execsql_test(test_name, test_itself, {0})
+
+ test_name = prefix .. "7." .. tostring(i)
+ test_itself = "SELECT 'ab" .. tested_string .. "c' LIKE 'abc';"
+ test:do_execsql_test(test_name, test_itself, {0})
+end
+
+-- Valid testcases.
+for i, tested_string in ipairs(valid_testcases) do
+ test_name = prefix .. "8." .. tostring(i)
+ local test_itself = "SELECT 'abc' LIKE 'ab" .. tested_string .. "';"
+ test:do_execsql_test(test_name, test_itself, {0})
+
+ test_name = prefix .. "9." .. tostring(i)
+ test_itself = "SELECT 'abc' LIKE 'abc" .. tested_string .. "';"
+ test:do_execsql_test(test_name, test_itself, {0})
+
+ test_name = prefix .. "10." .. tostring(i)
+ test_itself = "SELECT 'abc' LIKE 'ab" .. tested_string .. "c';"
+ test:do_execsql_test(test_name, test_itself, {0})
+
+ test_name = prefix .. "11." .. tostring(i)
+ test_itself = "SELECT 'ab" .. tested_string .. "' LIKE 'abc';"
+ test:do_execsql_test(test_name, test_itself, {0})
+
+ test_name = prefix .. "12." .. tostring(i)
+ test_itself = "SELECT 'abc" .. tested_string .. "' LIKE 'abc';"
+ test:do_execsql_test(test_name, test_itself, {0})
+
+ test_name = prefix .. "13." .. tostring(i)
+ test_itself = "SELECT 'ab" .. tested_string .. "c' LIKE 'abc';"
+ test:do_execsql_test(test_name, test_itself, {0})
+end
+
+test:finish_test()