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);
涉及的状态转移规则如下:
当前状态 | 字符类型 | 变更状态 |
| 下一个字符是 | 【返回】757( |
其他 | 状态 = |
MY_LEX_SEMICOLON
:在 ;
之后
如果在 ;
之后,则直接将状态置为 MY_LEX_CHAR
并继续处理当前 token。
case MY_LEX_SEMICOLON: // optional line terminator
state = MY_LEX_CHAR; // Return ';'
break;
涉及的状态转移规则如下:
当前状态 | 字符类型 | 变更状态 |
| - | 状态 = |
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;
涉及的状态转移规则如下:
当前状态 | 字符类型 | 变更状态 |
| 原始数据流已遍历结束,且不在注释中 | 状态 = |
原始数据流已遍历结束,且在注释中 | 【返回】258( | |
原始数据流没有遍历结束 | 状态 = |
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_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_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)'@');
涉及的状态转移规则如下:
当前状态 | 字符类型 | 变更状态 |
| 当前字符是单引号、标识符引号或双引号 | 【返回】64( |
当前字符是 | 状态 = | |
其他 | 状态 = |
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);
涉及的状态转移规则如下:
当前状态 | 字符类型 | 变更状态 |
| - | 【返回】529( |
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_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);
涉及的状态转移规则如下:
当前状态 | 字符类型 | 变更状态 |
| 遍历 SQL 字符之后的字符为 | 状态 = |
遍历 SQL 字符之后的字符为 | 状态 = | |
遍历 SQL 字符之后的字符不为 | 【返回】关键字的码值 | |
遍历 SQL 字符之后的字符不为 | 【返回】 |