MySQL 源码 - 27|ImplicitCommitParser
解析器和 SplittingAllowedParser
解析器的调用位置
源码位置:(版本 = MySQL 8.0.37)
前置文档:
ImplicitCommitParser
解析器
ImplicitCommitParser
在 is_implicitly_committed()
函数中被调用。该函数接收 2 个参数,分别为词法分析结果的 token遍历器 lexer
和事务状态 trx_state
。根据 ImplicitCommitParser
解析器的逻辑,这个函数返回 SQL 需要隐式提交则返回 true,否则返回 false。
stdx::expected<bool, std::string> is_implicitly_committed(
SqlLexer &&lexer,
std::optional<classic_protocol::session_track::TransactionState>
trx_state) {
return ImplicitCommitParser(lexer.begin(), lexer.end()).parse(trx_state);
}
函数 is_implicitly_committed
在 QueryForwarder::command()
的如下位置被调用:
- 调用
connection()->trx_state()
判断当前是否在事务中,如果不在则将 stage 更新为Stage::ClassifyQuery
- 如果在事务中,则调用
is_implicitly_committed()
函数判断是否需要隐式提交:-
如果
is_implicitly_committed()
函数遭遇异常,则调用harness_assert_this_should_not_execute()
-
如果需要隐式提交,则判断当前连接是否已经打开(
!server_conn.is_open()
):- 如果连接已经打开,则调用并
trace_connect_and_explicit_commit()
并将当前阶段置为Stage::ExplicitCommitConnect
- 如果连接已经打开,则将当前阶段置为
Stage::ExplicitCommit
- 如果连接已经打开,则调用并
-
如果不需要隐式提交,则将当前阶段置为
Stage::ClassifyQuery
-
if (!connection()->trx_state()) {
// no trx state, no trx.
stage(Stage::ClassifyQuery);
} else {
auto is_implictly_committed_res = is_implicitly_committed(
sql_parser_state_.lexer(), connection()->trx_state());
if (!is_implictly_committed_res) {
// it fails if trx-state() is not set, but it has been set.
harness_assert_this_should_not_execute();
} else if (*is_implictly_committed_res) {
auto &server_conn = connection()->server_conn();
if (!server_conn.is_open()) {
trace_event_connect_and_explicit_commit_ =
trace_connect_and_explicit_commit(trace_event_command_);
stage(Stage::ExplicitCommitConnect);
} else {
stage(Stage::ExplicitCommit);
}
} else {
// not implicitly committed.
stage(Stage::ClassifyQuery);
}
}
SplittingAllowedParser
解析器
SplittingAllowedParser
在 splitting_allowed()
函数中被调用。该函数接收 1 个参数,为词法分析结果的 token遍历器 lexer
。根据 SplittingAllowedParser
解析器的逻辑,这个函数返回 Allowed
状态。
stdx::expected<SplittingAllowedParser::Allowed, std::string> splitting_allowed(
SqlLexer &&lexer) {
return SplittingAllowedParser(lexer.begin(), lexer.end()).parse();
}
函数 splitting_allowed()
在 QueryForwarder::command()
的如下分支中被调用,推测是当前 access_mode
仍为自动,还没有被指定:
if (connection()->context().access_mode() == routing::AccessMode::kAuto) {
...
}
具体地,调用逻辑如下:
const auto allowed_res = splitting_allowed(sql_parser_state_.lexer());
如果遇到了预期外的问题,则构造报错信息并将当前阶段置为 Stage::Done
,返回 Result::SendToClient
。
if (!allowed_res) {
auto send_res = ClassicFrame::send_msg<
classic_protocol::borrowed::message::server::Error>(
src_conn, {ER_ROUTER_NOT_ALLOWED_WITH_CONNECTION_SHARING,
allowed_res.error(), "HY000"});
if (!send_res) return send_client_failed(send_res.error());
discard_current_msg(src_conn);
stage(Stage::Done);
return Result::SendToClient;
}
否则,switch
处理 splitting_allowed()
函数返回的各个枚举值。
- 如果返回
Allowed::Always
则不执行任何操作
case SplittingAllowedParser::Allowed::Always:
break;
- 如果返回
Allowed::Never
,则构造报错信息并将当前阶段置为Stage::Done
,返回Result::SendToClient
。
case SplittingAllowedParser::Allowed::Never: {
auto send_res = ClassicFrame::send_msg<
classic_protocol::borrowed::message::server::Error>(
src_conn,
{ER_ROUTER_NOT_ALLOWED_WITH_CONNECTION_SHARING,
"Statement not allowed if access_mode is 'auto'", "HY000"});
if (!send_res) return send_client_failed(send_res.error());
discard_current_msg(src_conn);
stage(Stage::Done);
return Result::SendToClient;
}
- 如果返回其他枚举值,如果当前不在事务中,则构造报错信息并将当前阶段置为
Stage::Done
,返回Result::SendToClient
;否则不做任何操作。
如果当前事务状态的类型为
_
,即说明不在事务中,不需要提交,直接返回 false。详见 MySQL 源码 - 23|句法解析:ImplicitCommitParser 的解析方法 的 Step 2,来源于注释描述。
case SplittingAllowedParser::Allowed::OnlyReadOnly:
case SplittingAllowedParser::Allowed::OnlyReadWrite:
case SplittingAllowedParser::Allowed::InTransaction:
if (!connection()->trx_state() ||
connection()->trx_state()->trx_type() == '_') {
auto send_res = ClassicFrame::send_msg<
classic_protocol::borrowed::message::server::Error>(
src_conn,
{ER_ROUTER_NOT_ALLOWED_WITH_CONNECTION_SHARING,
"Statement not allowed outside a transaction if access_mode "
"is 'auto'",
"HY000"});
if (!send_res) return send_client_failed(send_res.error());
discard_current_msg(src_conn);
stage(Stage::Done);
return Result::SendToClient;
}
break;