<div dir="ltr"><br><br><div class="gmail_quote"><div dir="ltr">ср, 1 авг. 2018 г. в 16:56, Alex Khatskevich <<a href="mailto:avkhatskevich@tarantool.org">avkhatskevich@tarantool.org</a>>:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
  
    
  
  <div bgcolor="#FFFFFF">
    <p><br>
    </p>
    <br>
    <div class="gmail-m_-4474003912722141488moz-cite-prefix">On 01.08.2018 13:51, Nikita Tatunov
      wrote:<br>
    </div>
    <blockquote type="cite">
      
      <div dir="ltr">
        <div>diff --git a/src/box/sql/func.c b/src/box/sql/func.c</div>
        <div>index c06e3bd..7f93ef6 100644</div>
        <div>--- a/src/box/sql/func.c</div>
        <div>+++ b/src/box/sql/func.c</div>
        <div>@@ -617,13 +617,17 @@ struct compareInfo {</div>
        <div> <span style="white-space:pre-wrap">        </span>u8 noCase;<span style="white-space:pre-wrap">              </span>/*
          true to ignore case differences */</div>
        <div> };</div>
        <div> </div>
        <div>-/*</div>
        <div>- * For LIKE and GLOB matching on EBCDIC machines, assume
          that every</div>
        <div>- * character is exactly one byte in size.  Also, provde
          the Utf8Read()</div>
        <div>- * macro for fast reading of the next character in the
          common case where</div>
        <div>- * the next character is ASCII.</div>
        <div>+/**</div>
        <div>+ * Providing there are symbols in string s this</div>
        <div>+ * macro returns UTF-8 code of character and</div>
        <div>+ * promotes pointer to the next symbol in the string.</div>
        <div>+ * Otherwise return code is SQL_END_OF_STRING.</div>
        <div>  */</div>
        <div>-#define Utf8Read(s, e)    ucnv_getNextUChar(pUtf8conv,
          &s, e, &status)</div>
        <div>+#define Utf8Read(s, e) (((s) < (e)) ? \</div>
        <div>+<span style="white-space:pre-wrap"> </span>ucnv_getNextUChar(pUtf8conv,
          &(s), (e), &(status)) : 0)</div>
      </div>
    </blockquote>
    [Later I will ask you to return this macro back, so, you may not do
    this]<br>
    As I understand, you are returning `0` from Utf8Read in case of end
    of the string.<br>
    Let's return `SQL_END_OF_STRING` instead of just `0`.<br></div></blockquote><div><br></div><div>Yeah, thank you, didn't notice it. <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div bgcolor="#FFFFFF">
    <blockquote type="cite">
      <div dir="ltr">
        <div>+</div>
        <div>+#define SQL_END_OF_STRING        0</div>
        <div>+#define SQL_INVALID_UTF8_SYMBOL  0xfffd</div>
        <div> </div>
        <div> static const struct compareInfo globInfo = { '*', '?',
          '[', 0 };</div>
        <div> </div>
        <div>@@ -638,19 +642,16 @@ static const struct compareInfo
          likeInfoNorm = { '%', '_', 0, 1 };</div>
        <div> static const struct compareInfo likeInfoAlt = { '%', '_',
          0, 0 };</div>
        <div> </div>
        <div> /*</div>
        <div>- * Possible error returns from patternMatch()</div>
        <div>+ * Possible error returns from sql_utf8_pattern_compare()</div>
        <div>  */</div>
        <div> #define SQLITE_MATCH             0</div>
        <div> #define SQLITE_NOMATCH           1</div>
        <div> #define SQLITE_NOWILDCARDMATCH   2</div>
        <div>+#define SQL_PROHIBITED_PATTERN   3</div>
      </div>
    </blockquote>
    I am not sure that the invalid (with invalid symbols) pattern can be
    called `prohibited`.<br>
    Rename somehow? My proposal: SQL_INVALID_PATTERN.<br></div></blockquote><div><br></div><div>Probably you're right, I was also thinking of changing it somehow.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div bgcolor="#FFFFFF">
    Moreover, You have named this definition with the `SQL` prefix,
    which is good, however,<br>
    similar definitions are still prefixed with `SQLITE`. I would like
    you to rename those in<br>
    this (preferred) or in a separate commit for consistency.
    <blockquote type="cite">
      <div dir="ltr">
        <div> </div></div></blockquote></div></blockquote><div>Tarantool != SQLite and I think we should get away from this approach.</div><div>The thing that names haven't been refactored yet isn't in my control.</div><div>You can ask Nikita's opinion on it, I guess he will tell you almost the same.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div bgcolor="#FFFFFF"><blockquote type="cite"><div dir="ltr">
        <div>-/*</div>
        <div>- * Compare two UTF-8 strings for equality where the first
          string is</div>
        <div>- * a GLOB or LIKE expression.  Return values:</div>
        <div>- *</div>
        <div>- *    SQLITE_MATCH:            Match</div>
        <div>- *    SQLITE_NOMATCH:          No match</div>
        <div>- *    SQLITE_NOWILDCARDMATCH:  No match in spite of having
          * or % wildcards.</div>
        <div>+/**</div>
        <div>+ * Compare two UTF-8 strings for equality where the first
          string</div>
        <div>+ * is a GLOB or LIKE expression.</div>
        <div>  *</div>
        <div>  * Globbing rules:</div>
        <div>  *</div>
        <div>@@ -663,92 +664,136 @@ static const struct compareInfo
          likeInfoAlt = { '%', '_', 0, 0 };</div>
        <div>  *</div>
        <div>  *     [^...]     Matches one character not in the
          enclosed list.</div>
        <div>  *</div>
        <div>- * With the [...] and [^...] matching, a ']' character can
          be included</div>
        <div>- * in the list by making it the first character after '['
          or '^'.  A</div>
        <div>- * range of characters can be specified using '-'. 
          Example:</div>
        <div>- * "[a-z]" matches any single lower-case letter.  To match
          a '-', make</div>
        <div>- * it the last character in the list.</div>
        <div>+ * With the [...] and [^...] matching, a ']' character can
          be</div>
        <div>+ * included in the list by making it the first character
          after</div>
        <div>+ * '[' or '^'. A range of characters can be specified
          using '-'.</div>
        <div>+ * Example: "[a-z]" matches any single lower-case letter.</div>
        <div>+ * To match a '-', make it the last character in the list.</div>
      </div>
    </blockquote>
    Does it work for UTF characters? I suppose no.<br>
    Let's write about it here + let's file an issue to make it<br>
    work with UTF characters.</div></blockquote><div><br></div><div>Soon this function is gonna be refactored & GLOB is gonna be removed anyways.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div bgcolor="#FFFFFF">
    <blockquote type="cite">
      <div dir="ltr">
        <div>  *</div>
        <div>  * Like matching rules:</div>
        <div>  *</div>
        <div>- *      '%'       Matches any sequence of zero or more
          characters</div>
        <div>+ *      '%'       Matches any sequence of zero or more
          characters.</div>
        <div>  *</div>
        <div>- **     '_'       Matches any one character</div>
        <div>+ **     '_'       Matches any one character.</div>
        <div>  *</div>
        <div>  *      Ec        Where E is the "esc" character and c is
          any other</div>
        <div>- *                character, including '%', '_', and esc,
          match exactly c.</div>
        <div>+ *                character, including '%', '_', and esc,
          match</div>
        <div>+ *                exactly c.</div>
        <div>  *</div>
        <div>  * The comments within this routine usually assume glob
          matching.</div>
        <div>  *</div>
        <div>- * This routine is usually quick, but can be N**2 in the
          worst case.</div>
        <div>+ * This routine is usually quick, but can be N**2 in the
          worst</div>
        <div>+ * case.</div>
        <div>+ *</div>
        <div>+ * @param pattern String containing comparison pattern.</div>
        <div>+ * @param string String being compared.</div>
        <div>+ * @param compareInfo Information about how to compare.</div>
        <div>+ * @param matchOther The escape char (LIKE) or '[' (GLOB).</div>
        <div>+ *</div>
        <div>+ * @retval SQLITE_MATCH:            Match.</div>
        <div>+ *<span style="white-space:pre-wrap">       </span> 
           SQLITE_NOMATCH:          No match.</div>
        <div>+ *<span style="white-space:pre-wrap">       </span> 
           SQLITE_NOWILDCARDMATCH:  No match in spite of having *</div>
        <div>+ *<span style="white-space:pre-wrap">                               </span>    or %
          wildcards.</div>
        <div>+ *<span style="white-space:pre-wrap">       </span> 
           SQL_PROHIBITED_PATTERN:  Pattern contains invalid</div>
        <div>+ *<span style="white-space:pre-wrap">                               </span>    symbol.</div>
      </div>
    </blockquote>
    Minor: It is not very good that you use symbol and character
    interchangeably.<br>
    I suppose that `character` should be used everywhere.</div></blockquote><div><br></div><div>They're synonyms, aren't they? </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div bgcolor="#FFFFFF">
    <blockquote type="cite">
      <div dir="ltr">
        <div>  */</div>
        <div> static int</div>
        <div>-patternCompare(const char * pattern,<span style="white-space:pre-wrap">     </span>/*
          The glob pattern */</div>
        <div>-<span style="white-space:pre-wrap"> </span>       const char *
          string,<span style="white-space:pre-wrap">    </span>/* The string to
          compare against the glob */</div>
        <div>-<span style="white-space:pre-wrap"> </span>       const struct
          compareInfo *pInfo,<span style="white-space:pre-wrap">        </span>/*
          Information about how to do the compare */</div>
        <div>-<span style="white-space:pre-wrap"> </span>       UChar32
          matchOther<span style="white-space:pre-wrap"> </span>/* The escape
          char (LIKE) or '[' (GLOB) */</div>
        <div>-    )</div>
        <div>+sql_utf8_pattern_compare(const char * pattern,</div>
        <div>+<span style="white-space:pre-wrap">                 </span> const char *
          string,</div>
        <div>+<span style="white-space:pre-wrap">                 </span> const struct
          compareInfo *pInfo,</div>
        <div>+<span style="white-space:pre-wrap">                 </span> UChar32
          matchOther)</div>
      </div>
    </blockquote>
    "star" sign should stick to the attribute  name.<br>
