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

阅读 16

01-17 12:00


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

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

  • router/src/routing/src/sql_lexer.cc

前置文档:

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

如果下一个字符是 =,则构成 :=,返回 757(SET_VAR);否则,将状态置为 MY_LEX_CHAR 并继续处理当前 token。

case MY_LEX_SET_VAR:  // Check if ':='
        if (lip->yyPeek() != '=') {
          state = MY_LEX_CHAR;  // Return ':'
          break;
        }
        lip->yySkip();
        return (SET_VAR);

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

当前状态

字符类型

变更状态

MY_LEX_SET_VAR

下一个字符是 =

【返回】757(SET_VAR

其他

状态 = MY_LEX_CHAR

MY_LEX_SEMICOLON:在 ; 之后

如果在 ; 之后,则直接将状态置为 MY_LEX_CHAR 并继续处理当前 token。

case MY_LEX_SEMICOLON:  // optional line terminator
        state = MY_LEX_CHAR;  // Return ';'
        break;

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

当前状态

字符类型

变更状态

MY_LEX_SEMICOLON

-

状态 = MY_LEX_CHAR

MY_LEX_EOL:在 \x00 之后

如果原始数据流已经遍历结束(lip->eof() 返回 true):如果当前仍然在注释中,则返回 258(ABORT_SYM);否则,将当前状态改为 MY_LEX_END,并返回 441(END_OF_INPUT)。

如果原始数据流还没有遍历结束,则将状态置为 MY_LEX_CHAR 并继续处理当前 token。

【函数含义】lip->eof():判断原始数据流中当前指针位置是否已经越过结束指针,如果已经越过则返回 True,否则返回 False。

case MY_LEX_EOL:
        if (lip->eof()) {
          lip->yyUnget();  // Reject the last '\0'
          lip->set_echo(false);
          lip->yySkip();
          lip->set_echo(true);
          /* Unbalanced comments with a missing '*' '/' are a syntax error */
          if (lip->in_comment != NO_COMMENT) return (ABORT_SYM);
          lip->next_state = MY_LEX_END;  // Mark for next loop
          return (END_OF_INPUT);
        }
        state = MY_LEX_CHAR;
        break;

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

当前状态

字符类型

变更状态

MY_LEX_EOL

原始数据流已遍历结束,且不在注释中

状态 = MY_LEX_END;【返回】441(END_OF_INPUT

原始数据流已遍历结束,且在注释中

【返回】258(ABORT_SYM

原始数据流没有遍历结束

状态 = MY_LEX_CHAR

MY_LEX_END:查询语句已遍历完成

将状态保持为 MY_LEX_END 且不执行任何操作。

case MY_LEX_END:
        lip->next_state = MY_LEX_END;
        return (0);  // We found end of input last time

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

当前状态

字符类型

变更状态

MY_LEX_END

-

状态 = MY_LEX_END;【返回】0

MY_LEX_REAL_OR_POINT:在 . 之后

如果之后为数字,则将状态置为 MY_LEX_REAL 并继续处理当前 token;否则,将状态置为 MY_LEX_IDENT_SEP,把指针向前移动 1 个字符并继续处理当前 token。

/* Actually real shouldn't start with . but allow them anyhow */
      case MY_LEX_REAL_OR_POINT:
        if (my_isdigit(cs, lip->yyPeek()))
          state = MY_LEX_REAL;  // Real
        else {
          state = MY_LEX_IDENT_SEP;  // return '.'
          lip->yyUnget();            // Put back '.'
        }
        break;

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

当前状态

字符类型

变更状态

MY_LEX_REAL_OR_POINT

当前字符是数字

状态 = MY_LEX_REAL

其他

状态 = MY_LEX_IDENT_SEP

MY_LEX_USER_END:在 @ 之后

如果当前字符为单引号(MY_LEX_STRING)、标识符引号(MY_LEX_USER_VARIABLE_DELIMITER)或双引号(MY_LEX_STRING_OR_DELIMITER),则不改变状态,并返回 64(@ 的 ASCII 码值)。

如果当前字符为 @MY_LEX_USER_END),则将状态置为 MY_LEX_SYSTEM_VAR,并返回 64(@ 的 ASCII 码值)。

如果当前字符为其他字符,则将状态置为 MY_LEX_HOSTNAME,并返回 64(@ 的 ASCII 码值)。

case MY_LEX_USER_END:  // end '@' of user@hostname
        switch (state_map[lip->yyPeek()]) {
          case MY_LEX_STRING:
          case MY_LEX_USER_VARIABLE_DELIMITER:
          case MY_LEX_STRING_OR_DELIMITER:
            break;
          case MY_LEX_USER_END:
            lip->next_state = MY_LEX_SYSTEM_VAR;
            break;
          default:
            lip->next_state = MY_LEX_HOSTNAME;
            break;
        }
        yylval->lex_str.str = const_cast<char *>(lip->get_ptr());
        yylval->lex_str.length = 1;
        return ((int)'@');

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

当前状态

字符类型

变更状态

MY_LEX_USER_END

当前字符是单引号、标识符引号或双引号

【返回】64(@ 的 ASCII 码值)

当前字符是 @

状态 = MY_LEX_SYSTEM_VAR;【返回】64(@ 的 ASCII 码值)

其他

状态 = MY_LEX_HOSTNAME;【返回】64(@ 的 ASCII 码值)

MY_LEX_HOSTNAME:在 @ 之后,且 @ 之后的字符不是引号或 @

遍历字母、数字、._$ 直至遇到其他字符,而后返回 529(LEX_HOSTNAME)。

case MY_LEX_HOSTNAME:  // end '@' of user@hostname
        for (c = lip->yyGet();
             my_isalnum(cs, c) || c == '.' || c == '_' || c == '$';
             c = lip->yyGet())
          ;
        yylval->lex_str = get_token(lip, 0, lip->yyLength());
        return (LEX_HOSTNAME);

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

当前状态

字符类型

变更状态

MY_LEX_USER_END

-

【返回】529(LEX_HOSTNAME

MY_LEX_SYSTEM_VAR:在 @@ 之后

将指针向后移动 1 个字符,跳过第 2 个 @。如果当前字符为标识符引号,则将状态置为 MY_LEX_START,否则将状态置为 MY_LEX_IDENT_OR_KEYWORD,并返回 64(@ 的 ASCII 码值)。

case MY_LEX_SYSTEM_VAR:
        yylval->lex_str.str = const_cast<char *>(lip->get_ptr());
        yylval->lex_str.length = 1;
        lip->yySkip();  // Skip '@'
        lip->next_state =
            (state_map[lip->yyPeek()] == MY_LEX_USER_VARIABLE_DELIMITER
                 ? MY_LEX_START
                 : MY_LEX_IDENT_OR_KEYWORD);
        return ((int)'@');

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

当前状态

字符类型

变更状态

MY_LEX_SYSTEM_VAR

当前字符为标识符引号

状态 = MY_LEX_START;【返回】64(@ 的 ASCII 码值)

其他

状态 = MY_LEX_IDENT_OR_KEYWORD;【返回】64(@ 的 ASCII 码值)

MY_LEX_IDENT_OR_KEYWORD:在 @@ 之后,且之后不是标识符引号

Step 1|逐个遍历字符,直至当前 token 结束。

Step 2|如果当前字符为 .,则将自动机状态置为 MY_LEX_IDENT_SEP

Step 3|如果 @@ 之后没有解析出字符,则返回 258(ABORT_SYM)。

Step 4|如果当前 token 为关键字,则将指针向前移动 1 个字符(重新指向 .),并返回关键字的编码。

Step 5|否则更新 UTF-8 格式数据流,如果遍历的字符中包含非 ASCII 字符,则最终返回 IDENT_QUOTED,否则最终返回 IDENT

case MY_LEX_IDENT_OR_KEYWORD:
        /*
          We come here when we have found two '@' in a row.
          We should now be able to handle:
          [(global | local | session) .]variable_name
        */

        for (result_state = 0; ident_map[c = lip->yyGet()]; result_state |= c)
          ;
        /* If there were non-ASCII characters, mark that we must convert */
        result_state = result_state & 0x80 ? IDENT_QUOTED : IDENT;

        if (c == '.') lip->next_state = MY_LEX_IDENT_SEP;
        length = lip->yyLength();
        if (length == 0) return (ABORT_SYM);  // Names must be nonempty.
        if ((tokval = find_keyword(lip, length, false))) {
          lip->yyUnget();   // Put back 'c'
          return (tokval);  // Was keyword
        }
        yylval->lex_str = get_token(lip, 0, length);

        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 (result_state);

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

当前状态

字符类型

变更状态

MY_LEX_SYSTEM_VAR

遍历 SQL 字符之后的字符为 .;且 token 中为关键字

状态 = MY_LEX_IDENT_SEP;【返回】关键字的码值

遍历 SQL 字符之后的字符为 .;且 token 中不是关键字

状态 = MY_LEX_IDENT_SEP;【返回】IDENT_QUOTEDIDENT

遍历 SQL 字符之后的字符不为 .;且 token 中为关键字

【返回】关键字的码值

遍历 SQL 字符之后的字符不为 .;且 token 中不是关键字

【返回】IDENT_QUOTEDIDENT


精彩评论(0)

0 0 举报