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);涉及的状态转移规则如下:
当前状态 | 字符类型 | 变更状态 |
| 没有配对的引号 | 【返回】258( |
其他 | 状态 = |
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涉及的状态转移规则如下:
当前状态 | 字符类型 | 变更状态 |
| 当前字符不是 | 【返回】根据数值长度返回其中之一:628( |
其他 | 状态 = |
MY_LEX_REAL:未完成的数值
Step 1|继续匹配剩余的数值
while (my_isdigit(cs, c = lip->yyGet()))
;Step 2|如果当前字符为 e 或 E,则跳过后续的 - 或 + 符号。若在此之后不是数字,则将状态置为 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|如果当前字符不是 e 或 E,则返回 373(DECIMAL_NUM)。
yylval->lex_str = get_token(lip, 0, lip->yyLength());
return (DECIMAL_NUM);涉及的状态转移规则如下:
当前状态 | 字符类型 | 变更状态 |
| 如果数字后为 | 状态 = |
如果数字后为 | 【返回】443( | |
其他 | 【返回】373( |
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);涉及的状态转移规则如下:
当前状态 | 字符类型 | 变更状态 |
| 能够匹配十六进制数格式 | 【返回】474( |
其他 | 【返回】258( |
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);涉及的状态转移规则如下:
当前状态 | 字符类型 | 变更状态 |
| 能够匹配二进制数格式 | 【返回】292( |
其他 | 【返回】258( |
















