MySQL 源码|词法解析:状态及状态转移规则(5)

源码位置:(版本 = MySQL 8.0.37)

前置文档:

  • MySQL 源码|词法解析的状态存储器(Lex_input_stream)的主要数据成员与函数
  • MySQL 源码|词法解析中的 CHARSET_INFO 结构体及衍生函数
  • MySQL 源码|词法解析:状态及状态转移规则(1)
  • MySQL 源码|词法解析:状态及状态转移规则(2)
  • MySQL 源码|词法解析:状态及状态转移规则(3)
  • MySQL 源码|词法解析:状态及状态转移规则(4)
MY_LEX_USER_VARIABLE_DELIMITER:在 ` 引号中

Step 1|首先,获取当前字符即引号字符,并存储到 quote_char 中。然后,逐个遍历字符。如果已经遍历到字符串末尾(c == 0)仍然没有找到配对的括号,则返回 258(ABORT_SYM)。如果能够找到配对引号,且该引号后的元素不是引号,即没有被转义,则找到了完整引号字符串,结束遍历。

uint double_quotes = 0;
        char quote_char = c;  // Used char
        for (;;) {
          c = lip->yyGet();
          if (c == 0) {
            lip->yyUnget();
            return ABORT_SYM;  // Unmatched quotes
          }

          int var_length;
          if ((var_length = my_mbcharlen(cs, c)) == 1) {
            if (c == quote_char) {
              if (lip->yyPeek() != quote_char) break;
              c = lip->yyGet();
              double_quotes++;
              continue;
            }
          } else if (use_mb(cs)) {
            if ((var_length = my_ismbchar(cs, lip->get_ptr() - 1,
                                          lip->get_end_of_query())))
              lip->skip_binary(var_length - 1);
          }
        }

Step 2|根据是否包含转义符,调用不同方法获取当前 token 内容。

if (double_quotes)
          yylval->lex_str = get_quoted_token(
              lip, 1, lip->yyLength() - double_quotes - 1, quote_char);
        else
          yylval->lex_str = get_token(lip, 1, lip->yyLength() - 1);

Step 3|跳过配对的引号字符

if (c == quote_char) lip->yySkip();  // Skip end `

Step 4|将状态更新为 MY_LEX_START(开始状态),并更新 UTF-8 格式数据流。返回 484(IDENT_QUOTED)。

lip->next_state = MY_LEX_START;

        lip->body_utf8_append(lip->m_cpp_text_start);

        lip->body_utf8_append_literal(thd, &yylval->lex_str, cs,
                                      lip->m_cpp_text_end);

        return (IDENT_QUOTED);

涉及的状态转移规则如下:

当前状态

字符类型

变更状态

MY_LEX_USER_VARIABLE_DELIMITER

没有配对的引号

【返回】258(ABORT_SYM

其他

状态 = MY_LEX_START;【返回】484(IDENT_QUOTED

MY_LEX_INT_OR_REAL:数字之后,不是标识符

如果当前字符不是 .,则说明数值已经匹配完成,解析当前整数并返回。具体地,根据数值长度,返回 628(NUM)、545(LONG_NUM)、373(DECIMAL_NUM)、849(ULONGLONG_NUM)中的一个。

如果当前字符是 .,则说明当前数值还没有匹配完成,继续执行 MY_LEX_REAL 的逻辑,即相当于将状态置为 MY_LEX_REAL 并 break 出 switch 重新处理当前 token。

case MY_LEX_INT_OR_REAL:  // Complete int or incom、plete real
        if (c != '.') {         // Found complete integer number.
          yylval->lex_str = get_token(lip, 0, lip->yyLength());
          return int_token(yylval->lex_str.str, (uint)yylval->lex_str.length);
        }
        [[fallthrough]];
      case MY_LEX_REAL:  // Incomplete real number

涉及的状态转移规则如下:

当前状态

字符类型

变更状态

MY_LEX_INT_OR_REAL

当前字符不是 .

【返回】根据数值长度返回其中之一:628(NUM)、545(LONG_NUM)、373(DECIMAL_NUM)、849(ULONGLONG_NUM

其他

状态 = MY_LEX_REAL

MY_LEX_REAL:未完成的数值

Step 1|继续匹配剩余的数值

while (my_isdigit(cs, c = lip->yyGet()))
          ;

Step 2|如果当前字符为 eE,则跳过后续的 -+ 符号。若在此之后不是数字,则将状态置为 MY_LEX_CHAR(其他字符或单字符之后)并继续处理当前 token。如果为数字,则按 1e+10 格式继续遍历梳理,并返回 443(FLOAT_NUM)。

if (c == 'e' || c == 'E') {
          c = lip->yyGet();
          if (c == '-' || c == '+') c = lip->yyGet();  // Skip sign
          if (!my_isdigit(cs, c)) {                    // No digit after sign
            state = MY_LEX_CHAR;
            break;
          }
          while (my_isdigit(cs, lip->yyGet()))
            ;
          yylval->lex_str = get_token(lip, 0, lip->yyLength());
          return (FLOAT_NUM);
        }

Step 3|如果当前字符不是 eE,则返回 373(DECIMAL_NUM)。

yylval->lex_str = get_token(lip, 0, lip->yyLength());
        return (DECIMAL_NUM);

涉及的状态转移规则如下:

当前状态

字符类型

变更状态

MY_LEX_REAL

如果数字后为 eE,且 eE 后不是数字

状态 = MY_LEX_CHAR

如果数字后为 eE,且 eE 后是数字

【返回】443(FLOAT_NUM

其他

【返回】373(DECIMAL_NUM

MY_LEX_HEX_NUMBER:十六进制数的 x' 之后

如果能够正确匹配 x'hexstring' 格式十六进制数,则返回 474(HEX_NUM);否则返回 258(ABORT_SYM)。

case MY_LEX_HEX_NUMBER:  // Found x'hexstring'
        lip->yySkip();         // Accept opening '
        while (my_isxdigit(cs, (c = lip->yyGet())))
          ;
        if (c != '\'') return (ABORT_SYM);          // Illegal hex constant
        lip->yySkip();                              // Accept closing '
        length = lip->yyLength();                   // Length of hexnum+3
        if ((length % 2) == 0) return (ABORT_SYM);  // odd number of hex digits
        yylval->lex_str = get_token(lip,
                                    2,            // skip x'
                                    length - 3);  // don't count x' and last '
        return (HEX_NUM);

涉及的状态转移规则如下:

当前状态

字符类型

变更状态

MY_LEX_HEX_NUMBER

能够匹配十六进制数格式

【返回】474(HEX_NUM

其他

【返回】258(ABORT_SYM

MY_LEX_BIN_NUMBER:二进制数的 b' 之后

如果能够正确匹配 b'binstring' 格式二进制数,则返回 292(BIN_NUM);否则返回 258(ABORT_SYM)。

case MY_LEX_BIN_NUMBER:  // Found b'bin-string'
        lip->yySkip();         // Accept opening '
        while ((c = lip->yyGet()) == '0' || c == '1')
          ;
        if (c != '\'') return (ABORT_SYM);  // Illegal hex constant
        lip->yySkip();                      // Accept closing '
        length = lip->yyLength();           // Length of bin-num + 3
        yylval->lex_str = get_token(lip,
                                    2,            // skip b'
                                    length - 3);  // don't count b' and last '
        return (BIN_NUM);

涉及的状态转移规则如下:

当前状态

字符类型

变更状态

MY_LEX_BIN_NUMBER

能够匹配二进制数格式

【返回】292(BIN_NUM

其他

【返回】258(ABORT_SYM