<a class="gmail-m_-4474003912722141488moz-txt-link-freetext" href="https://tarantool.io/en/doc/1.9/dev_guide/c_style_guide/#chapter-3-1-spaces" target="_blank">https://tarantool.io/en/doc/1.9/dev_guide/c_style_guide/#chapter-3-1-spaces</a><br>
    <br>
    To prevent such typos in the future, you can use special perl<br>
    script which checks coding style in Linux kernel patches.</div></blockquote><div><br></div><div>Subject of the ticket wasn't about refactoring function, but thnx, changed it.</div><div>REMEMBER THIS POINT #1 </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div bgcolor="#FFFFFF">
    <blockquote type="cite">
      <div dir="ltr">
        <div> {</div>
        <div>-<span style="white-space:pre-wrap"> </span>UChar32 c, c2;<span style="white-space:pre-wrap">          </span>/*
          Next pattern and input string chars */</div>
        <div>-<span style="white-space:pre-wrap"> </span>UChar32 matchOne =
          pInfo->matchOne;<span style="white-space:pre-wrap">        </span>/*
          "?" or "_" */</div>
        <div>-<span style="white-space:pre-wrap"> </span>UChar32 matchAll =
          pInfo->matchAll;<span style="white-space:pre-wrap">        </span>/*
          "*" or "%" */</div>
        <div>-<span style="white-space:pre-wrap"> </span>UChar32 noCase =
          pInfo->noCase;<span style="white-space:pre-wrap">  </span>/* True
          if uppercase==lowercase */</div>
        <div>-<span style="white-space:pre-wrap"> </span>const char *zEscaped
          = 0;<span style="white-space:pre-wrap">       </span>/* One past the last
          escaped input char */</div>
        <div>+<span style="white-space:pre-wrap"> </span>/* Next pattern and
          input string chars */</div>
        <div>+<span style="white-space:pre-wrap"> </span>UChar32 c, c2;</div>
        <div>+<span style="white-space:pre-wrap"> </span>/* "?" or "_" */</div>
        <div>+<span style="white-space:pre-wrap"> </span>UChar32 matchOne =
          pInfo->matchOne;</div>
        <div>+<span style="white-space:pre-wrap"> </span>/* "*" or "%" */</div>
        <div>+<span style="white-space:pre-wrap"> </span>UChar32 matchAll =
          pInfo->matchAll;</div>
        <div>+<span style="white-space:pre-wrap"> </span>/* True if
          uppercase==lowercase */</div>
        <div>+<span style="white-space:pre-wrap"> </span>UChar32 noCase =
          pInfo->noCase;</div>
        <div>+<span style="white-space:pre-wrap"> </span>/* One past the last
          escaped input char */</div>
        <div>+<span style="white-space:pre-wrap"> </span>const char *zEscaped
          = 0;</div>
        <div> <span style="white-space:pre-wrap">        </span>const char *
          pattern_end = pattern + strlen(pattern);</div>
        <div> <span style="white-space:pre-wrap">        </span>const char *
          string_end = string + strlen(string);</div>
        <div> <span style="white-space:pre-wrap">        </span>UErrorCode status =
          U_ZERO_ERROR;</div>
        <div> </div>
        <div>-<span style="white-space:pre-wrap"> </span>while (pattern <
          pattern_end){</div>
        <div>-<span style="white-space:pre-wrap">         </span>c =
          Utf8Read(pattern, pattern_end);</div>
        <div>+<span style="white-space:pre-wrap"> </span>while ((c =
          Utf8Read(pattern, pattern_end)) != SQL_END_OF_STRING) {</div>
      </div>
    </blockquote>
    REMEMBER THIS POINT #1<br>
    <blockquote type="cite">
      <div dir="ltr">
        <div>+<span style="white-space:pre-wrap">         </span>if (c ==
          SQL_INVALID_UTF8_SYMBOL)</div>
        <div>+<span style="white-space:pre-wrap">                 </span>return
          SQL_PROHIBITED_PATTERN;</div>
        <div> <span style="white-space:pre-wrap">                </span>if (c == matchAll)
          {<span style="white-space:pre-wrap">  </span>/* Match "*" */</div>
        <div>-<span style="white-space:pre-wrap">                 </span>/* Skip over
          multiple "*" characters in the pattern.  If there</div>
        <div>-<span style="white-space:pre-wrap">                 </span> * are also "?"
          characters, skip those as well, but consume a</div>
        <div>-<span style="white-space:pre-wrap">                 </span> * single
          character of the input string for each "?" skipped</div>
        <div>+<span style="white-space:pre-wrap">                 </span>/* Skip over
          multiple "*" characters in</div>
        <div>+<span style="white-space:pre-wrap">                 </span> * the pattern. If
          there are also "?"</div>
        <div>+<span style="white-space:pre-wrap">                 </span> * characters,
          skip those as well, but</div>
        <div>+<span style="white-space:pre-wrap">                 </span> * consume a
          single character of the</div>
        <div>+<span style="white-space:pre-wrap">                 </span> * input string
          for each "?" skipped.</div>
        <div> <span style="white-space:pre-wrap">                        </span> */</div>
        <div>-<span style="white-space:pre-wrap">                 </span>while (pattern
          < pattern_end){</div>
        <div>-<span style="white-space:pre-wrap">                         </span>c =
          Utf8Read(pattern, pattern_end);</div>
        <div>+<span style="white-space:pre-wrap">                 </span>while ((c =
          Utf8Read(pattern, pattern_end)) !=</div>
        <div>+<span style="white-space:pre-wrap">                 </span>     
           SQL_END_OF_STRING) {</div>
        <div>+<span style="white-space:pre-wrap">                         </span>if (c ==
          SQL_INVALID_UTF8_SYMBOL)</div>
        <div>+<span style="white-space:pre-wrap">                                 </span>return
          SQL_PROHIBITED_PATTERN;</div>
        <div> <span style="white-space:pre-wrap">                                </span>if (c != matchAll
          && c != matchOne)</div>
        <div> <span style="white-space:pre-wrap">                                        </span>break;</div>
        <div>-<span style="white-space:pre-wrap">                         </span>if (c == matchOne</div>
        <div>-<span style="white-space:pre-wrap">                         </span>    &&
          Utf8Read(string, string_end) == 0) {</div>
        <div>+<span style="white-space:pre-wrap">                         </span>if (c == matchOne
          &&</div>
        <div>+<span style="white-space:pre-wrap">                         </span>    (c2 =
          Utf8Read(string, string_end)) ==</div>
        <div>+<span style="white-space:pre-wrap">                         </span>   
          SQL_END_OF_STRING)</div>
        <div> <span style="white-space:pre-wrap">                                        </span>return
          SQLITE_NOWILDCARDMATCH;</div>
        <div>-<span style="white-space:pre-wrap">                         </span>}</div>
        <div>+<span style="white-space:pre-wrap">                         </span>if (c2 ==
          SQL_INVALID_UTF8_SYMBOL)</div>
        <div>+<span style="white-space:pre-wrap">                                 </span>return
          SQLITE_NOMATCH;</div>
        <div> <span style="white-space:pre-wrap">                        </span>}</div>
        <div>-<span style="white-space:pre-wrap">                 </span>/* "*" at the end
          of the pattern matches */</div>
        <div>-<span style="white-space:pre-wrap">                 </span>if (pattern ==
          pattern_end)</div>
        <div>+<span style="white-space:pre-wrap">                 </span>/*</div>
        <div>+<span style="white-space:pre-wrap">                 </span> * "*" at the end
          of the pattern matches.</div>
        <div>+<span style="white-space:pre-wrap">                 </span> */</div>
        <div>+<span style="white-space:pre-wrap">                 </span>if (c ==
          SQL_END_OF_STRING) {</div>
        <div>+<span style="white-space:pre-wrap">                         </span>while ((c2 =
          Utf8Read(string, string_end)) !=</div>
        <div>+<span style="white-space:pre-wrap">                         </span>     
           SQL_END_OF_STRING)</div>
        <div>+<span style="white-space:pre-wrap">                                 </span>if (c2 ==
          SQL_INVALID_UTF8_SYMBOL)</div>
        <div>+<span style="white-space:pre-wrap">                                         </span>return
          SQLITE_NOMATCH;</div>
        <div> <span style="white-space:pre-wrap">                                </span>return
          SQLITE_MATCH;</div>
        <div>+<span style="white-space:pre-wrap">                 </span>}</div>
        <div> <span style="white-space:pre-wrap">                        </span>if (c ==
          matchOther) {</div>
        <div> <span style="white-space:pre-wrap">                                </span>if
          (pInfo->matchSet == 0) {</div>
        <div> <span style="white-space:pre-wrap">                                        </span>c =
          Utf8Read(pattern, pattern_end);</div>
        <div>-<span style="white-space:pre-wrap">                                 </span>if (c == 0)</div>
        <div>+<span style="white-space:pre-wrap">                                 </span>if (c ==
          SQL_INVALID_UTF8_SYMBOL)</div>
        <div>+<span style="white-space:pre-wrap">                                         </span>return
          SQL_PROHIBITED_PATTERN;</div>
        <div>+<span style="white-space:pre-wrap">                                 </span>if (c ==
          SQL_END_OF_STRING)</div>
        <div> <span style="white-space:pre-wrap">                                                </span>return
          SQLITE_NOWILDCARDMATCH;</div>
        <div> <span style="white-space:pre-wrap">                                </span>} else {</div>
        <div>-<span style="white-space:pre-wrap">                                 </span>/* "[...]"
          immediately follows the "*".  We have to do a slow</div>
        <div>-<span style="white-space:pre-wrap">                                 </span> * recursive
          search in this case, but it is an unusual case.</div>
        <div>+<span style="white-space:pre-wrap">                                 </span>/* "[...]"
          immediately</div>
        <div>+<span style="white-space:pre-wrap">                                 </span> * follows the
          "*". We</div>
        <div>+<span style="white-space:pre-wrap">                                 </span> * have to do a
          slow</div>
        <div>+<span style="white-space:pre-wrap">                                 </span> * recursive
          search in</div>
        <div>+<span style="white-space:pre-wrap">                                 </span> * this case,
          but it is</div>
        <div>+<span style="white-space:pre-wrap">                                 </span> * an unusual
          case.</div>
        <div> <span style="white-space:pre-wrap">                                        </span> */</div>
        <div>-<span style="white-space:pre-wrap">                                 </span>assert(matchOther
          < 0x80);<span style="white-space:pre-wrap">        </span>/* '[' is a
          single-byte character */</div>
        <div>+<span style="white-space:pre-wrap">                                 </span>assert(matchOther
          < 0x80);</div>
        <div> <span style="white-space:pre-wrap">                                        </span>while (string
          < string_end) {</div>
      </div>
    </blockquote>
    REMEMBER THIS POINT #2<br>
    <br>
    <blockquote type="cite">
      <div dir="ltr">
        <div> <span style="white-space:pre-wrap">                                                </span>int bMatch =</div>
        <div>-<span style="white-space:pre-wrap">                                         </span>   
          patternCompare(&pattern[-1],</div>
        <div>-<span style="white-space:pre-wrap">                                                         </span>   string,</div>
        <div>-<span style="white-space:pre-wrap">                                                         </span>   pInfo,</div>
        <div>-<span style="white-space:pre-wrap">                                                         </span> 
           matchOther);</div>
        <div>+<span style="white-space:pre-wrap">                                         </span>   
          sql_utf8_pattern_compare(</div>
        <div>+<span style="white-space:pre-wrap">                                                         </span>&pattern[-1],</div>
        <div>+<span style="white-space:pre-wrap">                                                         </span>string,</div>
        <div>+<span style="white-space:pre-wrap">                                                         </span>pInfo,</div>
        <div>+<span style="white-space:pre-wrap">                                                         </span>matchOther);</div>
        <div> <span style="white-space:pre-wrap">                                                </span>if (bMatch !=
          SQLITE_NOMATCH)</div>
        <div> <span style="white-space:pre-wrap">                                                        </span>return bMatch;</div>
        <div>-<span style="white-space:pre-wrap">                                         </span>Utf8Read(string,
          string_end);</div>
        <div>+<span style="white-space:pre-wrap">                                         </span>c =
          Utf8Read(string, string_end);</div>
        <div>+<span style="white-space:pre-wrap">                                         </span>if (c ==
          SQL_INVALID_UTF8_SYMBOL)</div>
        <div>+<span style="white-space:pre-wrap">                                                 </span>return
          SQLITE_NOMATCH;</div>
      </div>
    </blockquote>
    look at <REMEMBER THIS POINT #1,2> and other `Utf8Read`
    usages.<br>
    You have introduced SQL_END_OF_STRING and changed `Utf8Read` pattern
    to use it in<br>
    half of cases?<br>
    <br></div></blockquote><div><br></div><div><span style="font-size:small;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">1) Look at <REMEMBER THIS POINT #1>.</span><br></div><div><span style="font-size:small;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline"><span style="text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">2) I have introduced it not to use explicit 0 and to fix the bug.</span></span></div><div><span style="font-size:small;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">3) Fixed it though.</span></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div bgcolor="#FFFFFF">
    Moreover,in that place you do check `string < string_end`
    implicitly inside of<br>
    `Utf8Read` but you never use that result. <br></div></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div bgcolor="#FFFFFF">
    I suppose you should return old iteration style and `Utf8Read`
    macro.<br>
    ```<br>
    while (string < string_end) {<br>
    <span style="white-space:pre-wrap"></span>    c = Utf8Read(string,
    string_end);
    <div><span style="white-space:pre-wrap">    </span>if (c ==
      SQL_INVALID_UTF8_SYMBOL)<br>
             <span style="white-space:pre-wrap"> </span>return
      SQLITE_NOMATCH;</div>
    ```<br></div></blockquote><div><br></div><div>I think it's better to use this approach, but yes: return prev. version of macro:</div><div>1) 'zero byte' ticket will be partially fixed.</div><div>2) 0xffff<span style="font-size:small;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline"><span> </span>is</span> non-unicode<span style="font-size:small;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline"><span> </span>anyways</span>.</div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div bgcolor="#FFFFFF">
    <blockquote type="cite">
      <div dir="ltr">
        <div> <span style="white-space:pre-wrap">                                        </span>}</div>
        <div> <span style="white-space:pre-wrap">                                        </span>return
          SQLITE_NOWILDCARDMATCH;</div>
        <div> <span style="white-space:pre-wrap">                                </span>}</div>
        <div> <span style="white-space:pre-wrap">                        </span>}</div>
        <div> </div>
        <div>-<span style="white-space:pre-wrap">                 </span>/* At this point
          variable c contains the first character of the</div>
        <div>-<span style="white-space:pre-wrap">                 </span> * pattern string
          past the "*".  Search in the input string for the</div>
        <div>-<span style="white-space:pre-wrap">                 </span> * first matching
          character and recursively continue the match from</div>
        <div>-<span style="white-space:pre-wrap">                 </span> * that point.</div>
        <div>+<span style="white-space:pre-wrap">                 </span>/* At this point
          variable c contains the</div>
        <div>+<span style="white-space:pre-wrap">                 </span> * first character
          of the pattern string</div>
        <div>+<span style="white-space:pre-wrap">                 </span> * past the "*".
          Search in the input</div>
        <div>+<span style="white-space:pre-wrap">                 </span> * string for the
          first matching</div>
        <div>+<span style="white-space:pre-wrap">                 </span> * character and
          recursively continue the</div>
        <div>+<span style="white-space:pre-wrap">                 </span> * match from that
          point.</div>
        <div> <span style="white-space:pre-wrap">                        </span> *</div>
        <div>-<span style="white-space:pre-wrap">                 </span> * For a
          case-insensitive search, set variable cx to be the same as</div>
        <div>-<span style="white-space:pre-wrap">                 </span> * c but in the
          other case and search the input string for either</div>
        <div>-<span style="white-space:pre-wrap">                 </span> * c or cx.</div>
        <div>+<span style="white-space:pre-wrap">                 </span> * For a
          case-insensitive search, set</div>
        <div>+<span style="white-space:pre-wrap">                 </span> * variable cx to
          be the same as c but in</div>
        <div>+<span style="white-space:pre-wrap">                 </span> * the other case
          and search the input</div>
        <div>+<span style="white-space:pre-wrap">                 </span> * string for
          either c or cx.</div>
        <div> <span style="white-space:pre-wrap">                        </span> */</div>
        <div> </div>
        <div> <span style="white-space:pre-wrap">                        </span>int bMatch;</div>
        <div>@@ -756,14 +801,18 @@ patternCompare(const char * pattern,<span style="white-space:pre-wrap">        </span>/*
          The glob pattern */</div>
        <div> <span style="white-space:pre-wrap">                                </span>c = u_tolower(c);</div>
        <div> <span style="white-space:pre-wrap">                        </span>while (string <
          string_end){</div>
        <div> <span style="white-space:pre-wrap">                                </span>/**</div>
        <div>-<span style="white-space:pre-wrap">                         </span> * This loop
          could have been implemented</div>
        <div>-<span style="white-space:pre-wrap">                         </span> * without if
          converting c2 to lower case</div>
        <div>-<span style="white-space:pre-wrap">                         </span> * (by holding
          c_upper and c_lower), however</div>
        <div>-<span style="white-space:pre-wrap">                         </span> * it is
          implemented this way because lower</div>
        <div>-<span style="white-space:pre-wrap">                         </span> * works better
          with German and Turkish</div>
        <div>-<span style="white-space:pre-wrap">                         </span> * languages.</div>
        <div>+<span style="white-space:pre-wrap">                         </span> * This loop
          could have been</div>
        <div>+<span style="white-space:pre-wrap">                         </span> * implemented
          without if</div>
        <div>+<span style="white-space:pre-wrap">                         </span> * converting c2
          to lower case</div>
        <div>+<span style="white-space:pre-wrap">                         </span> * by holding
          c_upper and</div>
        <div>+<span style="white-space:pre-wrap">                         </span> *
          c_lower,however it is</div>
        <div>+<span style="white-space:pre-wrap">                         </span> * implemented
          this way because</div>
        <div>+<span style="white-space:pre-wrap">                         </span> * lower works
          better with German</div>
        <div>+<span style="white-space:pre-wrap">                         </span> * and Turkish
          languages.</div>
        <div> <span style="white-space:pre-wrap">                                </span> */</div>
        <div> <span style="white-space:pre-wrap">                                </span>c2 =
          Utf8Read(string, string_end);</div>
        <div>+<span style="white-space:pre-wrap">                         </span>if (c2 ==
          SQL_INVALID_UTF8_SYMBOL)</div>
        <div>+<span style="white-space:pre-wrap">                                 </span>return
          SQLITE_NOMATCH;</div>
        <div> <span style="white-space:pre-wrap">                                </span>if (!noCase) {</div>
        <div> <span style="white-space:pre-wrap">                                        </span>if (c2 != c)</div>
        <div> <span style="white-space:pre-wrap">                                                </span>continue;</div>
        <div>@@ -771,9 +820,10 @@ patternCompare(const char * pattern,<span style="white-space:pre-wrap"> </span>/*
          The glob pattern */</div>
        <div> <span style="white-space:pre-wrap">                                        </span>if (c2 != c
          && u_tolower(c2) != c)</div>
        <div> <span style="white-space:pre-wrap">                                                </span>continue;</div>
        <div> <span style="white-space:pre-wrap">                                </span>}</div>
        <div>-<span style="white-space:pre-wrap">                         </span>bMatch =</div>
        <div>-<span style="white-space:pre-wrap">                         </span>   
          patternCompare(pattern, string,</div>
        <div>-<span style="white-space:pre-wrap">                                         </span>   pInfo,
          matchOther);</div>
        <div>+<span style="white-space:pre-wrap">                         </span>bMatch =
          sql_utf8_pattern_compare(pattern,</div>
        <div>+<span style="white-space:pre-wrap">                                                         </span>  string,</div>
        <div>+<span style="white-space:pre-wrap">                                                         </span>  pInfo,</div>
        <div>+<span style="white-space:pre-wrap">                                                         </span> 
          matchOther);</div>
        <div> <span style="white-space:pre-wrap">                                </span>if (bMatch !=
          SQLITE_NOMATCH)</div>
        <div> <span style="white-space:pre-wrap">                                        </span>return bMatch;</div>
        <div> <span style="white-space:pre-wrap">                        </span>}</div>
        <div>@@ -782,7 +832,9 @@ patternCompare(const char * pattern,<span style="white-space:pre-wrap">  </span>/*
          The glob pattern */</div>
        <div> <span style="white-space:pre-wrap">                </span>if (c ==
          matchOther) {</div>
        <div> <span style="white-space:pre-wrap">                        </span>if
          (pInfo->matchSet == 0) {</div>
        <div> <span style="white-space:pre-wrap">                                </span>c =
          Utf8Read(pattern, pattern_end);</div>
        <div>-<span style="white-space:pre-wrap">                         </span>if (c == 0)</div>
        <div>+<span style="white-space:pre-wrap">                         </span>if (c ==
          SQL_INVALID_UTF8_SYMBOL)</div>
        <div>+<span style="white-space:pre-wrap">                                 </span>return
          SQL_PROHIBITED_PATTERN;</div>
        <div>+<span style="white-space:pre-wrap">                         </span>if (c ==
          SQL_END_OF_STRING)</div>
        <div> <span style="white-space:pre-wrap">                                        </span>return
          SQLITE_NOMATCH;</div>
        <div> <span style="white-space:pre-wrap">                                </span>zEscaped =
          pattern;</div>
        <div> <span style="white-space:pre-wrap">                        </span>} else {</div>
        <div>@@ -790,23 +842,33 @@ patternCompare(const char * pattern,<span style="white-space:pre-wrap">        </span>/*
          The glob pattern */</div>
        <div> <span style="white-space:pre-wrap">                                </span>int seen = 0;</div>
        <div> <span style="white-space:pre-wrap">                                </span>int invert = 0;</div>
        <div> <span style="white-space:pre-wrap">                                </span>c =
          Utf8Read(string, string_end);</div>
        <div>+<span style="white-space:pre-wrap">                         </span>if (c ==
          SQL_INVALID_UTF8_SYMBOL)</div>
        <div>+<span style="white-space:pre-wrap">                                 </span>return
          SQLITE_NOMATCH;</div>
        <div> <span style="white-space:pre-wrap">                                </span>if (string ==
          string_end)</div>
        <div> <span style="white-space:pre-wrap">                                        </span>return
          SQLITE_NOMATCH;</div>
        <div> <span style="white-space:pre-wrap">                                </span>c2 =
          Utf8Read(pattern, pattern_end);</div>
        <div>+<span style="white-space:pre-wrap">                         </span>if (c2 ==
          SQL_INVALID_UTF8_SYMBOL)</div>
        <div>+<span style="white-space:pre-wrap">                                 </span>return
          SQL_PROHIBITED_PATTERN;</div>
        <div> <span style="white-space:pre-wrap">                                </span>if (c2 == '^') {</div>
        <div> <span style="white-space:pre-wrap">                                        </span>invert = 1;</div>
        <div> <span style="white-space:pre-wrap">                                        </span>c2 =
          Utf8Read(pattern, pattern_end);</div>
        <div>+<span style="white-space:pre-wrap">                                 </span>if (c2 ==
          SQL_INVALID_UTF8_SYMBOL)</div>
        <div>+<span style="white-space:pre-wrap">                                         </span>return
          SQL_PROHIBITED_PATTERN;</div>
        <div> <span style="white-space:pre-wrap">                                </span>}</div>
        <div> <span style="white-space:pre-wrap">                                </span>if (c2 == ']') {</div>
        <div> <span style="white-space:pre-wrap">                                        </span>if (c == ']')</div>
        <div> <span style="white-space:pre-wrap">                                                </span>seen = 1;</div>
        <div> <span style="white-space:pre-wrap">                                        </span>c2 =
          Utf8Read(pattern, pattern_end);</div>
        <div>+<span style="white-space:pre-wrap">                                 </span>if (c2 ==
          SQL_INVALID_UTF8_SYMBOL)</div>
        <div>+<span style="white-space:pre-wrap">                                         </span>return
          SQL_PROHIBITED_PATTERN;</div>
        <div> <span style="white-space:pre-wrap">                                </span>}</div>
        <div>-<span style="white-space:pre-wrap">                         </span>while (c2
          && c2 != ']') {</div>
        <div>+<span style="white-space:pre-wrap">                         </span>while (c2 !=
          SQL_END_OF_STRING && c2 != ']') {</div>
        <div> <span style="white-space:pre-wrap">                                        </span>if (c2 == '-'
          && pattern[0] != ']'</div>
        <div> <span style="white-space:pre-wrap">                                        </span>    &&
          pattern < pattern_end</div>
        <div> <span style="white-space:pre-wrap">                                        </span>    &&
          prior_c > 0) {</div>
        <div> <span style="white-space:pre-wrap">                                                </span>c2 =
          Utf8Read(pattern, pattern_end);</div>
        <div>+<span style="white-space:pre-wrap">                                         </span>if (c2 ==
          SQL_INVALID_UTF8_SYMBOL)</div>
        <div>+<span style="white-space:pre-wrap">                                                 </span>return
          SQL_PROHIBITED_PATTERN;</div>
        <div> <span style="white-space:pre-wrap">                                                </span>if (c >=
          prior_c && c <= c2)</div>
        <div> <span style="white-space:pre-wrap">                                                        </span>seen = 1;</div>
        <div> <span style="white-space:pre-wrap">                                                </span>prior_c = 0;</div>
        <div>@@ -817,29 +879,36 @@ patternCompare(const char * pattern,<span style="white-space:pre-wrap">        </span>/*
          The glob pattern */</div>
        <div> <span style="white-space:pre-wrap">                                                </span>prior_c = c2;</div>
        <div> <span style="white-space:pre-wrap">                                        </span>}</div>
        <div> <span style="white-space:pre-wrap">                                        </span>c2 =
          Utf8Read(pattern, pattern_end);</div>
        <div>+<span style="white-space:pre-wrap">                                 </span>if (c2 ==
          SQL_INVALID_UTF8_SYMBOL)</div>
        <div>+<span style="white-space:pre-wrap">                                         </span>return
          SQL_PROHIBITED_PATTERN;</div>
        <div> <span style="white-space:pre-wrap">                                </span>}</div>
        <div>-<span style="white-space:pre-wrap">                         </span>if (pattern ==
          pattern_end || (seen ^ invert) == 0) {</div>
        <div>+<span style="white-space:pre-wrap">                         </span>if (pattern ==
          pattern_end ||</div>
        <div>+<span style="white-space:pre-wrap">                         </span>    (seen ^
          invert) == 0) {</div>
        <div> <span style="white-space:pre-wrap">                                        </span>return
          SQLITE_NOMATCH;</div>
        <div> <span style="white-space:pre-wrap">                                </span>}</div>
        <div> <span style="white-space:pre-wrap">                                </span>continue;</div>
        <div> <span style="white-space:pre-wrap">                        </span>}</div>
        <div> <span style="white-space:pre-wrap">                </span>}</div>
        <div> <span style="white-space:pre-wrap">                </span>c2 =
          Utf8Read(string, string_end);</div>
        <div>+<span style="white-space:pre-wrap">         </span>if (c2 ==
          SQL_INVALID_UTF8_SYMBOL)</div>
        <div>+<span style="white-space:pre-wrap">                 </span>return
          SQLITE_NOMATCH;</div>
        <div> <span style="white-space:pre-wrap">                </span>if (c == c2)</div>
        <div> <span style="white-space:pre-wrap">                        </span>continue;</div>
        <div> <span style="white-space:pre-wrap">                </span>if (noCase){</div>
        <div> <span style="white-space:pre-wrap">                        </span>/**</div>
        <div>-<span style="white-space:pre-wrap">                 </span> * Small
          optimisation. Reduce number of calls</div>
        <div>-<span style="white-space:pre-wrap">                 </span> * to u_tolower
          function.</div>
        <div>-<span style="white-space:pre-wrap">                 </span> * SQL standards
          suggest use to_upper for symbol</div>
        <div>-<span style="white-space:pre-wrap">                 </span> * normalisation.
          However, using to_lower allows to</div>
        <div>-<span style="white-space:pre-wrap">                 </span> * respect Turkish
          'İ' in default locale.</div>
        <div>+<span style="white-space:pre-wrap">                 </span> * Small
          optimisation. Reduce number of</div>
        <div>+<span style="white-space:pre-wrap">                 </span> * calls to
          u_tolower function. SQL</div>
        <div>+<span style="white-space:pre-wrap">                 </span> * standards
          suggest use to_upper for</div>
        <div>+<span style="white-space:pre-wrap">                 </span> * symbol
          normalisation. However, using</div>
        <div>+<span style="white-space:pre-wrap">                 </span> * to_lower allows
          to respect Turkish 'İ'</div>
        <div>+<span style="white-space:pre-wrap">                 </span> * in default
          locale.</div>
        <div> <span style="white-space:pre-wrap">                        </span> */</div>
        <div> <span style="white-space:pre-wrap">                        </span>if (u_tolower(c)
          == c2 ||</div>
        <div> <span style="white-space:pre-wrap">                        </span>    c ==
          u_tolower(c2))</div>
        <div> <span style="white-space:pre-wrap">                                </span>continue;</div>
        <div> <span style="white-space:pre-wrap">                </span>}</div>
        <div>-<span style="white-space:pre-wrap">         </span>if (c == matchOne
          && pattern != zEscaped && c2 != 0)</div>
        <div>+<span style="white-space:pre-wrap">         </span>if (c == matchOne
          && pattern != zEscaped &&</div>
        <div>+<span style="white-space:pre-wrap">         </span>    c2 !=
          SQL_END_OF_STRING)</div>
        <div> <span style="white-space:pre-wrap">                        </span>continue;</div>
        <div> <span style="white-space:pre-wrap">                </span>return
          SQLITE_NOMATCH;</div>
        <div> <span style="white-space:pre-wrap">        </span>}</div>
        <div>@@ -853,8 +922,7 @@ patternCompare(const char * pattern,<span style="white-space:pre-wrap">  </span>/*
          The glob pattern */</div>
        <div> int</div>
        <div> sqlite3_strglob(const char *zGlobPattern, const char
          *zString)</div>
        <div> {</div>
        <div>-<span style="white-space:pre-wrap"> </span>return
          patternCompare(zGlobPattern, zString, &globInfo,</div>
        <div>-<span style="white-space:pre-wrap">                 </span>      '[');</div>
        <div>+<span style="white-space:pre-wrap"> </span>return
          sql_utf8_pattern_compare(zGlobPattern, zString, &globInfo,
          '[');</div>
        <div> }</div>
        <div> </div>
        <div> /*</div>
        <div>@@ -864,7 +932,7 @@ sqlite3_strglob(const char
          *zGlobPattern, const char *zString)</div>
        <div> int</div>
        <div> sqlite3_strlike(const char *zPattern, const char *zStr,
          unsigned int esc)</div>
        <div> {</div>
        <div>-<span style="white-space:pre-wrap"> </span>return
          patternCompare(zPattern, zStr, &likeInfoNorm, esc);</div>
        <div>+<span style="white-space:pre-wrap"> </span>return
          sql_utf8_pattern_compare(zPattern, zStr, &likeInfoNorm,
          esc);</div>
        <div> }</div>
        <div> </div>
        <div> /*</div>
        <div>@@ -910,8 +978,9 @@ likeFunc(sqlite3_context * context, int
          argc, sqlite3_value ** argv)</div>
        <div> <span style="white-space:pre-wrap">        </span>zB = (const char *)
          sqlite3_value_text(argv[0]);</div>
        <div> <span style="white-space:pre-wrap">        </span>zA = (const char *)
          sqlite3_value_text(argv[1]);</div>
        <div> </div>
        <div>-<span style="white-space:pre-wrap"> </span>/* Limit the length
          of the LIKE or GLOB pattern to avoid problems</div>
        <div>-<span style="white-space:pre-wrap"> </span> * of deep recursion
          and N*N behavior in patternCompare().</div>
        <div>+<span style="white-space:pre-wrap"> </span>/* Limit the length
          of the LIKE or GLOB pattern to avoid</div>
        <div>+<span style="white-space:pre-wrap"> </span> * problems of deep
          recursion and N*N behavior in</div>
        <div>+<span style="white-space:pre-wrap"> </span> *
          sql_utf8_pattern_compare().</div>
        <div> <span style="white-space:pre-wrap">        </span> */</div>
        <div> <span style="white-space:pre-wrap">        </span>nPat =
          sqlite3_value_bytes(argv[0]);</div>
        <div> <span style="white-space:pre-wrap">        </span>testcase(nPat ==
          db->aLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]);</div>
        <div>@@ -947,7 +1016,12 @@ likeFunc(sqlite3_context * context,
          int argc, sqlite3_value ** argv)</div>
        <div> <span style="white-space:pre-wrap">        </span>sqlite3_like_count++;</div>
        <div> #endif</div>
        <div> <span style="white-space:pre-wrap">        </span>int res;</div>
        <div>-<span style="white-space:pre-wrap"> </span>res =
          patternCompare(zB, zA, pInfo, escape);</div>
        <div>+<span style="white-space:pre-wrap"> </span>res =
          sql_utf8_pattern_compare(zB, zA, pInfo, escape);</div>
        <div>+<span style="white-space:pre-wrap"> </span>if (res ==
          SQL_PROHIBITED_PATTERN) {</div>
        <div>+<span style="white-space:pre-wrap">         </span>sqlite3_result_error(context,
          "LIKE or GLOB pattern can only"</div>
        <div>+<span style="white-space:pre-wrap">                         </span>     " contain
          UTF-8 characters", -1);</div>
        <div>+<span style="white-space:pre-wrap">         </span>return;</div>
        <div>+<span style="white-space:pre-wrap"> </span>}</div>
        <div> <span style="white-space:pre-wrap">        </span>sqlite3_result_int(context,
          res == SQLITE_MATCH);</div>
        <div> }</div>
        <div> </div>
        <div>diff --git a/test-run b/test-run</div>
        <div>index 77e9327..95562e9 160000</div>
        <div>--- a/test-run</div>
        <div>+++ b/test-run</div>
        <div>@@ -1 +1 @@</div>
        <div>-Subproject commit 77e93279210f8c5c1fd0ed03416fa19a184f0b6d</div>
        <div>+Subproject commit 95562e95401fef4e0b755ab0bb430974b5d1a29a</div>
        <div>diff --git a/test/sql-tap/e_expr.test.lua
          b/test/sql-tap/e_expr.test.lua</div>
        <div>index 13d3a96..9780d2c 100755</div>
        <div>--- a/test/sql-tap/e_expr.test.lua</div>
        <div>+++ b/test/sql-tap/e_expr.test.lua</div>
        <div>@@ -1,6 +1,6 @@</div>
        <div> #!/usr/bin/env tarantool</div>
        <div> test = require("sqltester")</div>
        <div>-test:plan(12431)</div>
        <div>+test:plan(10665)</div>
        <div> </div>
        <div> --!./tcltestrunner.lua</div>
        <div> -- 2010 July 16</div>
        <div>@@ -77,8 +77,10 @@ local operations = {</div>
        <div>     {"<>", "ne1"},</div>
        <div>     {"!=", "ne2"},</div>
        <div>     {"IS", "is"},</div>
        <div>-    {"LIKE", "like"},</div>
        <div>-    {"GLOB", "glob"},</div>
        <div>+-- NOTE: This test needs refactoring after deletion of
          GLOB &</div>
        <div>+--<span style="white-space:pre-wrap">       </span> type restrictions
          for LIKE. (See #3572)</div>
        <div>+--    {"LIKE", "like"},</div>
        <div>+--    {"GLOB", "glob"},</div>
      </div>
    </blockquote>
    Yes, this behavior is not valid anymore.<br>
    To make sure that likes and globs will be tested in the future,
    please, delete this<br>
    commented lines and add your own simple test, which tries to call
    `like` and `glob`<br>
    with inappropriate types.<br>
    It is important to have a functional tests for any possible
    behavior.<br></div></blockquote><div><br></div><div>1) Globs are going to be deleted as you can see few lines above.</div><div>2) I'm gonna refactor that when LIKE is in its final state (basically after #3572 is closed</div><div>& static build is into 2.1).</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div bgcolor="#FFFFFF">
    <blockquote type="cite">
      <div dir="ltr">
        <div>     {"AND", "and"},</div>
        <div>     {"OR", "or"},</div>
        <div>     {"MATCH", "match"},</div>
        <div>@@ -96,7 +98,12 @@ operations = {</div>
        <div>     {"+", "-"},</div>
        <div>     {"<<", ">>", "&", "|"},</div>
        <div>     {"<", "<=", ">", ">="},</div>
        <div>-    {"=", "==", "!=", "<>", "LIKE", "GLOB"},
          --"MATCH", "REGEXP"},</div>
        <div>+-- NOTE: This test needs refactoring after deletion of
          GLOB &</div>
        <div>+--<span style="white-space:pre-wrap">       </span> type restrictions
          for LIKE. (See #3572)</div>
        <div>+-- Another NOTE: MATCH & REGEXP aren't supported in
          Tarantool &</div>
        <div>+-- <span style="white-space:pre-wrap">              </span> are waiting
          for their hour, don't confuse them</div>
        <div>+--<span style="white-space:pre-wrap">               </span> being commented
          with ticket above.</div>
        <div>+    {"=", "==", "!=", "<>"}, --"LIKE", "GLOB"},
          --"MATCH", "REGEXP"},</div>
        <div>     {"AND"},</div>
        <div>     {"OR"},</div>
        <div> }</div>
        <div>@@ -475,6 +482,7 @@ for _, op in ipairs(oplist) do</div>
        <div>         end</div>
        <div>     end</div>
        <div> end</div>
        <div>+</div>
        <div> ---------------------------------------------------------------------------</div>
        <div> -- Test the IS and IS NOT operators.</div>
        <div> --</div>
        <div>diff --git
          a/test/sql-tap/gh-3251-string-pattern-comparison.test.lua
          b/test/sql-tap/gh-3251-string-pattern-comparison.test.lua</div>
        <div>new file mode 100755</div>
        <div>index 0000000..2a787f2</div>
        <div>--- /dev/null</div>
        <div>+++
          b/test/sql-tap/gh-3251-string-pattern-comparison.test.lua</div>
        <div>@@ -0,0 +1,213 @@</div>
        <div>+#!/usr/bin/env tarantool</div>
        <div>+test = require("sqltester")</div>
        <div>+test:plan(128)</div>
        <div>+</div>
        <div>+local prefix = "like-test-"</div>
        <div>+</div>
        <div>+-- Unicode byte sequences.</div>
        <div>+local valid_testcases = {</div>
        <div>+    '\x01',</div>
        <div>+    '\x09',</div>
        <div>+    '\x1F',</div>
        <div>+    '\x7F',</div>
        <div>+    '\xC2\x80',</div>
        <div>+    '\xC2\x90',</div>
        <div>+    '\xC2\x9F',</div>
        <div>+    '\xE2\x80\xA8',</div>
        <div>+    '\x20\x0B',</div>
        <div>+    '\xE2\x80\xA9',</div>
        <div>+}</div>
      </div>
    </blockquote>
    optional: add descriptions to those byte sequences (what it is).<br></div></blockquote><div><br></div><div>Some valid unicode symbols which is only that matters for LIKE operator.</div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div bgcolor="#FFFFFF">
    <blockquote type="cite">
      <div dir="ltr">
        <div>+</div>
        <div>+-- Non-Unicode byte sequences.</div>
        <div>+local invalid_testcases = {</div>
        <div>+    '\xE2\x80',</div>
        <div>+    '\xFE\xFF',</div>
        <div>+    '\xC2',</div>
        <div>+    '\xED\xB0\x80',</div>
        <div>+    '\xD0',</div>
        <div>+}</div>
      </div>
    </blockquote>
    Place that after like_test_cases, just before it is used.<br></div></blockquote><div><br></div><div>Changed it.</div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div bgcolor="#FFFFFF">
    <blockquote type="cite">
      <div dir="ltr">
        <div>+</div>
        <div>+local like_test_cases =</div>
        <div>+{</div>
        <div>+    {"1.1",</div>
        <div>+        "SELECT 'AB' LIKE '_B';",</div>
        <div>+        {0, {1}} },</div>
        <div>+    {"1.2",</div>
        <div>+        "SELECT 'CD' LIKE '_B';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.3",</div>
        <div>+        "SELECT '' LIKE '_B';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.4",</div>
        <div>+        "SELECT 'AB' LIKE '%B';",</div>
        <div>+        {0, {1}} },</div>
        <div>+    {"1.5",</div>
        <div>+        "SELECT 'CD' LIKE '%B';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.6",</div>
        <div>+        "SELECT '' LIKE '%B';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.7",</div>
        <div>+        "SELECT 'AB' LIKE 'A__';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.8",</div>
        <div>+        "SELECT 'CD' LIKE 'A__';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.9",</div>
        <div>+        "SELECT '' LIKE 'A__';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.10",</div>
        <div>+        "SELECT 'AB' LIKE 'A_';",</div>
        <div>+        {0, {1}} },</div>
        <div>+    {"1.11",</div>
        <div>+        "SELECT 'CD' LIKE 'A_';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.12",</div>
        <div>+        "SELECT '' LIKE 'A_';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.13",</div>
        <div>+        "SELECT 'AB' LIKE 'A';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.14",</div>
        <div>+        "SELECT 'CD' LIKE 'A';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.15",</div>
        <div>+        "SELECT '' LIKE 'A';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.16",</div>
        <div>+        "SELECT 'AB' LIKE '_';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.17",</div>
        <div>+        "SELECT 'CD' LIKE '_';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.18",</div>
        <div>+        "SELECT '' LIKE '_';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.19",</div>
        <div>+        "SELECT 'AB' LIKE '__';",</div>
        <div>+        {0, {1}} },</div>
        <div>+    {"1.20",</div>
        <div>+        "SELECT 'CD' LIKE '__';",</div>
        <div>+        {0, {1}} },</div>
        <div>+    {"1.21",</div>
        <div>+        "SELECT '' LIKE '__';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.22",</div>
        <div>+        "SELECT 'AB' LIKE '%A';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.23",</div>
        <div>+        "SELECT 'AB' LIKE '%C';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.24",</div>
        <div>+        "SELECT 'ab' LIKE '%df';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.25",</div>
        <div>+        "SELECT 'abCDF' LIKE '%df';",</div>
        <div>+        {0, {1}} },</div>
        <div>+    {"1.26",</div>
        <div>+        "SELECT 'CDF' LIKE '%df';",</div>
        <div>+        {0, {1}} },</div>
        <div>+    {"1.27",</div>
        <div>+        "SELECT 'ab' LIKE 'a_';",</div>
        <div>+        {0, {1}} },</div>
        <div>+    {"1.28",</div>
        <div>+        "SELECT 'abCDF' LIKE 'a_';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.29",</div>
        <div>+        "SELECT 'CDF' LIKE 'a_';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.30",</div>
        <div>+        "SELECT 'ab' LIKE 'ab%';",</div>
        <div>+        {0, {1}} },</div>
        <div>+    {"1.31",</div>
        <div>+        "SELECT 'abCDF' LIKE 'ab%';",</div>
        <div>+        {0, {1}} },</div>
        <div>+    {"1.32",</div>
        <div>+        "SELECT 'CDF' LIKE 'ab%';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.33",</div>
        <div>+        "SELECT 'ab' LIKE 'abC%';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.34",</div>
        <div>+        "SELECT 'abCDF' LIKE 'abC%';",</div>
        <div>+        {0, {1}} },</div>
        <div>+    {"1.35",</div>
        <div>+        "SELECT 'CDF' LIKE 'abC%';",</div>
        <div>+        {0, {0}} },</div>
        <div>+    {"1.36",</div>
        <div>+        "SELECT 'ab' LIKE 'a_%';",</div>
        <div>+        {0, {1}} },</div>
        <div>+    {"1.37",</div>
        <div>+        "SELECT 'abCDF' LIKE 'a_%';",</div>
        <div>+        {0, {1}} },</div>
        <div>+    {"1.38",</div>
        <div>+        "SELECT 'CDF' LIKE 'a_%';",</div>
        <div>+        {0, {0}} },</div>
        <div>+}</div>
      </div>
    </blockquote>
    Please, add some tests for unicode strings. (or replace letters in
    those tests with unicode letters)<br></div></blockquote><div><br></div><div>Changed existing tests a little bit.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div bgcolor="#FFFFFF">
    <blockquote type="cite">
      <div dir="ltr">
        <div>+</div>
        <div>+test:do_catchsql_set_test(like_test_cases, prefix)</div>
        <div>+</div>
        <div>+-- Invalid testcases.</div>
        <div>+for i, tested_string in ipairs(invalid_testcases) do</div>
        <div>+</div>
        <div>+    -- We should raise an error in case</div>
        <div>+    -- pattern contains invalid characters.</div>
        <div>+</div>
        <div>+    local test_name = prefix .. "2." .. tostring(i)</div>
        <div>+    local test_itself = "SELECT 'abc' LIKE 'ab" ..
          tested_string .. "';"</div>
        <div>+    test:do_catchsql_test(test_name, test_itself,</div>
        <div>+                          {1, "LIKE or GLOB pattern can
          only contain UTF-8 characters"})</div>
        <div>+</div>
        <div>+    test_name = prefix .. "3." .. tostring(i)</div>
        <div>+    test_itself = "SELECT 'abc' LIKE 'abc" ..
          tested_string .. "';"</div>
        <div>+    test:do_catchsql_test(test_name, test_itself,</div>
        <div>+                          {1, "LIKE or GLOB pattern can
          only contain UTF-8 characters"})</div>
        <div>+</div>
        <div>+    test_name = prefix .. "4." .. tostring(i)</div>
        <div>+    test_itself = "SELECT 'abc' LIKE 'ab" .. tested_string
          .. "c';"</div>
        <div>+    test:do_catchsql_test(test_name, test_itself,</div>
        <div>+                          {1, "LIKE or GLOB pattern can
          only contain UTF-8 characters"})</div>
        <div>+</div>
        <div>+    -- Just skipping if row value predicand contains
          invalid character.</div>
      </div>
    </blockquote>
    What the predicand is? Is it a typo?<br></div></blockquote><div><br></div><div>You can find it in ANSI SQL: <row value predicand>.</div><div>Basically it's just operand inside of a predicate.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div bgcolor="#FFFFFF">
    <blockquote type="cite">
      <div dir="ltr">
        <div>+</div>
        <div>+    test_name = prefix .. "5." .. tostring(i)</div>
        <div>+    test_itself = "SELECT 'ab" .. tested_string .. "' LIKE
          'abc';"</div>
        <div>+    test:do_execsql_test(test_name, test_itself, {0})</div>
        <div>+</div>
        <div>+    test_name = prefix .. "6." .. tostring(i)</div>
        <div>+    test_itself = "SELECT 'abc" .. tested_string .. "'
          LIKE 'abc';"</div>
        <div>+    test:do_execsql_test(test_name, test_itself, {0})</div>
        <div>+</div>
        <div>+    test_name = prefix .. "7." .. tostring(i)</div>
        <div>+    test_itself = "SELECT 'ab" .. tested_string .. "c'
          LIKE 'abc';"</div>
        <div>+    test:do_execsql_test(test_name, test_itself, {0})</div>
        <div>+end</div>
        <div>+</div>
        <div>+-- Valid testcases.</div>
        <div>+for i, tested_string in ipairs(valid_testcases) do</div>
        <div>+    test_name = prefix .. "8." .. tostring(i)</div>
        <div>+    local test_itself = "SELECT 'abc' LIKE 'ab" ..
          tested_string .. "';"</div>
        <div>+    test:do_execsql_test(test_name, test_itself, {0})</div>
        <div>+</div>
        <div>+    test_name = prefix .. "9." .. tostring(i)</div>
        <div>+    test_itself = "SELECT 'abc' LIKE 'abc" ..
          tested_string .. "';"</div>
        <div>+    test:do_execsql_test(test_name, test_itself, {0})</div>
        <div>+</div>
        <div>+    test_name = prefix .. "10." .. tostring(i)</div>
        <div>+    test_itself = "SELECT 'abc' LIKE 'ab" .. tested_string
          .. "c';"</div>
        <div>+    test:do_execsql_test(test_name,<span style="white-space:pre-wrap">    </span>test_itself,
          {0})</div>
        <div>+</div>
        <div>+    test_name = prefix .. "11." .. tostring(i)</div>
        <div>+    test_itself = "SELECT 'ab" .. tested_string .. "' LIKE
          'abc';"</div>
        <div>+    test:do_execsql_test(test_name,<span style="white-space:pre-wrap">    </span>test_itself,
          {0})</div>
        <div>+</div>
        <div>+    test_name = prefix .. "12." .. tostring(i)</div>
        <div>+    test_itself = "SELECT 'abc" .. tested_string .. "'
          LIKE 'abc';"</div>
        <div>+    test:do_execsql_test(test_name, test_itself, {0})</div>
        <div>+</div>
        <div>+    test_name = prefix .. "13." .. tostring(i)</div>
        <div>+    test_itself = "SELECT 'ab" .. tested_string .. "c'
          LIKE 'abc';"</div>
        <div>+    test:do_execsql_test(test_name, test_itself, {0})</div>
        <div>+end</div>
        <div>+</div>
        <div>+test:finish_test()</div>
      </div>
    </blockquote>
    Why I cannot find a test of `GLOB`? Even if we delete it in the
    future, it should be tested. You can write much less tests for glob.</div></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div bgcolor="#FFFFFF">
    E.g. this<br>
    ```<br>
    select '1' glob '[0-4]';<br>
    ```<br>
    somewhy returns 0.</div></blockquote><div><br></div><div><div style="font-size:small;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial"> </div><div style="font-size:small;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial">I actually don't think that operator that is going to be deleted in a few days should be tested.</div><div style="font-size:small;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial">It's just useless and redundant code.</div></div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div bgcolor="#FFFFFF">
    <br>
    Sorry, some of the tests I ask you to write are a little out of
    scope of the ticket and they should already have been written.<br>
    But I suppose most of ambiguity should be clarified now. This ticket
    has raised important questions related to those functions.<br></div></blockquote><div><br></div><div><div>commit f15fbaef2182084dec7cdc6c661509cb908df892</div><div>Author: N.Tatunov <<a href="mailto:hollow653@gmail.com">hollow653@gmail.com</a>></div><div>Date:   Thu Jun 28 15:17:32 2018 +0300</div><div><br></div><div>    sql: LIKE & GLOB pattern comparison issue</div><div>    </div><div>    Currently function that compares pattern and string for GLOB & LIKE</div><div>    operators doesn't work properly. It uses ICU reading function which</div><div>    was assumed having other return codes and the implementation for the</div><div>    comparison ending isn't paying attention to some special cases, hence</div><div>    in those cases it works improperly.</div><div>    </div><div>    With the patch applied an error will be returned in case there's an</div><div>    invalid UTF-8 symbol in pattern & pattern containing only valid UTF-8</div><div>    symbols will not be matched with the string that contains invalid</div><div>    symbol.</div><div>    </div><div>    Сloses #3251</div><div>    Сloses #3334</div><div>    Part of #3572</div><div><br></div><div>diff --git a/src/box/sql/func.c b/src/box/sql/func.c</div><div>index c06e3bd..7f93ef6 100644</div><div>--- a/src/box/sql/func.c</div><div>+++ b/src/box/sql/func.c</div><div>@@ -617,13 +617,17 @@ struct compareInfo {</div><div> <span style="white-space:pre">     </span>u8 noCase;<span style="white-space:pre">           </span>/* true to ignore case differences */</div><div> };</div><div> </div><div>-/*</div><div>- * For LIKE and GLOB matching on EBCDIC machines, assume that every</div><div>- * character is exactly one byte in size.  Also, provde the Utf8Read()</div><div>- * macro for fast reading of the next character in the common case where</div><div>- * the next character is ASCII.</div><div>+/**</div><div>+ * Providing there are symbols in string s this</div><div>+ * macro returns UTF-8 code of character and</div><div>+ * promotes pointer to the next symbol in the string.</div><div>+ * Otherwise return code is SQL_END_OF_STRING.</div><div>  */</div><div>-#define Utf8Read(s, e)    ucnv_getNextUChar(pUtf8conv, &s, e, &status)</div><div>+#define Utf8Read(s, e) (((s) < (e)) ? \</div><div>+<span style="white-space:pre">  </span>ucnv_getNextUChar(pUtf8conv, &(s), (e), &(status)) : 0)</div><div>+</div><div>+#define SQL_END_OF_STRING        0</div><div>+#define SQL_INVALID_UTF8_SYMBOL  0xfffd</div><div> </div><div> static const struct compareInfo globInfo = { '*', '?', '[', 0 };</div><div> </div><div>@@ -638,19 +642,16 @@ static const struct compareInfo likeInfoNorm = { '%', '_', 0, 1 };</div><div> static const struct compareInfo likeInfoAlt = { '%', '_', 0, 0 };</div><div> </div><div> /*</div><div>- * Possible error returns from patternMatch()</div><div>+ * Possible error returns from sql_utf8_pattern_compare()</div><div>  */</div><div> #define SQLITE_MATCH             0</div><div> #define SQLITE_NOMATCH           1</div><div> #define SQLITE_NOWILDCARDMATCH   2</div><div>+#define SQL_PROHIBITED_PATTERN   3</div><div> </div><div>-/*</div><div>- * Compare two UTF-8 strings for equality where the first string is</div><div>- * a GLOB or LIKE expression.  Return values:</div><div>- *</div><div>- *    SQLITE_MATCH:            Match</div><div>- *    SQLITE_NOMATCH:          No match</div><div>- *    SQLITE_NOWILDCARDMATCH:  No match in spite of having * or % wildcards.</div><div>+/**</div><div>+ * Compare two UTF-8 strings for equality where the first string</div><div>+ * is a GLOB or LIKE expression.</div><div>  *</div><div>  * Globbing rules:</div><div>  *</div><div>@@ -663,92 +664,136 @@ static const struct compareInfo likeInfoAlt = { '%', '_', 0, 0 };</div><div>  *</div><div>  *     [^...]     Matches one character not in the enclosed list.</div><div>  *</div><div>- * With the [...] and [^...] matching, a ']' character can be included</div><div>- * in the list by making it the first character after '[' or '^'.  A</div><div>- * range of characters can be specified using '-'.  Example:</div><div>- * "[a-z]" matches any single lower-case letter.  To match a '-', make</div><div>- * it the last character in the list.</div><div>+ * With the [...] and [^...] matching, a ']' character can be</div><div>+ * included in the list by making it the first character after</div><div>+ * '[' or '^'. A range of characters can be specified using '-'.</div><div>+ * Example: "[a-z]" matches any single lower-case letter.</div><div>+ * To match a '-', make it the last character in the list.</div><div>  *</div><div>  * Like matching rules:</div><div>  *</div><div>- *      '%'       Matches any sequence of zero or more characters</div><div>+ *      '%'       Matches any sequence of zero or more characters.</div><div>  *</div><div>- **     '_'       Matches any one character</div><div>+ **     '_'       Matches any one character.</div><div>  *</div><div>  *      Ec        Where E is the "esc" character and c is any other</div><div>- *                character, including '%', '_', and esc, match exactly c.</div><div>+ *                character, including '%', '_', and esc, match</div><div>+ *                exactly c.</div><div>  *</div><div>  * The comments within this routine usually assume glob matching.</div><div>  *</div><div>- * This routine is usually quick, but can be N**2 in the worst case.</div><div>+ * This routine is usually quick, but can be N**2 in the worst</div><div>+ * case.</div><div>+ *</div><div>+ * @param pattern String containing comparison pattern.</div><div>+ * @param string String being compared.</div><div>+ * @param compareInfo Information about how to compare.</div><div>+ * @param matchOther The escape char (LIKE) or '[' (GLOB).</div><div>+ *</div><div>+ * @retval SQLITE_MATCH:            Match.</div><div>+ *<span style="white-space:pre"> </span>   SQLITE_NOMATCH:          No match.</div><div>+ *<span style="white-space:pre">       </span>   SQLITE_NOWILDCARDMATCH:  No match in spite of having *</div><div>+ *<span style="white-space:pre">                               </span>    or % wildcards.</div><div>+ *<span style="white-space:pre">      </span>   SQL_PROHIBITED_PATTERN:  Pattern contains invalid</div><div>+ *<span style="white-space:pre">                            </span>    symbol.</div><div>  */</div><div> static int</div><div>-patternCompare(const char * pattern,<span style="white-space:pre">     </span>/* The glob pattern */</div><div>-<span style="white-space:pre">       </span>       const char * string,<span style="white-space:pre">      </span>/* The string to compare against the glob */</div><div>-<span style="white-space:pre"> </span>       const struct compareInfo *pInfo,<span style="white-space:pre">  </span>/* Information about how to do the compare */</div><div>-<span style="white-space:pre">        </span>       UChar32 matchOther<span style="white-space:pre">        </span>/* The escape char (LIKE) or '[' (GLOB) */</div><div>-    )</div><div>+sql_utf8_pattern_compare(const char * pattern,</div><div>+<span style="white-space:pre">                      </span> const char * string,</div><div>+<span style="white-space:pre">                        </span> const struct compareInfo *pInfo,</div><div>+<span style="white-space:pre">                    </span> UChar32 matchOther)</div><div> {</div><div>-<span style="white-space:pre">       </span>UChar32 c, c2;<span style="white-space:pre">               </span>/* Next pattern and input string chars */</div><div>-<span style="white-space:pre">    </span>UChar32 matchOne = pInfo->matchOne;<span style="white-space:pre">       </span>/* "?" or "_" */</div><div>-<span style="white-space:pre"> </span>UChar32 matchAll = pInfo->matchAll;<span style="white-space:pre">       </span>/* "*" or "%" */</div><div>-<span style="white-space:pre"> </span>UChar32 noCase = pInfo->noCase;<span style="white-space:pre">   </span>/* True if uppercase==lowercase */</div><div>-<span style="white-space:pre">   </span>const char *zEscaped = 0;<span style="white-space:pre">    </span>/* One past the last escaped input char */</div><div>+<span style="white-space:pre">   </span>/* Next pattern and input string chars */</div><div>+<span style="white-space:pre">    </span>UChar32 c, c2;</div><div>+<span style="white-space:pre">       </span>/* "?" or "_" */</div><div>+<span style="white-space:pre"> </span>UChar32 matchOne = pInfo->matchOne;</div><div>+<span style="white-space:pre">       </span>/* "*" or "%" */</div><div>+<span style="white-space:pre"> </span>UChar32 matchAll = pInfo->matchAll;</div><div>+<span style="white-space:pre">       </span>/* True if uppercase==lowercase */</div><div>+<span style="white-space:pre">   </span>UChar32 noCase = pInfo->noCase;</div><div>+<span style="white-space:pre">   </span>/* One past the last escaped input char */</div><div>+<span style="white-space:pre">   </span>const char *zEscaped = 0;</div><div> <span style="white-space:pre">   </span>const char * pattern_end = pattern + strlen(pattern);</div><div> <span style="white-space:pre">       </span>const char * string_end = string + strlen(string);</div><div> <span style="white-space:pre">  </span>UErrorCode status = U_ZERO_ERROR;</div><div> </div><div>-<span style="white-space:pre">   </span>while (pattern < pattern_end){</div><div>-<span style="white-space:pre">            </span>c = Utf8Read(pattern, pattern_end);</div><div>+<span style="white-space:pre">  </span>while ((c = Utf8Read(pattern, pattern_end)) != SQL_END_OF_STRING) {</div><div>+<span style="white-space:pre">          </span>if (c == SQL_INVALID_UTF8_SYMBOL)</div><div>+<span style="white-space:pre">                    </span>return SQL_PROHIBITED_PATTERN;</div><div> <span style="white-space:pre">              </span>if (c == matchAll) {<span style="white-space:pre"> </span>/* Match "*" */</div><div>-<span style="white-space:pre">                    </span>/* Skip over multiple "*" characters in the pattern.  If there</div><div>-<span style="white-space:pre">                    </span> * are also "?" characters, skip those as well, but consume a</div><div>-<span style="white-space:pre">                      </span> * single character of the input string for each "?" skipped</div><div>+<span style="white-space:pre">                       </span>/* Skip over multiple "*" characters in</div><div>+<span style="white-space:pre">                    </span> * the pattern. If there are also "?"</div><div>+<span style="white-space:pre">                      </span> * characters, skip those as well, but</div><div>+<span style="white-space:pre">                       </span> * consume a single character of the</div><div>+<span style="white-space:pre">                 </span> * input string for each "?" skipped.</div><div> <span style="white-space:pre">                     </span> */</div><div>-<span style="white-space:pre">                  </span>while (pattern < pattern_end){</div><div>-<span style="white-space:pre">                            </span>c = Utf8Read(pattern, pattern_end);</div><div>+<span style="white-space:pre">                  </span>while ((c = Utf8Read(pattern, pattern_end)) !=</div><div>+<span style="white-space:pre">                       </span>       SQL_END_OF_STRING) {</div><div>+<span style="white-space:pre">                              </span>if (c == SQL_INVALID_UTF8_SYMBOL)</div><div>+<span style="white-space:pre">                                    </span>return SQL_PROHIBITED_PATTERN;</div><div> <span style="white-space:pre">                              </span>if (c != matchAll && c != matchOne)</div><div> <span style="white-space:pre">                                 </span>break;</div><div>-<span style="white-space:pre">                               </span>if (c == matchOne</div><div>-<span style="white-space:pre">                            </span>    && Utf8Read(string, string_end) == 0) {</div><div>+<span style="white-space:pre">                                </span>if (c == matchOne &&</div><div>+<span style="white-space:pre">                         </span>    (c2 = Utf8Read(string, string_end)) ==</div><div>+<span style="white-space:pre">                         </span>    SQL_END_OF_STRING)</div><div> <span style="white-space:pre">                                    </span>return SQLITE_NOWILDCARDMATCH;</div><div>-<span style="white-space:pre">                               </span>}</div><div>+<span style="white-space:pre">                            </span>if (c2 == SQL_INVALID_UTF8_SYMBOL)</div><div>+<span style="white-space:pre">                                   </span>return SQLITE_NOMATCH;</div><div> <span style="white-space:pre">                      </span>}</div><div>-<span style="white-space:pre">                    </span>/* "*" at the end of the pattern matches */</div><div>-<span style="white-space:pre">                        </span>if (pattern == pattern_end)</div><div>+<span style="white-space:pre">                  </span>/*</div><div>+<span style="white-space:pre">                   </span> * "*" at the end of the pattern matches.</div><div>+<span style="white-space:pre">                  </span> */</div><div>+<span style="white-space:pre">                  </span>if (c == SQL_END_OF_STRING) {</div><div>+<span style="white-space:pre">                                </span>while ((c2 = Utf8Read(string, string_end)) !=</div><div>+<span style="white-space:pre">                                </span>       SQL_END_OF_STRING)</div><div>+<span style="white-space:pre">                                        </span>if (c2 == SQL_INVALID_UTF8_SYMBOL)</div><div>+<span style="white-space:pre">                                           </span>return SQLITE_NOMATCH;</div><div> <span style="white-space:pre">                              </span>return SQLITE_MATCH;</div><div>+<span style="white-space:pre">                 </span>}</div><div> <span style="white-space:pre">                   </span>if (c == matchOther) {</div><div> <span style="white-space:pre">                              </span>if (pInfo->matchSet == 0) {</div><div> <span style="white-space:pre">                                      </span>c = Utf8Read(pattern, pattern_end);</div><div>-<span style="white-space:pre">                                  </span>if (c == 0)</div><div>+<span style="white-space:pre">                                  </span>if (c == SQL_INVALID_UTF8_SYMBOL)</div><div>+<span style="white-space:pre">                                            </span>return SQL_PROHIBITED_PATTERN;</div><div>+<span style="white-space:pre">                                       </span>if (c == SQL_END_OF_STRING)</div><div> <span style="white-space:pre">                                         </span>return SQLITE_NOWILDCARDMATCH;</div><div> <span style="white-space:pre">                              </span>} else {</div><div>-<span style="white-space:pre">                                     </span>/* "[...]" immediately follows the "*".  We have to do a slow</div><div>-<span style="white-space:pre">                                   </span> * recursive search in this case, but it is an unusual case.</div><div>+<span style="white-space:pre">                                 </span>/* "[...]" immediately</div><div>+<span style="white-space:pre">                                     </span> * follows the "*". We</div><div>+<span style="white-space:pre">                                     </span> * have to do a slow</div><div>+<span style="white-space:pre">                                 </span> * recursive search in</div><div>+<span style="white-space:pre">                                       </span> * this case, but it is</div><div>+<span style="white-space:pre">                                      </span> * an unusual case.</div><div> <span style="white-space:pre">                                 </span> */</div><div>-<span style="white-space:pre">                                  </span>assert(matchOther < 0x80);<span style="white-space:pre">        </span>/* '[' is a single-byte character */</div><div>+<span style="white-space:pre">                                 </span>assert(matchOther < 0x80);</div><div> <span style="white-space:pre">                                       </span>while (string < string_end) {</div><div> <span style="white-space:pre">                                            </span>int bMatch =</div><div>-<span style="white-space:pre">                                         </span>    patternCompare(&pattern[-1],</div><div>-<span style="white-space:pre">                                                               </span>   string,</div><div>-<span style="white-space:pre">                                                         </span>   pInfo,</div><div>-<span style="white-space:pre">                                                          </span>   matchOther);</div><div>+<span style="white-space:pre">                                            </span>    sql_utf8_pattern_compare(</div><div>+<span style="white-space:pre">                                                              </span>&pattern[-1],</div><div>+<span style="white-space:pre">                                                            </span>string,</div><div>+<span style="white-space:pre">                                                              </span>pInfo,</div><div>+<span style="white-space:pre">                                                               </span>matchOther);</div><div> <span style="white-space:pre">                                                </span>if (bMatch != SQLITE_NOMATCH)</div><div> <span style="white-space:pre">                                                       </span>return bMatch;</div><div>-<span style="white-space:pre">                                               </span>Utf8Read(string, string_end);</div><div>+<span style="white-space:pre">                                                </span>c = Utf8Read(string, string_end);</div><div>+<span style="white-space:pre">                                            </span>if (c == SQL_INVALID_UTF8_SYMBOL)</div><div>+<span style="white-space:pre">                                                    </span>return SQLITE_NOMATCH;</div><div> <span style="white-space:pre">                                      </span>}</div><div> <span style="white-space:pre">                                   </span>return SQLITE_NOWILDCARDMATCH;</div><div> <span style="white-space:pre">                              </span>}</div><div> <span style="white-space:pre">                   </span>}</div><div> </div><div>-<span style="white-space:pre">                   </span>/* At this point variable c contains the first character of the</div><div>-<span style="white-space:pre">                      </span> * pattern string past the "*".  Search in the input string for the</div><div>-<span style="white-space:pre">                       </span> * first matching character and recursively continue the match from</div><div>-<span style="white-space:pre">                  </span> * that point.</div><div>+<span style="white-space:pre">                       </span>/* At this point variable c contains the</div><div>+<span style="white-space:pre">                     </span> * first character of the pattern string</div><div>+<span style="white-space:pre">                     </span> * past the "*". Search in the input</div><div>+<span style="white-space:pre">                       </span> * string for the first matching</div><div>+<span style="white-space:pre">                     </span> * character and recursively continue the</div><div>+<span style="white-space:pre">                    </span> * match from that point.</div><div> <span style="white-space:pre">                   </span> *</div><div>-<span style="white-space:pre">                   </span> * For a case-insensitive search, set variable cx to be the same as</div><div>-<span style="white-space:pre">                  </span> * c but in the other case and search the input string for either</div><div>-<span style="white-space:pre">                    </span> * c or cx.</div><div>+<span style="white-space:pre">                  </span> * For a case-insensitive search, set</div><div>+<span style="white-space:pre">                        </span> * variable cx to be the same as c but in</div><div>+<span style="white-space:pre">                    </span> * the other case and search the input</div><div>+<span style="white-space:pre">                       </span> * string for either c or cx.</div><div> <span style="white-space:pre">                       </span> */</div><div> </div><div> <span style="white-space:pre">                        </span>int bMatch;</div><div>@@ -756,14 +801,18 @@ patternCompare(const char * pattern,<span style="white-space:pre"> </span>/* The glob pattern */</div><div> <span style="white-space:pre">                              </span>c = u_tolower(c);</div><div> <span style="white-space:pre">                   </span>while (string < string_end){</div><div> <span style="white-space:pre">                             </span>/**</div><div>-<span style="white-space:pre">                          </span> * This loop could have been implemented</div><div>-<span style="white-space:pre">                             </span> * without if converting c2 to lower case</div><div>-<span style="white-space:pre">                            </span> * (by holding c_upper and c_lower), however</div><div>-<span style="white-space:pre">                         </span> * it is implemented this way because lower</div><div>-<span style="white-space:pre">                          </span> * works better with German and Turkish</div><div>-<span style="white-space:pre">                              </span> * languages.</div><div>+<span style="white-space:pre">                                </span> * This loop could have been</div><div>+<span style="white-space:pre">                         </span> * implemented without if</div><div>+<span style="white-space:pre">                            </span> * converting c2 to lower case</div><div>+<span style="white-space:pre">                               </span> * by holding c_upper and</div><div>+<span style="white-space:pre">                            </span> * c_lower,however it is</div><div>+<span style="white-space:pre">                             </span> * implemented this way because</div><div>+<span style="white-space:pre">                              </span> * lower works better with German</div><div>+<span style="white-space:pre">                            </span> * and Turkish languages.</div><div> <span style="white-space:pre">                           </span> */</div><div> <span style="white-space:pre">                         </span>c2 = Utf8Read(string, string_end);</div><div>+<span style="white-space:pre">                           </span>if (c2 == SQL_INVALID_UTF8_SYMBOL)</div><div>+<span style="white-space:pre">                                   </span>return SQLITE_NOMATCH;</div><div> <span style="white-space:pre">                              </span>if (!noCase) {</div><div> <span style="white-space:pre">                                      </span>if (c2 != c)</div><div> <span style="white-space:pre">                                                </span>continue;</div><div>@@ -771,9 +820,10 @@ patternCompare(const char * pattern,<span style="white-space:pre">    </span>/* The glob pattern */</div><div> <span style="white-space:pre">                                      </span>if (c2 != c && u_tolower(c2) != c)</div><div> <span style="white-space:pre">                                          </span>continue;</div><div> <span style="white-space:pre">                           </span>}</div><div>-<span style="white-space:pre">                            </span>bMatch =</div><div>-<span style="white-space:pre">                             </span>    patternCompare(pattern, string,</div><div>-<span style="white-space:pre">                                                </span>   pInfo, matchOther);</div><div>+<span style="white-space:pre">                             </span>bMatch = sql_utf8_pattern_compare(pattern,</div><div>+<span style="white-space:pre">                                                           </span>  string,</div><div>+<span style="white-space:pre">                                                           </span>  pInfo,</div><div>+<span style="white-space:pre">                                                            </span>  matchOther);</div><div> <span style="white-space:pre">                             </span>if (bMatch != SQLITE_NOMATCH)</div><div> <span style="white-space:pre">                                       </span>return bMatch;</div><div> <span style="white-space:pre">                      </span>}</div><div>@@ -782,7 +832,9 @@ patternCompare(const char * pattern,<span style="white-space:pre">     </span>/* The glob pattern */</div><div> <span style="white-space:pre">              </span>if (c == matchOther) {</div><div> <span style="white-space:pre">                      </span>if (pInfo->matchSet == 0) {</div><div> <span style="white-space:pre">                              </span>c = Utf8Read(pattern, pattern_end);</div><div>-<span style="white-space:pre">                          </span>if (c == 0)</div><div>+<span style="white-space:pre">                          </span>if (c == SQL_INVALID_UTF8_SYMBOL)</div><div>+<span style="white-space:pre">                                    </span>return SQL_PROHIBITED_PATTERN;</div><div>+<span style="white-space:pre">                               </span>if (c == SQL_END_OF_STRING)</div><div> <span style="white-space:pre">                                 </span>return SQLITE_NOMATCH;</div><div> <span style="white-space:pre">                              </span>zEscaped = pattern;</div><div> <span style="white-space:pre">                 </span>} else {</div><div>@@ -790,23 +842,33 @@ patternCompare(const char * pattern,<span style="white-space:pre">    </span>/* The glob pattern */</div><div> <span style="white-space:pre">                              </span>int seen = 0;</div><div> <span style="white-space:pre">                               </span>int invert = 0;</div><div> <span style="white-space:pre">                             </span>c = Utf8Read(string, string_end);</div><div>+<span style="white-space:pre">                            </span>if (c == SQL_INVALID_UTF8_SYMBOL)</div><div>+<span style="white-space:pre">                                    </span>return SQLITE_NOMATCH;</div><div> <span style="white-space:pre">                              </span>if (string == string_end)</div><div> <span style="white-space:pre">                                   </span>return SQLITE_NOMATCH;</div><div> <span style="white-space:pre">                              </span>c2 = Utf8Read(pattern, pattern_end);</div><div>+<span style="white-space:pre">                         </span>if (c2 == SQL_INVALID_UTF8_SYMBOL)</div><div>+<span style="white-space:pre">                                   </span>return SQL_PROHIBITED_PATTERN;</div><div> <span style="white-space:pre">                              </span>if (c2 == '^') {</div><div> <span style="white-space:pre">                                    </span>invert = 1;</div><div> <span style="white-space:pre">                                 </span>c2 = Utf8Read(pattern, pattern_end);</div><div>+<span style="white-space:pre">                                 </span>if (c2 == SQL_INVALID_UTF8_SYMBOL)</div><div>+<span style="white-space:pre">                                           </span>return SQL_PROHIBITED_PATTERN;</div><div> <span style="white-space:pre">                              </span>}</div><div> <span style="white-space:pre">                           </span>if (c2 == ']') {</div><div> <span style="white-space:pre">                                    </span>if (c == ']')</div><div> <span style="white-space:pre">                                               </span>seen = 1;</div><div> <span style="white-space:pre">                                   </span>c2 = Utf8Read(pattern, pattern_end);</div><div>+<span style="white-space:pre">                                 </span>if (c2 == SQL_INVALID_UTF8_SYMBOL)</div><div>+<span style="white-space:pre">                                           </span>return SQL_PROHIBITED_PATTERN;</div><div> <span style="white-space:pre">                              </span>}</div><div>-<span style="white-space:pre">                            </span>while (c2 && c2 != ']') {</div><div>+<span style="white-space:pre">                            </span>while (c2 != SQL_END_OF_STRING && c2 != ']') {</div><div> <span style="white-space:pre">                                      </span>if (c2 == '-' && pattern[0] != ']'</div><div> <span style="white-space:pre">                                  </span>    && pattern < pattern_end</div><div> <span style="white-space:pre">                                   </span>    && prior_c > 0) {</div><div> <span style="white-space:pre">                                          </span>c2 = Utf8Read(pattern, pattern_end);</div><div>+<span style="white-space:pre">                                         </span>if (c2 == SQL_INVALID_UTF8_SYMBOL)</div><div>+<span style="white-space:pre">                                                   </span>return SQL_PROHIBITED_PATTERN;</div><div> <span style="white-space:pre">                                              </span>if (c >= prior_c && c <= c2)</div><div> <span style="white-space:pre">                                                  </span>seen = 1;</div><div> <span style="white-space:pre">                                           </span>prior_c = 0;</div><div>@@ -817,29 +879,36 @@ patternCompare(const char * pattern,<span style="white-space:pre">        </span>/* The glob pattern */</div><div> <span style="white-space:pre">                                              </span>prior_c = c2;</div><div> <span style="white-space:pre">                                       </span>}</div><div> <span style="white-space:pre">                                   </span>c2 = Utf8Read(pattern, pattern_end);</div><div>+<span style="white-space:pre">                                 </span>if (c2 == SQL_INVALID_UTF8_SYMBOL)</div><div>+<span style="white-space:pre">                                           </span>return SQL_PROHIBITED_PATTERN;</div><div> <span style="white-space:pre">                              </span>}</div><div>-<span style="white-space:pre">                            </span>if (pattern == pattern_end || (seen ^ invert) == 0) {</div><div>+<span style="white-space:pre">                                </span>if (pattern == pattern_end ||</div><div>+<span style="white-space:pre">                                </span>    (seen ^ invert) == 0) {</div><div> <span style="white-space:pre">                                       </span>return SQLITE_NOMATCH;</div><div> <span style="white-space:pre">                              </span>}</div><div> <span style="white-space:pre">                           </span>continue;</div><div> <span style="white-space:pre">                   </span>}</div><div> <span style="white-space:pre">           </span>}</div><div> <span style="white-space:pre">           </span>c2 = Utf8Read(string, string_end);</div><div>+<span style="white-space:pre">           </span>if (c2 == SQL_INVALID_UTF8_SYMBOL)</div><div>+<span style="white-space:pre">                   </span>return SQLITE_NOMATCH;</div><div> <span style="white-space:pre">              </span>if (c == c2)</div><div> <span style="white-space:pre">                        </span>continue;</div><div> <span style="white-space:pre">           </span>if (noCase){</div><div> <span style="white-space:pre">                        </span>/**</div><div>-<span style="white-space:pre">                  </span> * Small optimisation. Reduce number of calls</div><div>-<span style="white-space:pre">                        </span> * to u_tolower function.</div><div>-<span style="white-space:pre">                    </span> * SQL standards suggest use to_upper for symbol</div><div>-<span style="white-space:pre">                     </span> * normalisation. However, using to_lower allows to</div><div>-<span style="white-space:pre">                  </span> * respect Turkish 'İ' in default locale.</div><div>+<span style="white-space:pre">                   </span> * Small optimisation. Reduce number of</div><div>+<span style="white-space:pre">                      </span> * calls to u_tolower function. SQL</div><div>+<span style="white-space:pre">                  </span> * standards suggest use to_upper for</div><div>+<span style="white-space:pre">                        </span> * symbol normalisation. However, using</div><div>+<span style="white-space:pre">                      </span> * to_lower allows to respect Turkish 'İ'</div><div>+<span style="white-space:pre">                   </span> * in default locale.</div><div> <span style="white-space:pre">                       </span> */</div><div> <span style="white-space:pre">                 </span>if (u_tolower(c) == c2 ||</div><div> <span style="white-space:pre">                   </span>    c == u_tolower(c2))</div><div> <span style="white-space:pre">                           </span>continue;</div><div> <span style="white-space:pre">           </span>}</div><div>-<span style="white-space:pre">            </span>if (c == matchOne && pattern != zEscaped && c2 != 0)</div><div>+<span style="white-space:pre">         </span>if (c == matchOne && pattern != zEscaped &&</div><div>+<span style="white-space:pre">          </span>    c2 != SQL_END_OF_STRING)</div><div> <span style="white-space:pre">                      </span>continue;</div><div> <span style="white-space:pre">           </span>return SQLITE_NOMATCH;</div><div> <span style="white-space:pre">      </span>}</div><div>@@ -853,8 +922,7 @@ patternCompare(const char * pattern,<span style="white-space:pre">     </span>/* The glob pattern */</div><div> int</div><div> sqlite3_strglob(const char *zGlobPattern, const char *zString)</div><div> {</div><div>-<span style="white-space:pre">  </span>return patternCompare(zGlobPattern, zString, &globInfo,</div><div>-<span style="white-space:pre">                  </span>      '[');</div><div>+<span style="white-space:pre">       </span>return sql_utf8_pattern_compare(zGlobPattern, zString, &globInfo, '[');</div><div> }</div><div> </div><div> /*</div><div>@@ -864,7 +932,7 @@ sqlite3_strglob(const char *zGlobPattern, const char *zString)</div><div> int</div><div> sqlite3_strlike(const char *zPattern, const char *zStr, unsigned int esc)</div><div> {</div><div>-<span style="white-space:pre">   </span>return patternCompare(zPattern, zStr, &likeInfoNorm, esc);</div><div>+<span style="white-space:pre">       </span>return sql_utf8_pattern_compare(zPattern, zStr, &likeInfoNorm, esc);</div><div> }</div><div> </div><div> /*</div><div>@@ -910,8 +978,9 @@ likeFunc(sqlite3_context * context, int argc, sqlite3_value ** argv)</div><div> <span style="white-space:pre">       </span>zB = (const char *) sqlite3_value_text(argv[0]);</div><div> <span style="white-space:pre">    </span>zA = (const char *) sqlite3_value_text(argv[1]);</div><div> </div><div>-<span style="white-space:pre">    </span>/* Limit the length of the LIKE or GLOB pattern to avoid problems</div><div>-<span style="white-space:pre">    </span> * of deep recursion and N*N behavior in patternCompare().</div><div>+<span style="white-space:pre">   </span>/* Limit the length of the LIKE or GLOB pattern to avoid</div><div>+<span style="white-space:pre">     </span> * problems of deep recursion and N*N behavior in</div><div>+<span style="white-space:pre">    </span> * sql_utf8_pattern_compare().</div><div> <span style="white-space:pre">      </span> */</div><div> <span style="white-space:pre"> </span>nPat = sqlite3_value_bytes(argv[0]);</div><div> <span style="white-space:pre">        </span>testcase(nPat == db->aLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]);</div><div>@@ -947,7 +1016,12 @@ likeFunc(sqlite3_context * context, int argc, sqlite3_value ** argv)</div><div> <span style="white-space:pre"> </span>sqlite3_like_count++;</div><div> #endif</div><div> <span style="white-space:pre">        </span>int res;</div><div>-<span style="white-space:pre">     </span>res = patternCompare(zB, zA, pInfo, escape);</div><div>+<span style="white-space:pre"> </span>res = sql_utf8_pattern_compare(zB, zA, pInfo, escape);</div><div>+<span style="white-space:pre">       </span>if (res == SQL_PROHIBITED_PATTERN) {</div><div>+<span style="white-space:pre">         </span>sqlite3_result_error(context, "LIKE or GLOB pattern can only"</div><div>+<span style="white-space:pre">                              </span>     " contain UTF-8 characters", -1);</div><div>+<span style="white-space:pre">          </span>return;</div><div>+<span style="white-space:pre">      </span>}</div><div> <span style="white-space:pre">   </span>sqlite3_result_int(context, res == SQLITE_MATCH);</div><div> }</div><div> </div><div>diff --git a/test/sql-tap/e_expr.test.lua b/test/sql-tap/e_expr.test.lua</div><div>index 13d3a96..9780d2c 100755</div><div>--- a/test/sql-tap/e_expr.test.lua</div><div>+++ b/test/sql-tap/e_expr.test.lua</div><div>@@ -1,6 +1,6 @@</div><div> #!/usr/bin/env tarantool</div><div> test = require("sqltester")</div><div>-test:plan(12431)</div><div>+test:plan(10665)</div><div> </div><div> --!./tcltestrunner.lua</div><div> -- 2010 July 16</div><div>@@ -77,8 +77,10 @@ local operations = {</div><div>     {"<>", "ne1"},</div><div>     {"!=", "ne2"},</div><div>     {"IS", "is"},</div><div>-    {"LIKE", "like"},</div><div>-    {"GLOB", "glob"},</div><div>+-- NOTE: This test needs refactoring after deletion of GLOB &</div><div>+--<span style="white-space:pre">        </span> type restrictions for LIKE. (See #3572)</div><div>+--    {"LIKE", "like"},</div><div>+--    {"GLOB", "glob"},</div><div>     {"AND", "and"},</div><div>     {"OR", "or"},</div><div>     {"MATCH", "match"},</div><div>@@ -96,7 +98,12 @@ operations = {</div><div>     {"+", "-"},</div><div>     {"<<", ">>", "&", "|"},</div><div>     {"<", "<=", ">", ">="},</div><div>-    {"=", "==", "!=", "<>", "LIKE", "GLOB"}, --"MATCH", "REGEXP"},</div><div>+-- NOTE: This test needs refactoring after deletion of GLOB &</div><div>+--<span style="white-space:pre">        </span> type restrictions for LIKE. (See #3572)</div><div>+-- Another NOTE: MATCH & REGEXP aren't supported in Tarantool &</div><div>+-- <span style="white-space:pre">               </span> are waiting for their hour, don't confuse them</div><div>+--<span style="white-space:pre">                </span> being commented with ticket above.</div><div>+    {"=", "==", "!=", "<>"}, --"LIKE", "GLOB"}, --"MATCH", "REGEXP"},</div><div>     {"AND"},</div><div>     {"OR"},</div><div> }</div><div>@@ -475,6 +482,7 @@ for _, op in ipairs(oplist) do</div><div>         end</div><div>     end</div><div> end</div><div>+</div><div> ---------------------------------------------------------------------------</div><div> -- Test the IS and IS NOT operators.</div><div> --</div><div>diff --git a/test/sql-tap/gh-3251-string-pattern-comparison.test.lua b/test/sql-tap/gh-3251-string-pattern-comparison.test.lua</div><div>new file mode 100755</div><div>index 0000000..2a787f2</div><div>--- /dev/null</div><div>+++ b/test/sql-tap/gh-3251-string-pattern-comparison.test.lua</div><div>@@ -0,0 +1,213 @@</div><div>+#!/usr/bin/env tarantool</div><div>+test = require("sqltester")</div><div>+test:plan(128)</div><div>+</div><div>+local prefix = "like-test-"</div><div>+</div><div>+-- Unicode byte sequences.</div><div>+local valid_testcases = {</div><div>+    '\x01',</div><div>+    '\x09',</div><div>+    '\x1F',</div><div>+    '\x7F',</div><div>+    '\xC2\x80',</div><div>+    '\xC2\x90',</div><div>+    '\xC2\x9F',</div><div>+    '\xE2\x80\xA8',</div><div>+    '\x20\x0B',</div><div>+    '\xE2\x80\xA9',</div><div>+}</div><div>+</div><div>+-- Non-Unicode byte sequences.</div><div>+local invalid_testcases = {</div><div>+    '\xE2\x80',</div><div>+    '\xFE\xFF',</div><div>+    '\xC2',</div><div>+    '\xED\xB0\x80',</div><div>+    '\xD0',</div><div>+}</div><div>+</div><div>+local like_test_cases =</div><div>+{</div><div>+    {"1.1",</div><div>+        "SELECT 'AB' LIKE '_B';",</div><div>+        {0, {1}} },</div><div>+    {"1.2",</div><div>+        "SELECT 'CD' LIKE '_B';",</div><div>+        {0, {0}} },</div><div>+    {"1.3",</div><div>+        "SELECT '' LIKE '_B';",</div><div>+        {0, {0}} },</div><div>+    {"1.4",</div><div>+        "SELECT 'AB' LIKE '%B';",</div><div>+        {0, {1}} },</div><div>+    {"1.5",</div><div>+        "SELECT 'CD' LIKE '%B';",</div><div>+        {0, {0}} },</div><div>+    {"1.6",</div><div>+        "SELECT '' LIKE '%B';",</div><div>+        {0, {0}} },</div><div>+    {"1.7",</div><div>+        "SELECT 'AB' LIKE 'A__';",</div><div>+        {0, {0}} },</div><div>+    {"1.8",</div><div>+        "SELECT 'CD' LIKE 'A__';",</div><div>+        {0, {0}} },</div><div>+    {"1.9",</div><div>+        "SELECT '' LIKE 'A__';",</div><div>+        {0, {0}} },</div><div>+    {"1.10",</div><div>+        "SELECT 'AB' LIKE 'A_';",</div><div>+        {0, {1}} },</div><div>+    {"1.11",</div><div>+        "SELECT 'CD' LIKE 'A_';",</div><div>+        {0, {0}} },</div><div>+    {"1.12",</div><div>+        "SELECT '' LIKE 'A_';",</div><div>+        {0, {0}} },</div><div>+    {"1.13",</div><div>+        "SELECT 'AB' LIKE 'A';",</div><div>+        {0, {0}} },</div><div>+    {"1.14",</div><div>+        "SELECT 'CD' LIKE 'A';",</div><div>+        {0, {0}} },</div><div>+    {"1.15",</div><div>+        "SELECT '' LIKE 'A';",</div><div>+        {0, {0}} },</div><div>+    {"1.16",</div><div>+        "SELECT 'AB' LIKE '_';",</div><div>+        {0, {0}} },</div><div>+    {"1.17",</div><div>+        "SELECT 'CD' LIKE '_';",</div><div>+        {0, {0}} },</div><div>+    {"1.18",</div><div>+        "SELECT '' LIKE '_';",</div><div>+        {0, {0}} },</div><div>+    {"1.19",</div><div>+        "SELECT 'AB' LIKE '__';",</div><div>+        {0, {1}} },</div><div>+    {"1.20",</div><div>+        "SELECT 'CD' LIKE '__';",</div><div>+        {0, {1}} },</div><div>+    {"1.21",</div><div>+        "SELECT '' LIKE '__';",</div><div>+        {0, {0}} },</div><div>+    {"1.22",</div><div>+        "SELECT 'AB' LIKE '%A';",</div><div>+        {0, {0}} },</div><div>+    {"1.23",</div><div>+        "SELECT 'AB' LIKE '%C';",</div><div>+        {0, {0}} },</div><div>+    {"1.24",</div><div>+        "SELECT 'ab' LIKE '%df';",</div><div>+        {0, {0}} },</div><div>+    {"1.25",</div><div>+        "SELECT 'abCDF' LIKE '%df';",</div><div>+        {0, {1}} },</div><div>+    {"1.26",</div><div>+        "SELECT 'CDF' LIKE '%df';",</div><div>+        {0, {1}} },</div><div>+    {"1.27",</div><div>+        "SELECT 'ab' LIKE 'a_';",</div><div>+        {0, {1}} },</div><div>+    {"1.28",</div><div>+        "SELECT 'abCDF' LIKE 'a_';",</div><div>+        {0, {0}} },</div><div>+    {"1.29",</div><div>+        "SELECT 'CDF' LIKE 'a_';",</div><div>+        {0, {0}} },</div><div>+    {"1.30",</div><div>+        "SELECT 'ab' LIKE 'ab%';",</div><div>+        {0, {1}} },</div><div>+    {"1.31",</div><div>+        "SELECT 'abCDF' LIKE 'ab%';",</div><div>+        {0, {1}} },</div><div>+    {"1.32",</div><div>+        "SELECT 'CDF' LIKE 'ab%';",</div><div>+        {0, {0}} },</div><div>+    {"1.33",</div><div>+        "SELECT 'ab' LIKE 'abC%';",</div><div>+        {0, {0}} },</div><div>+    {"1.34",</div><div>+        "SELECT 'abCDF' LIKE 'abC%';",</div><div>+        {0, {1}} },</div><div>+    {"1.35",</div><div>+        "SELECT 'CDF' LIKE 'abC%';",</div><div>+        {0, {0}} },</div><div>+    {"1.36",</div><div>+        "SELECT 'ab' LIKE 'a_%';",</div><div>+        {0, {1}} },</div><div>+    {"1.37",</div><div>+        "SELECT 'abCDF' LIKE 'a_%';",</div><div>+        {0, {1}} },</div><div>+    {"1.38",</div><div>+        "SELECT 'CDF' LIKE 'a_%';",</div><div>+        {0, {0}} },</div><div>+}</div><div>+</div><div>+test:do_catchsql_set_test(like_test_cases, prefix)</div><div>+</div><div>+-- Invalid testcases.</div><div>+for i, tested_string in ipairs(invalid_testcases) do</div><div>+</div><div>+    -- We should raise an error in case</div><div>+    -- pattern contains invalid characters.</div><div>+</div><div>+    local test_name = prefix .. "2." .. tostring(i)</div><div>+    local test_itself = "SELECT 'abc' LIKE 'ab" .. tested_string .. "';"</div><div>+    test:do_catchsql_test(test_name, test_itself,</div><div>+                          {1, "LIKE or GLOB pattern can only contain UTF-8 characters"})</div><div>+</div><div>+    test_name = prefix .. "3." .. tostring(i)</div><div>+    test_itself = "SELECT 'abc' LIKE 'abc" .. tested_string .. "';"</div><div>+    test:do_catchsql_test(test_name, test_itself,</div><div>+                          {1, "LIKE or GLOB pattern can only contain UTF-8 characters"})</div><div>+</div><div>+    test_name = prefix .. "4." .. tostring(i)</div><div>+    test_itself = "SELECT 'abc' LIKE 'ab" .. tested_string .. "c';"</div><div>+    test:do_catchsql_test(test_name, test_itself,</div><div>+                          {1, "LIKE or GLOB pattern can only contain UTF-8 characters"})</div><div>+</div><div>+    -- Just skipping if row value predicand contains invalid character.</div><div>+</div><div>+    test_name = prefix .. "5." .. tostring(i)</div><div>+    test_itself = "SELECT 'ab" .. tested_string .. "' LIKE 'abc';"</div><div>+    test:do_execsql_test(test_name, test_itself, {0})</div><div>+</div><div>+    test_name = prefix .. "6." .. tostring(i)</div><div>+    test_itself = "SELECT 'abc" .. tested_string .. "' LIKE 'abc';"</div><div>+    test:do_execsql_test(test_name, test_itself, {0})</div><div>+</div><div>+    test_name = prefix .. "7." .. tostring(i)</div><div>+    test_itself = "SELECT 'ab" .. tested_string .. "c' LIKE 'abc';"</div><div>+    test:do_execsql_test(test_name, test_itself, {0})</div><div>+end</div><div>+</div><div>+-- Valid testcases.</div><div>+for i, tested_string in ipairs(valid_testcases) do</div><div>+    test_name = prefix .. "8." .. tostring(i)</div><div>+    local test_itself = "SELECT 'abc' LIKE 'ab" .. tested_string .. "';"</div><div>+    test:do_execsql_test(test_name, test_itself, {0})</div><div>+</div><div>+    test_name = prefix .. "9." .. tostring(i)</div><div>+    test_itself = "SELECT 'abc' LIKE 'abc" .. tested_string .. "';"</div><div>+    test:do_execsql_test(test_name, test_itself, {0})</div><div>+</div><div>+    test_name = prefix .. "10." .. tostring(i)</div><div>+    test_itself = "SELECT 'abc' LIKE 'ab" .. tested_string .. "c';"</div><div>+    test:do_execsql_test(test_name,<span style="white-space:pre"> </span>test_itself, {0})</div><div>+</div><div>+    test_name = prefix .. "11." .. tostring(i)</div><div>+    test_itself = "SELECT 'ab" .. tested_string .. "' LIKE 'abc';"</div><div>+    test:do_execsql_test(test_name,<span style="white-space:pre">     </span>test_itself, {0})</div><div>+</div><div>+    test_name = prefix .. "12." .. tostring(i)</div><div>+    test_itself = "SELECT 'abc" .. tested_string .. "' LIKE 'abc';"</div><div>+    test:do_execsql_test(test_name, test_itself, {0})</div><div>+</div><div>+    test_name = prefix .. "13." .. tostring(i)</div><div>+    test_itself = "SELECT 'ab" .. tested_string .. "c' LIKE 'abc';"</div><div>+    test:do_execsql_test(test_name, test_itself, {0})</div><div>+end</div><div>+</div><div>+test:finish_test()</div></div><div><br></div></div></div>