mysql8 从C++源码角度看sql生成抽象语法树
在 MySQL 8 的 C++ 源码中,SQL 语句的解析过程涉及多个步骤,包括词法分析、语法分析和抽象语法树(AST)的生成。以下是详细的解析过程和相关组件的描述:
1. 词法分析器(Lexer)
MySQL 使用一个称为 MYSQL_LEX
的类来进行词法分析。它负责将 SQL 查询字符串分解成一系列的标记(tokens),如关键字、标识符、操作符等。
在 dispatch_sql_command
函数中,调用 parse_sql
函数开始解析过程,这会触发词法分析器的工作。
2. 语法分析器(Parser)
MySQL 的语法分析器基于 Bison 生成,定义在 .y
文件中(例如 sql_yacc.yy
)。这些文件定义了 SQL 语言的语法规则。
当词法分析器生成了一系列标记后,语法分析器根据这些标记和预定义的语法规则构建抽象语法树(AST)。
3. 抽象语法树(AST)
AST 是一种树形结构,用于表示源代码的逻辑结构。在 MySQL 中,AST 是由多个类组成的复杂数据结构,主要由 LEX
和 Item
类家族来表示。
-
LEX
对象:包含了整个查询的上下文信息,包括但不限于表名、列名、表达式等。 -
Item
类及其子类:用于表示查询中的各种元素,如表达式、条件、值等。
解析流程
-
初始化:
-
在
dispatch_sql_command
函数中,首先调用了lex_start(thd)
,这初始化了解析环境,为接下来的词法和语法分析做好准备。
-
-
词法分析:
-
调用
parse_sql(thd, parser_state, nullptr)
启动解析过程。这里,parser_state
包含了当前解析状态的信息,包括词法分析器的状态。 -
MYSQL_LEX
对象作为词法分析器的核心,逐字符读取 SQL 查询,并将其转换为标记序列。
-
-
语法分析:
-
使用 Bison 生成的解析器根据词法分析器产生的标记序列,按照 SQL 语法规则进行解析,并构建出抽象语法树(AST)。
-
如果解析过程中遇到错误,比如语法错误,解析器会设置错误标志并返回相应的错误信息。
-
-
后续处理:
-
解析完成后,可能会对 AST 进行一些重写操作(例如,应用插件或执行特定的优化)。
-
最终,解析后的命令会被传递给查询执行引擎(通过调用
mysql_execute_command
)来执行实际的操作。
-
关键代码行
-
触发词法和语法分析:
cpp
const bool mysql_parse_status = thd->sql_parser();
这行代码是关键点,它触发了对输入 SQL 文本的解析过程,其中包含了词法分析和语法分析两个阶段的工作。
-
调用解析器函数:
cpp
extern int my_sql_parser_parse(class THD *thd, class Parse_tree_root **root);
这行代码调用了由 YACC 从
sql_yacc.yy
生成的解析器函数my_sql_parser_parse
。这个函数负责执行 SQL 语句的词法分析和语法分析,并构建出解析树(parse tree)。
总结
-
词法分析:将 SQL 文本分割成标记,识别出 SQL 语句中的各个组成部分。
-
语法分析:根据 SQL 语言的语法规则检查这些标记,并构造抽象语法树。
-
抽象语法树:表示 SQL 查询的逻辑结构,便于后续的查询优化和执行计划生成。
通过这些步骤,MySQL 8 的 C++ 源码实现了从 SQL 文本到抽象语法树的转换,为后续的查询优化和执行提供了基础。
##词法分析器(Lexer)
MySQL 8.0 源码中 词法分析器(Lexer) 的核心实现部分,具体来说,它是 my_sql_parser_lex
函数的实现。该函数是 MySQL 语法分析器(由 Bison 生成)的 词法分析接口,负责从 SQL 语句中提取词法单元(Token),并将其传递给语法分析器。
以下是对这段代码的详细解析:
1. 函数功能
my_sql_parser_lex
是 MySQL 语法分析器的词法分析接口,它的主要功能是:
-
从 SQL 语句中提取词法单元(Token)。
-
将词法单元的值和位置信息传递给语法分析器。
-
处理一些特殊情况(如
WITH ROLLUP
语法)。
2. 函数参数
cpp
复制
int my_sql_parser_lex(MY_SQL_PARSER_STYPE *yacc_yylval, POS *yylloc, THD *thd);
-
yacc_yylval
:词法单元的值(语义值),传递给语法分析器。 -
yylloc
:词法单元的位置信息(起始和结束位置)。 -
thd
:当前线程的THD
对象,包含 SQL 语句的解析状态。
3. 代码解析
(1)初始化
cpp
复制
auto *yylval = reinterpret_cast<Lexer_yystype *>(yacc_yylval); Lex_input_stream *lip = &thd->m_parser_state->m_lip; int token;
-
yylval
:将yacc_yylval
转换为Lexer_yystype
类型,用于存储词法单元的值。 -
lip
:获取当前 SQL 语句的输入流(Lex_input_stream
),用于读取 SQL 语句。 -
token
:存储词法单元的类型(Token 编号)。
(2)错误处理
cpp
复制
if (thd->is_error()) {if (thd->get_parser_da()->has_sql_condition(ER_CAPACITY_EXCEEDED))return ABORT_SYM; }
-
如果当前线程(
thd
)存在错误,并且错误是ER_CAPACITY_EXCEEDED
(容量超出),则返回ABORT_SYM
,表示终止解析。
(3)处理预读的 Token
cpp
复制
if (lip->lookahead_token >= 0) {token = lip->lookahead_token;lip->lookahead_token = -1;*yylval = *(lip->lookahead_yylval);yylloc->cpp.start = lip->get_cpp_tok_start();yylloc->cpp.end = lip->get_cpp_ptr();yylloc->raw.start = lip->get_tok_start();yylloc->raw.end = lip->get_ptr();lip->lookahead_yylval = nullptr;lip->add_digest_token(token, yylval);return token; }
-
如果存在预读的 Token(
lookahead_token >= 0
),则直接返回该 Token,并清空预读状态。 -
lookahead_token
:用于处理需要预读的语法(如WITH ROLLUP
)。
(4)提取一个 Token
cpp
token = lex_one_token(yylval, thd); yylloc->cpp.start = lip->get_cpp_tok_start(); yylloc->raw.start = lip->get_tok_start();
-
调用
lex_one_token
函数从输入流中提取一个 Token。 -
记录 Token 的起始位置。
(5)处理特殊情况:WITH ROLLUP
cpp
switch (token) {case WITH:token = lex_one_token(yylval, thd);switch (token) {case ROLLUP_SYM:yylloc->cpp.end = lip->get_cpp_ptr();yylloc->raw.end = lip->get_ptr();lip->add_digest_token(WITH_ROLLUP_SYM, yylval);return WITH_ROLLUP_SYM;default:lip->lookahead_yylval = lip->yylval;lip->yylval = nullptr;lip->lookahead_token = token;yylloc->cpp.end = lip->get_cpp_ptr();yylloc->raw.end = lip->get_ptr();lip->add_digest_token(WITH, yylval);return WITH;}break; }
-
如果当前 Token 是
WITH
,则需要预读下一个 Token:-
如果下一个 Token 是
ROLLUP_SYM
,则合并为WITH_ROLLUP_SYM
。 -
否则,保存预读的 Token,并返回
WITH
。
-
(6)返回 Token
cpp
yylloc->cpp.end = lip->get_cpp_ptr(); yylloc->raw.end = lip->get_ptr(); if (!lip->skip_digest) lip->add_digest_token(token, yylval); lip->skip_digest = false; return token;
-
记录 Token 的结束位置。
-
将 Token 添加到 SQL 语句的摘要(Digest) 中(除非显式跳过)。
-
返回提取的 Token。
4. 关键函数和数据结构
(1)lex_one_token
-
从输入流中提取一个 Token。
-
实现位于
sql/lex.cc
中。
(2)Lex_input_stream
-
SQL 语句的输入流,用于读取和解析 SQL 语句。
-
包含当前解析位置、Token 起始位置等信息。
(3)Lexer_yystype
-
词法单元的值类型,用于存储 Token 的语义值。
(4)POS
-
词法单元的位置信息,包括起始和结束位置。
5. 总结
my_sql_parser_lex
是 MySQL 词法分析器的核心函数,负责从 SQL 语句中提取 Token 并传递给语法分析器。它的主要功能包括:
-
提取 Token。
-
处理预读 Token。
-
处理特殊情况(如
WITH ROLLUP
)。 -
记录 Token 的值和位置信息。
通过这种设计,MySQL 能够高效地解析 SQL 语句,并将其转换为语法分析器可以处理的 Token 流。
##源码
/**Parse an SQL command from a text string and pass the resulting AST to thequery executor.@param thd Current session.@param parser_state Parser state.
*/void dispatch_sql_command(THD *thd, Parser_state *parser_state) {DBUG_TRACE;DBUG_PRINT("dispatch_sql_command", ("query: '%s'", thd->query().str));statement_id_to_session(thd);DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on(););mysql_reset_thd_for_next_command(thd);// It is possible that rewritten query may not be empty (in case of// multiqueries). So reset it.thd->reset_rewritten_query();lex_start(thd);thd->m_parser_state = parser_state;invoke_pre_parse_rewrite_plugins(thd);thd->m_parser_state = nullptr;// we produce digest if it's not explicitly turned off// by setting maximum digest length to zeroif (get_max_digest_length() != 0)parser_state->m_input.m_compute_digest = true;LEX *lex = thd->lex;const char *found_semicolon = nullptr;bool err = thd->get_stmt_da()->is_error();size_t qlen = 0;if (!err) {err = parse_sql(thd, parser_state, nullptr);if (!err) err = invoke_post_parse_rewrite_plugins(thd, false);found_semicolon = parser_state->m_lip.found_semicolon;qlen = found_semicolon ? (found_semicolon - thd->query().str): thd->query().length;/*We set thd->query_length correctly to not log several queries, when weexecute only first. We set it to not see the ';' otherwise it would getinto binlog and Query_log_event::print() would give ';;' output.*/if (!thd->is_error() && found_semicolon && (ulong)(qlen)) {thd->set_query(thd->query().str, qlen - 1);}}DEBUG_SYNC_C("sql_parse_before_rewrite");if (!err) {/*Rewrite the query for logging and for the Performance Schemastatement tables. (Raw logging happened earlier.)Sub-routines of mysql_rewrite_query() should try to only rewrite whennecessary (e.g. not do password obfuscation when query contains nopassword).If rewriting does not happen here, thd->m_rewritten_query is stillempty from being reset in alloc_query().*/if (thd->rewritten_query().length() == 0) mysql_rewrite_query(thd);if (thd->rewritten_query().length()) {lex->safe_to_cache_query = false; // see comments belowthd->set_query_for_display(thd->rewritten_query().ptr(),thd->rewritten_query().length());} else if (thd->slave_thread) {/*In the slave, we add the information to pfs.events_statements_history,but not to pfs.threads, as that is what the test suite expects.*/MYSQL_SET_STATEMENT_TEXT(thd->m_statement_psi, thd->query().str,thd->query().length);} else {thd->set_query_for_display(thd->query().str, thd->query().length);}if (!(opt_general_log_raw || thd->slave_thread)) {if (thd->rewritten_query().length())query_logger.general_log_write(thd, COM_QUERY,thd->rewritten_query().ptr(),thd->rewritten_query().length());else {query_logger.general_log_write(thd, COM_QUERY, thd->query().str, qlen);}}}const bool with_query_attributes = (thd->bind_parameter_values_count > 0);MYSQL_NOTIFY_STATEMENT_QUERY_ATTRIBUTES(thd->m_statement_psi,with_query_attributes);DEBUG_SYNC_C("sql_parse_after_rewrite");if (!err) {thd->m_statement_psi = MYSQL_REFINE_STATEMENT(thd->m_statement_psi, sql_statement_info[thd->lex->sql_command].m_key);if (mqh_used && thd->get_user_connect() &&check_mqh(thd, lex->sql_command)) {if (thd->is_classic_protocol())thd->get_protocol_classic()->get_net()->error = NET_ERROR_UNSET;} else {if (!thd->is_error()) {/* Actually execute the query */if (found_semicolon) {lex->safe_to_cache_query = false;thd->server_status |= SERVER_MORE_RESULTS_EXISTS;}lex->set_trg_event_type_for_tables();int error [[maybe_unused]];if (unlikely((thd->security_context()->password_expired() ||thd->security_context()->is_in_registration_sandbox_mode()) &&lex->sql_command != SQLCOM_SET_PASSWORD &&lex->sql_command != SQLCOM_ALTER_USER)) {if (thd->security_context()->is_in_registration_sandbox_mode())my_error(ER_PLUGIN_REQUIRES_REGISTRATION, MYF(0));elsemy_error(ER_MUST_CHANGE_PASSWORD, MYF(0));error = 1;} else {resourcegroups::Resource_group *src_res_grp = nullptr;resourcegroups::Resource_group *dest_res_grp = nullptr;MDL_ticket *ticket = nullptr;MDL_ticket *cur_ticket = nullptr;auto mgr_ptr = resourcegroups::Resource_group_mgr::instance();const bool switched = mgr_ptr->switch_resource_group_if_needed(thd, &src_res_grp, &dest_res_grp, &ticket, &cur_ticket);error = mysql_execute_command(thd, true);if (switched)mgr_ptr->restore_original_resource_group(thd, src_res_grp,dest_res_grp);thd->resource_group_ctx()->m_switch_resource_group_str[0] = '\0';if (ticket != nullptr)mgr_ptr->release_shared_mdl_for_resource_group(thd, ticket);if (cur_ticket != nullptr)mgr_ptr->release_shared_mdl_for_resource_group(thd, cur_ticket);}}}} else {/*Log the failed raw query in the Performance Schema. This statement didnot parse, so there is no way to tell if it may contain a password of not.The tradeoff is:a) If we do log the query, a user typing by accident a broken querycontaining a password will have the password exposed. This is veryunlikely, and this behavior can be documented. Remediation is touse a new password when retyping the corrected query.b) If we do not log the query, finding broken queries in the clientapplication will be much more difficult. This is much more likely.Considering that broken queries can typically be generated by attempts atSQL injection, finding the source of the SQL injection is critical, so thedesign choice is to log the query text of broken queries (a).*/thd->set_query_for_display(thd->query().str, thd->query().length);/* Instrument this broken statement as "statement/sql/error" */thd->m_statement_psi = MYSQL_REFINE_STATEMENT(thd->m_statement_psi, sql_statement_info[SQLCOM_END].m_key);assert(thd->is_error());DBUG_PRINT("info",("Command aborted. Fatal_error: %d", thd->is_fatal_error()));}THD_STAGE_INFO(thd, stage_freeing_items);sp_cache_enforce_limit(thd->sp_proc_cache, stored_program_cache_size);sp_cache_enforce_limit(thd->sp_func_cache, stored_program_cache_size);thd->lex->destroy();thd->end_statement();thd->cleanup_after_query();assert(thd->change_list.is_empty());DEBUG_SYNC(thd, "query_rewritten");
}
##
/**Transform an SQL statement into an AST that is ready for resolving, using thesupplied parser state and object creation context.This is a wrapper() for THD::sql_parser() and should generally be used for ASTconstruction.The function may optionally generate a query digest, invoke this function asfollows:@verbatimTHD *thd = ...;const char *query_text = ...;uint query_length = ...;Object_creation_ctx *ctx = ...;bool rc;Parser_state parser_state;if (parser_state.init(thd, query_text, query_length){... handle error}parser_state.m_input.m_has_digest= true;parser_state.m_input.m_compute_digest= true;rc= parse_sql(the, &parser_state, ctx);if (! rc){unsigned char md5[MD5_HASH_SIZE];char digest_text[1024];bool truncated;const sql_digest_storage *digest= & thd->m_digest->m_digest_storage;compute_digest_md5(digest, & md5[0]);compute_digest_text(digest, & digest_text[0], sizeof(digest_text), &truncated);}@endverbatim@param thd Thread context.@param parser_state Parser state.@param creation_ctx Object creation context.@return Error status.@retval false on success.@retval true on parsing error.
*/bool parse_sql(THD *thd, Parser_state *parser_state,Object_creation_ctx *creation_ctx) {DBUG_TRACE;bool ret_value;assert(thd->m_parser_state == nullptr);// TODO fix to allow parsing gcol exprs after main query.// assert(thd->lex->m_sql_cmd == NULL);/* Backup creation context. */Object_creation_ctx *backup_ctx = nullptr;if (creation_ctx) backup_ctx = creation_ctx->set_n_backup(thd);/* Set parser state. */thd->m_parser_state = parser_state;parser_state->m_digest_psi = nullptr;parser_state->m_lip.m_digest = nullptr;/*Partial parsers (GRAMMAR_SELECTOR_*) are not supposed to compute digests.*/assert(!parser_state->m_lip.is_partial_parser() ||!parser_state->m_input.m_has_digest);/*Only consider statements that are supposed to have a digest,like top level queries.*/if (parser_state->m_input.m_has_digest) {/*For these statements,see if the digest computation is required.*/if (thd->m_digest != nullptr) {/* Start Digest */parser_state->m_digest_psi = MYSQL_DIGEST_START(thd->m_statement_psi);if (parser_state->m_input.m_compute_digest ||(parser_state->m_digest_psi != nullptr)) {/*If either:- the caller wants to compute a digest- the performance schema wants to compute a digestset the digest listener in the lexer.*/parser_state->m_lip.m_digest = thd->m_digest;parser_state->m_lip.m_digest->m_digest_storage.m_charset_number =thd->charset()->number;}}}/* Parse the query. *//*Use a temporary DA while parsing. We don't know until after parsingwhether the current command is a diagnostic statement, in which casewe'll need to have the previous DA around to answer questions about it.*/Diagnostics_area *parser_da = thd->get_parser_da();Diagnostics_area *da = thd->get_stmt_da();Parser_oom_handler poomh;// Note that we may be called recursively here, on INFORMATION_SCHEMA queries.thd->mem_root->set_max_capacity(thd->variables.parser_max_mem_size);thd->mem_root->set_error_for_capacity_exceeded(true);thd->push_internal_handler(&poomh);thd->push_diagnostics_area(parser_da, false);const bool mysql_parse_status = thd->sql_parser();thd->pop_internal_handler();thd->mem_root->set_max_capacity(0);thd->mem_root->set_error_for_capacity_exceeded(false);/*Unwind diagnostics area.If any issues occurred during parsing, they will becomethe sole conditions for the current statement.Otherwise, if we have a diagnostic statement on our hands,we'll preserve the previous diagnostics area here so wecan answer questions about it. This specifically meansthat repeatedly asking about a DA won't clear it.Otherwise, it's a regular command with no issues duringparsing, so we'll just clear the DA in preparation forthe processing of this command.*/if (parser_da->current_statement_cond_count() != 0) {/*Error/warning during parsing: top DA should contain parse error(s)! Anypre-existing conditions will be replaced. The exception is diagnosticsstatements, in which case we wish to keep the errors so they can be sentto the client.*/if (thd->lex->sql_command != SQLCOM_SHOW_WARNS &&thd->lex->sql_command != SQLCOM_GET_DIAGNOSTICS)da->reset_condition_info(thd);/*We need to put any errors in the DA as well as the condition list.*/if (parser_da->is_error() && !da->is_error()) {da->set_error_status(parser_da->mysql_errno(), parser_da->message_text(),parser_da->returned_sqlstate());}da->copy_sql_conditions_from_da(thd, parser_da);parser_da->reset_diagnostics_area();parser_da->reset_condition_info(thd);/*Do not clear the condition list when starting execution as itnow contains not the results of the previous executions, buta non-zero number of errors/warnings thrown during parsing!*/thd->lex->keep_diagnostics = DA_KEEP_PARSE_ERROR;}thd->pop_diagnostics_area();/*Check that if THD::sql_parser() failed either thd->is_error() is set, or aninternal error handler is set.The assert will not catch a situation where parsing fails without anerror reported if an error handler exists. The problem is that theerror handler might have intercepted the error, so thd->is_error() isnot set. However, there is no way to be 100% sure here (the errorhandler might be for other errors than parsing one).*/assert(!mysql_parse_status || (mysql_parse_status && thd->is_error()) ||(mysql_parse_status && thd->get_internal_handler()));/* Reset parser state. */thd->m_parser_state = nullptr;/* Restore creation context. */if (creation_ctx) creation_ctx->restore_env(thd, backup_ctx);/* That's it. */ret_value = mysql_parse_status || thd->is_fatal_error();if ((ret_value == 0) && (parser_state->m_digest_psi != nullptr)) {/*On parsing success, record the digest in the performance schema.*/assert(thd->m_digest != nullptr);MYSQL_DIGEST_END(parser_state->m_digest_psi,&thd->m_digest->m_digest_storage);}return ret_value;
}
##
/**Call parser to transform statement into a parse tree.Then, transform the parse tree further into an AST, ready for resolving.
*/
bool THD::sql_parser() {/*SQL parser function generated by YACC from sql_yacc.yy.In the case of success returns 0, and THD::is_error() is false.Otherwise returns 1, or THD::>is_error() is true.The second (output) parameter "root" returns the new parse tree.It is undefined (unchanged) on error. If "root" is NULL on success,then the parser has already called lex->make_sql_cmd() internally.*/extern int my_sql_parser_parse(class THD * thd,class Parse_tree_root * *root);Parse_tree_root *root = nullptr;if (my_sql_parser_parse(this, &root) || is_error()) {/*Restore the original LEX if it was replaced when parsinga stored procedure. We must ensure that a parsing errordoes not leave any side effects in the THD.*/cleanup_after_parse_error();return true;}if (root != nullptr && lex->make_sql_cmd(root)) {return true;}return false;
}
##词法分析部分源码
/**yylex() function implementation for the main parser@param [out] yacc_yylval semantic value of the token being parsed (yylval)@param [out] yylloc "location" of the token being parsed (yylloc)@param thd THD@return token number@notemy_sql_parser_lex remember the following states from thefollowing my_sql_parser_lex():- MY_LEX_END Found end of query
*/int my_sql_parser_lex(MY_SQL_PARSER_STYPE *yacc_yylval, POS *yylloc, THD *thd) {auto *yylval = reinterpret_cast<Lexer_yystype *>(yacc_yylval);Lex_input_stream *lip = &thd->m_parser_state->m_lip;int token;if (thd->is_error()) {if (thd->get_parser_da()->has_sql_condition(ER_CAPACITY_EXCEEDED))return ABORT_SYM;}if (lip->lookahead_token >= 0) {/*The next token was already parsed in advance,return it.*/token = lip->lookahead_token;lip->lookahead_token = -1;*yylval = *(lip->lookahead_yylval);yylloc->cpp.start = lip->get_cpp_tok_start();yylloc->cpp.end = lip->get_cpp_ptr();yylloc->raw.start = lip->get_tok_start();yylloc->raw.end = lip->get_ptr();lip->lookahead_yylval = nullptr;lip->add_digest_token(token, yylval);return token;}token = lex_one_token(yylval, thd);yylloc->cpp.start = lip->get_cpp_tok_start();yylloc->raw.start = lip->get_tok_start();switch (token) {case WITH:/*Parsing 'WITH' 'ROLLUP' requires 2 look ups,which makes the grammar LALR(2).Replace by a single 'WITH_ROLLUP' token,to transform the grammar into a LALR(1) grammar,which sql_yacc.yy can process.*/token = lex_one_token(yylval, thd);switch (token) {case ROLLUP_SYM:yylloc->cpp.end = lip->get_cpp_ptr();yylloc->raw.end = lip->get_ptr();lip->add_digest_token(WITH_ROLLUP_SYM, yylval);return WITH_ROLLUP_SYM;default:/*Save the token following 'WITH'*/lip->lookahead_yylval = lip->yylval;lip->yylval = nullptr;lip->lookahead_token = token;yylloc->cpp.end = lip->get_cpp_ptr();yylloc->raw.end = lip->get_ptr();lip->add_digest_token(WITH, yylval);return WITH;}break;}yylloc->cpp.end = lip->get_cpp_ptr();yylloc->raw.end = lip->get_ptr();if (!lip->skip_digest) lip->add_digest_token(token, yylval);lip->skip_digest = false;return token;
}
##/home/yym/mysql8/mysql-8.1.0/sql/sql_yacc.cc
#define yyparse my_sql_parser_parse
#define yylex my_sql_parser_lex
##语法树给命令行赋值
MySQL 8.0 源码中与 SQL 解析和执行相关的部分,主要涉及 LEX
结构和 Sql_cmd
对象的创建和赋值过程。以下是对代码的详细解析:
1. LEX::make_sql_cmd
函数
cpp
复制
bool LEX::make_sql_cmd(Parse_tree_root *parse_tree) {if (!will_contextualize) return false;m_sql_cmd = parse_tree->make_cmd(thd);if (m_sql_cmd == nullptr) return true;assert(m_sql_cmd->sql_command_code() == sql_command);return false; }
功能:
-
该函数的作用是通过解析树(
Parse_tree_root
)创建一个Sql_cmd
对象,并将其赋值给LEX
结构中的m_sql_cmd
成员。 -
Sql_cmd
是一个基类,表示一个 SQL 命令的具体实现。不同的 SQL 命令(如SELECT
、INSERT
等)会有对应的派生类(如Sql_cmd_select
、Sql_cmd_insert
等)。
参数:
-
parse_tree
:解析树的根节点,表示一个 SQL 语句的抽象语法树(AST)。
逻辑:
-
will_contextualize
检查:-
如果
will_contextualize
为false
,则直接返回false
,表示不需要进一步处理。 -
这个标志通常用于控制是否需要将解析树转换为
Sql_cmd
对象。
-
-
创建
Sql_cmd
对象:-
调用
parse_tree->make_cmd(thd)
,通过解析树创建对应的Sql_cmd
对象。 -
如果创建失败(返回
nullptr
),则返回true
,表示出错。
-
-
断言检查:
-
使用
assert
检查m_sql_cmd->sql_command_code()
是否与sql_command
一致。 -
这一步确保创建的
Sql_cmd
对象的命令类型与LEX
中记录的sql_command
一致。
-
-
返回值:
-
如果成功创建
Sql_cmd
对象并赋值,则返回false
,表示成功。 -
如果失败,则返回
true
。
-
2. PT_select_stmt::make_cmd
函数
cpp
复制
Sql_cmd *PT_select_stmt::make_cmd(THD *thd) {Parse_context pc(thd, thd->lex->current_query_block());thd->lex->sql_command = m_sql_command;if (m_qe->contextualize(&pc)) {return nullptr;}const bool has_into_clause_inside_query_block = thd->lex->result != nullptr;if (has_into_clause_inside_query_block && m_into != nullptr) {my_error(ER_MULTIPLE_INTO_CLAUSES, MYF(0));return nullptr;}if (contextualize_safe(&pc, m_into)) {return nullptr;}if (pc.finalize_query_expression()) return nullptr;if (m_into != nullptr && m_has_trailing_locking_clauses) {// Example: ... INTO ... FOR UPDATE;push_warning(thd, ER_WARN_DEPRECATED_INNER_INTO);} else if (has_into_clause_inside_query_block &&thd->lex->unit->is_set_operation()) {// Example: ... UNION ... INTO ...;if (!m_qe->has_trailing_into_clause()) {// Example: ... UNION SELECT * INTO OUTFILE 'foo' FROM ...;push_warning(thd, ER_WARN_DEPRECATED_INNER_INTO);} else if (m_has_trailing_locking_clauses) {// Example: ... UNION SELECT ... FROM ... INTO OUTFILE 'foo' FOR UPDATE;push_warning(thd, ER_WARN_DEPRECATED_INNER_INTO);}}return new (thd->mem_root) Sql_cmd_select(thd->lex); }
功能:
-
该函数是
PT_select_stmt
类的一个方法,用于为SELECT
语句创建一个Sql_cmd_select
对象。 -
PT_select_stmt
是SELECT
语句的解析树节点。
参数:
-
thd
:当前线程的THD
对象,包含线程相关的上下文信息。
逻辑:
-
初始化
Parse_context
:-
创建一个
Parse_context
对象pc
,用于管理解析过程中的上下文信息。 -
thd->lex->current_query_block()
获取当前查询块(Query_block
)。
-
-
设置
sql_command
:-
将
thd->lex->sql_command
设置为m_sql_command
。 -
m_sql_command
是PT_select_stmt
的成员变量,表示当前 SQL 命令的类型(如SQLCOM_SELECT
)。
-
-
上下文化查询表达式:
-
调用
m_qe->contextualize(&pc)
,对查询表达式进行上下文化处理。 -
如果失败,则返回
nullptr
。
-
-
检查
INTO
子句:-
检查是否存在多个
INTO
子句,如果存在则报错(ER_MULTIPLE_INTO_CLAUSES
)。 -
调用
contextualize_safe(&pc, m_into)
,对INTO
子句进行上下文化处理。
-
-
完成查询表达式:
-
调用
pc.finalize_query_expression()
,完成查询表达式的处理。 -
如果失败,则返回
nullptr
。
-
-
处理警告:
-
如果存在
INTO
子句和锁子句(如FOR UPDATE
),则发出警告(ER_WARN_DEPRECATED_INNER_INTO
)。 -
对于
UNION
查询中的INTO
子句,也会发出警告。
-
-
创建
Sql_cmd_select
对象:-
使用
thd->mem_root
分配内存,创建一个Sql_cmd_select
对象。 -
返回该对象。
-
3. 关键点总结
-
sql_command
的赋值:-
在
PT_select_stmt::make_cmd
中,thd->lex->sql_command
被赋值为m_sql_command
。 -
这个赋值发生在解析树转换为
Sql_cmd
对象的过程中。
-
-
Sql_cmd
的创建:-
Sql_cmd
对象是通过解析树(Parse_tree_root
)创建的。 -
对于
SELECT
语句,会创建Sql_cmd_select
对象。
-
-
上下文化处理:
-
在创建
Sql_cmd
对象之前,需要对解析树进行上下文化处理(contextualize
),以确保所有语法元素都被正确解析和处理。
-
-
错误处理:
-
如果解析或上下文化过程中出现错误,函数会返回
nullptr
,并在适当的情况下抛出错误或警告。
-
4. 代码的作用
这段代码的核心作用是将解析树(Parse_tree_root
)转换为具体的 Sql_cmd
对象,并将其与 LEX
结构关联起来。这是 SQL 解析和执行的关键步骤之一,确保 SQL 语句能够被正确解析并执行。
##源码
/**Uses parse_tree to instantiate an Sql_cmd object and assigns it to the Lex.@param parse_tree The parse tree.@returns false on success, true on error.
*/
bool LEX::make_sql_cmd(Parse_tree_root *parse_tree) {if (!will_contextualize) return false;m_sql_cmd = parse_tree->make_cmd(thd);if (m_sql_cmd == nullptr) return true;assert(m_sql_cmd->sql_command_code() == sql_command);return false;
}Sql_cmd *PT_select_stmt::make_cmd(THD *thd) {Parse_context pc(thd, thd->lex->current_query_block());thd->lex->sql_command = m_sql_command;if (m_qe->contextualize(&pc)) {return nullptr;}const bool has_into_clause_inside_query_block = thd->lex->result != nullptr;if (has_into_clause_inside_query_block && m_into != nullptr) {my_error(ER_MULTIPLE_INTO_CLAUSES, MYF(0));return nullptr;}if (contextualize_safe(&pc, m_into)) {return nullptr;}if (pc.finalize_query_expression()) return nullptr;if (m_into != nullptr && m_has_trailing_locking_clauses) {// Example: ... INTO ... FOR UPDATE;push_warning(thd, ER_WARN_DEPRECATED_INNER_INTO);} else if (has_into_clause_inside_query_block &&thd->lex->unit->is_set_operation()) {// Example: ... UNION ... INTO ...;if (!m_qe->has_trailing_into_clause()) {// Example: ... UNION SELECT * INTO OUTFILE 'foo' FROM ...;push_warning(thd, ER_WARN_DEPRECATED_INNER_INTO);} else if (m_has_trailing_locking_clauses) {// Example: ... UNION SELECT ... FROM ... INTO OUTFILE 'foo' FOR UPDATE;push_warning(thd, ER_WARN_DEPRECATED_INNER_INTO);}}DBUG_EXECUTE_IF("ast", Query_term *qn =pc.select->master_query_expression()->query_term();std::ostringstream buf; qn->debugPrint(0, buf);DBUG_PRINT("ast", ("\n%s", buf.str().c_str())););if (thd->lex->sql_command == SQLCOM_SELECT)return new (thd->mem_root) Sql_cmd_select(thd->lex->result);else // (thd->lex->sql_command == SQLCOM_DO)return new (thd->mem_root) Sql_cmd_do(nullptr);
}
##gdb
#0 yyparse () at /home/yym/mysql8/mysql-8.1.0/build/storage/innobase/pars0grm.cc:1409
#1 0x000057cc696f0864 in pars_sql (info=0x78341c0e0cb8,str=0x57cc6ba8f5a8 "PROCEDURE FETCH_STATS () IS\nfound INT;\nDECLARE FUNCTION fetch_table_stats_step;\nDECLARE FUNCTION fetch_index_stats_step;\nDECLARE CURSOR table_stats_cur IS\n SELECT\n n_rows,\n clustered_index_size,\n "...) at /home/yym/mysql8/mysql-8.1.0/storage/innobase/pars/pars0pars.cc:1706
#2 0x000057cc696f7747 in que_eval_sql (info=0x78341c0e0cb8,sql=0x57cc6ba8f5a8 "PROCEDURE FETCH_STATS () IS\nfound INT;\nDECLARE FUNCTION fetch_table_stats_step;\nDECLARE FUNCTION fetch_index_stats_step;\nDECLARE CURSOR table_stats_cur IS\n SELECT\n n_rows,\n clustered_index_size,\n "..., trx=0x7834be600ff8) at /home/yym/mysql8/mysql-8.1.0/storage/innobase/que/que0que.cc:1049
#3 0x000057cc69a9d370 in dict_stats_fetch_from_ps (table=0x78341c0fd1f8) at /home/yym/mysql8/mysql-8.1.0/storage/innobase/dict/dict0stats.cc:2723
#4 0x000057cc69a9d8be in dict_stats_update (table=0x78341c0c5988, stats_upd_option=DICT_STATS_FETCH_ONLY_IF_NOT_IN_MEMORY)at /home/yym/mysql8/mysql-8.1.0/storage/innobase/dict/dict0stats.cc:2907
#5 0x000057cc694b0e5e in dict_stats_init (table=0x78341c0c5988) at /home/yym/mysql8/mysql-8.1.0/storage/innobase/include/dict0stats.ic:159
#6 0x000057cc694c1e06 in ha_innobase::open (this=0x78341c0c6450, name=0x78341c032d78 "./grey/o2o_hot_marketing", open_flags=2, table_def=0x78341c09d668)at /home/yym/mysql8/mysql-8.1.0/storage/innobase/handler/ha_innodb.cc:7324
#7 0x000057cc67fe7cb9 in handler::ha_open (this=0x78341c0c6450, table_arg=0x78341c0a6e20, name=0x78341c032d78 "./grey/o2o_hot_marketing", mode=2, test_if_locked=2,table_def=0x78341c09d668) at /home/yym/mysql8/mysql-8.1.0/sql/handler.cc:2796
#8 0x000057cc67d6dfa4 in open_table_from_share (thd=0x78341c000c40, share=0x78341c0329c0, alias=0x78341c025918 "o2o_hot_marketing", db_stat=39, prgflag=8, ha_open_flags=0,outparam=0x78341c0a6e20, is_create_table=false, table_def_param=0x78341c09d668) at /home/yym/mysql8/mysql-8.1.0/sql/table.cc:3213
#9 0x000057cc67a8b2d8 in open_table (thd=0x78341c000c40, table_list=0x78341c025930, ot_ctx=0x7834bebfb700) at /home/yym/mysql8/mysql-8.1.0/sql/sql_base.cc:3413
#10 0x000057cc67a8f208 in open_and_process_table (thd=0x78341c000c40, lex=0x78341c010260, tables=0x78341c025930, counter=0x7834bebfb7dc, prelocking_strategy=0x7834bebfb780,has_prelocking_list=false, ot_ctx=0x7834bebfb700) at /home/yym/mysql8/mysql-8.1.0/sql/sql_base.cc:5090
#11 0x000057cc67a90d82 in open_tables (thd=0x78341c000c40, start=0x78341c010270, counter=0x7834bebfb7dc, flags=0, prelocking_strategy=0x7834bebfb780)at /home/yym/mysql8/mysql-8.1.0/sql/sql_base.cc:5912
#12 0x000057cc67a9ecb4 in open_tables (thd=0x78341c000c40, tables=0x78341c010270, counter=0x7834bebfb7dc, flags=0) at /home/yym/mysql8/mysql-8.1.0/sql/sql_base.h:453
#13 0x000057cc67ca2a6c in mysql_create_table (thd=0x78341c000c40, create_table=0x78341c025930, create_info=0x7834bebfc2a0, alter_info=0x7834bebfc3e0)at /home/yym/mysql8/mysql-8.1.0/sql/sql_table.cc:10068
#14 0x000057cc6839b239 in Sql_cmd_create_table::execute (this=0x78341c028c78, thd=0x78341c000c40) at /home/yym/mysql8/mysql-8.1.0/sql/sql_cmd_ddl_table.cc:435
#15 0x000057cc67bbdfbb in mysql_execute_command (thd=0x78341c000c40, first_level=true) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:3736
#16 0x000057cc67bc3cb3 in dispatch_sql_command (thd=0x78341c000c40, parser_state=0x7834bebfd9f0) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:5447
#17 0x000057cc67bb90d7 in dispatch_command (thd=0x78341c000c40, com_data=0x7834bebfe340, command=COM_QUERY) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:2112
#18 0x000057cc67bb6f77 in do_command (thd=0x78341c000c40) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:1459
#19 0x000057cc67e0e835 in handle_connection (arg=0x57cca10e5ca0) at /home/yym/mysql8/mysql-8.1.0/sql/conn_handler/connection_handler_per_thread.cc:303
#20 0x000057cc69d4dbdc in pfs_spawn_thread (arg=0x57cca0dc1ea0) at /home/yym/mysql8/mysql-8.1.0/storage/perfschema/pfs.cc:3043
#21 0x00007834cd694ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#22 0x00007834cd726850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
##gdb
(gdb) bt
#0 my_sql_parser_parse (YYTHD=0x78341c000c40, parse_tree=0x7834bebfd710) at /var/lib/pb2/sb_1-11858416-1687334106.05/dist_GPL/sql/sql_yacc.cc:25429
#1 0x000057cc67abb61c in THD::sql_parser (this=0x78341c000c40) at /home/yym/mysql8/mysql-8.1.0/sql/sql_class.cc:3073
#2 0x000057cc67bc8985 in parse_sql (thd=0x78341c000c40, parser_state=0x7834bebfd9f0, creation_ctx=0x0) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:7213
#3 0x000057cc67bc36ae in dispatch_sql_command (thd=0x78341c000c40, parser_state=0x7834bebfd9f0) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:5344
#4 0x000057cc67bb90d7 in dispatch_command (thd=0x78341c000c40, com_data=0x7834bebfe340, command=COM_QUERY) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:2112
#5 0x000057cc67bb6f77 in do_command (thd=0x78341c000c40) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:1459
#6 0x000057cc67e0e835 in handle_connection (arg=0x57cca10e5ca0) at /home/yym/mysql8/mysql-8.1.0/sql/conn_handler/connection_handler_per_thread.cc:303
#7 0x000057cc69d4dbdc in pfs_spawn_thread (arg=0x57cca0dc1ea0) at /home/yym/mysql8/mysql-8.1.0/storage/perfschema/pfs.cc:3043
#8 0x00007834cd694ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#9 0x00007834cd726850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
(gdb) list
25424 | yyparse. |
25425 `----------*/
25426
25427 int
25428 yyparse (class THD *YYTHD, class Parse_tree_root **parse_tree)
25429 {
25430 /* The lookahead symbol. */
25431 int yychar;
25432
25433
(gdb) p YYTHD->query().str
$1 = 0x78341c015040 "CREATE TABLE `o2o_hot_marketing` (\n `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',\n `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '活动名称',"...
(gdb)
##词法分析gdb堆栈信息
(gdb) bt
#0 my_sql_parser_lex (yacc_yylval=0x7607825fb960, yylloc=0x7607825fb980, thd=0x76069c001070) at /home/yym/mysql8/mysql-8.1.0/sql/sql_lex.cc:1356
#1 0x000060fe1444b23f in my_sql_parser_parse (YYTHD=0x76069c001070, parse_tree=0x7607825fd710) at /var/lib/pb2/sb_1-11858416-1687334106.05/dist_GPL/sql/sql_yacc.cc:25622
#2 0x000060fe140d161c in THD::sql_parser (this=0x76069c001070) at /home/yym/mysql8/mysql-8.1.0/sql/sql_class.cc:3073
#3 0x000060fe141de985 in parse_sql (thd=0x76069c001070, parser_state=0x7607825fd9f0, creation_ctx=0x0) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:7213
#4 0x000060fe141d96ae in dispatch_sql_command (thd=0x76069c001070, parser_state=0x7607825fd9f0) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:5344
#5 0x000060fe141cf0d7 in dispatch_command (thd=0x76069c001070, com_data=0x7607825fe340, command=COM_QUERY) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:2112
#6 0x000060fe141ccf77 in do_command (thd=0x76069c001070) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:1459
#7 0x000060fe14424835 in handle_connection (arg=0x60fe5942f6a0) at /home/yym/mysql8/mysql-8.1.0/sql/conn_handler/connection_handler_per_thread.cc:303
#8 0x000060fe16363bdc in pfs_spawn_thread (arg=0x60fe5942eae0) at /home/yym/mysql8/mysql-8.1.0/storage/perfschema/pfs.cc:3043
#9 0x0000760791494ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#10 0x0000760791526850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
(gdb) p thd->query().str
$15 = 0x76069c0ad510 "select * from o2o_boss_order_detail\n LIMIT 0, 1000"
##执行命令
#0 mysql_execute_command (thd=0x76069c001070, first_level=true) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:2990
#1 0x000060fe141d9cb3 in dispatch_sql_command (thd=0x76069c001070, parser_state=0x7607825fd9f0) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:5447
#2 0x000060fe141cf0d7 in dispatch_command (thd=0x76069c001070, com_data=0x7607825fe340, command=COM_QUERY) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:2112
#3 0x000060fe141ccf77 in do_command (thd=0x76069c001070) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:1459
#4 0x000060fe14424835 in handle_connection (arg=0x60fe5942f6a0) at /home/yym/mysql8/mysql-8.1.0/sql/conn_handler/connection_handler_per_thread.cc:303
#5 0x000060fe16363bdc in pfs_spawn_thread (arg=0x60fe5942eae0) at /home/yym/mysql8/mysql-8.1.0/storage/perfschema/pfs.cc:3043
#6 0x0000760791494ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#7 0x0000760791526850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
##通过语法树给sql命令行赋值
#0 PT_select_stmt::make_cmd (this=0x76069c01e1a8, thd=0x76069c001070) at /home/yym/mysql8/mysql-8.1.0/sql/parse_tree_nodes.cc:750
#1 0x000060fe14188761 in LEX::make_sql_cmd (this=0x76069c004650, parse_tree=0x76069c01e1a8) at /home/yym/mysql8/mysql-8.1.0/sql/sql_lex.cc:5032
#2 0x000060fe140d1676 in THD::sql_parser (this=0x76069c001070) at /home/yym/mysql8/mysql-8.1.0/sql/sql_class.cc:3082
#3 0x000060fe141de985 in parse_sql (thd=0x76069c001070, parser_state=0x7607825fd9f0, creation_ctx=0x0) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:7213
#4 0x000060fe141d96ae in dispatch_sql_command (thd=0x76069c001070, parser_state=0x7607825fd9f0) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:5344
#5 0x000060fe141cf0d7 in dispatch_command (thd=0x76069c001070, com_data=0x7607825fe340, command=COM_QUERY) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:2112
#6 0x000060fe141ccf77 in do_command (thd=0x76069c001070) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:1459
#7 0x000060fe14424835 in handle_connection (arg=0x60fe5942f6a0) at /home/yym/mysql8/mysql-8.1.0/sql/conn_handler/connection_handler_per_thread.cc:303
#8 0x000060fe16363bdc in pfs_spawn_thread (arg=0x60fe5942eae0) at /home/yym/mysql8/mysql-8.1.0/storage/perfschema/pfs.cc:3043
#9 0x0000760791494ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#10 0x0000760791526850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
##发送网络数据包
(gdb) b /home/yym/mysql8/mysql-8.1.0/sql/sql_union.cc:1785
(gdb) bt
#0 Protocol_classic::end_row (this=0x76069c0052e0) at /home/yym/mysql8/mysql-8.1.0/sql/protocol_classic.cc:3364
#1 0x000060fe1492ded3 in Query_result_send::send_data (this=0x76069cbbe118, thd=0x76069c001070, items=...) at /home/yym/mysql8/mysql-8.1.0/sql/query_result.cc:108
#2 0x000060fe14324ef0 in Query_expression::ExecuteIteratorQuery (this=0x76069cb9eb70, thd=0x76069c001070) at /home/yym/mysql8/mysql-8.1.0/sql/sql_union.cc:1785
#3 0x000060fe14325181 in Query_expression::execute (this=0x76069cb9eb70, thd=0x76069c001070) at /home/yym/mysql8/mysql-8.1.0/sql/sql_union.cc:1823
#4 0x000060fe14265cf6 in Sql_cmd_dml::execute_inner (this=0x76069cbbe0e0, thd=0x76069c001070) at /home/yym/mysql8/mysql-8.1.0/sql/sql_select.cc:1022
#5 0x000060fe14265067 in Sql_cmd_dml::execute (this=0x76069cbbe0e0, thd=0x76069c001070) at /home/yym/mysql8/mysql-8.1.0/sql/sql_select.cc:793
#6 0x000060fe141d7841 in mysql_execute_command (thd=0x76069c001070, first_level=true) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:4797
#7 0x000060fe141d9cb3 in dispatch_sql_command (thd=0x76069c001070, parser_state=0x7607825fd9f0) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:5447
#8 0x000060fe141cf0d7 in dispatch_command (thd=0x76069c001070, com_data=0x7607825fe340, command=COM_QUERY) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:2112
#9 0x000060fe141ccf77 in do_command (thd=0x76069c001070) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:1459
#10 0x000060fe14424835 in handle_connection (arg=0x60fe5942f6a0) at /home/yym/mysql8/mysql-8.1.0/sql/conn_handler/connection_handler_per_thread.cc:303
#11 0x000060fe16363bdc in pfs_spawn_thread (arg=0x60fe5942eae0) at /home/yym/mysql8/mysql-8.1.0/storage/perfschema/pfs.cc:3043
#12 0x0000760791494ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#13 0x0000760791526850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81(gdb) bt
#0 my_net_write (net=0x76069c002ea8,packet=0x76069c015210 "\aioshd61\006\061.1.45\002\066\061\023\062\060\062\062-06-22 10:27:00=https://gangan-test1.oss-cn-beijing.aliyuncs.com/wgt-new.html\001\071\001\061\001\060\001\061\n指定包1\373\001\060\373\373a/ios/fxbhd.plist\001\066\001\061\001\060\001\061\374+\001更新包大小:112.81M|更新内容:|1、", <incomplete sequence \351>..., len=124) at /home/yym/mysql8/mysql-8.1.0/sql-common/net_serv.cc:435
#1 0x000060fe1492aeab in Protocol_classic::end_row (this=0x76069c0052e0) at /home/yym/mysql8/mysql-8.1.0/sql/protocol_classic.cc:3366
#2 0x000060fe1492ded3 in Query_result_send::send_data (this=0x76069cbbe118, thd=0x76069c001070, items=...) at /home/yym/mysql8/mysql-8.1.0/sql/query_result.cc:108
#3 0x000060fe14324ef0 in Query_expression::ExecuteIteratorQuery (this=0x76069cb9eb70, thd=0x76069c001070) at /home/yym/mysql8/mysql-8.1.0/sql/sql_union.cc:1785
#4 0x000060fe14325181 in Query_expression::execute (this=0x76069cb9eb70, thd=0x76069c001070) at /home/yym/mysql8/mysql-8.1.0/sql/sql_union.cc:1823
#5 0x000060fe14265cf6 in Sql_cmd_dml::execute_inner (this=0x76069cbbe0e0, thd=0x76069c001070) at /home/yym/mysql8/mysql-8.1.0/sql/sql_select.cc:1022
#6 0x000060fe14265067 in Sql_cmd_dml::execute (this=0x76069cbbe0e0, thd=0x76069c001070) at /home/yym/mysql8/mysql-8.1.0/sql/sql_select.cc:793
#7 0x000060fe141d7841 in mysql_execute_command (thd=0x76069c001070, first_level=true) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:4797
#8 0x000060fe141d9cb3 in dispatch_sql_command (thd=0x76069c001070, parser_state=0x7607825fd9f0) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:5447
#9 0x000060fe141cf0d7 in dispatch_command (thd=0x76069c001070, com_data=0x7607825fe340, command=COM_QUERY) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:2112
#10 0x000060fe141ccf77 in do_command (thd=0x76069c001070) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:1459
#11 0x000060fe14424835 in handle_connection (arg=0x60fe5942f6a0) at /home/yym/mysql8/mysql-8.1.0/sql/conn_handler/connection_handler_per_thread.cc:303
#12 0x000060fe16363bdc in pfs_spawn_thread (arg=0x60fe5942eae0) at /home/yym/mysql8/mysql-8.1.0/storage/perfschema/pfs.cc:3043
#13 0x0000760791494ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#14 0x0000760791526850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
相关文章:

mysql8 从C++源码角度看sql生成抽象语法树
在 MySQL 8 的 C 源码中,SQL 语句的解析过程涉及多个步骤,包括词法分析、语法分析和抽象语法树(AST)的生成。以下是详细的解析过程和相关组件的描述: 1. 词法分析器(Lexer) MySQL 使用一个称为…...

【DeepSeek】DeepSeek概述 | 本地部署deepseek
目录 1 -> 概述 1.1 -> 技术特点 1.2 -> 模型发布 1.3 -> 应用领域 1.4 -> 优势与影响 2 -> 本地部署 2.1 -> 安装ollama 2.2 -> 部署deepseek-r1模型 1 -> 概述 DeepSeek是由中国的深度求索公司开发的一系列人工智能模型,以其…...

【C++】多态原理剖析
目录 1.虚表指针与虚表 2.多态原理剖析 1.虚表指针与虚表 🍪类的大小计算规则 一个类的大小,实际就是该类中成员变量之和,需要注意内存对齐空类:编译器给空类一个字节来唯一标识这个类的对象 对于下面的Base类,它的…...

【Rust自学】20.4. 结语:Rust学习一阶段完成+附录
喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 20.4.1. 总结 Rust初级学习之旅终于完成了!恭喜! 包括这篇文章,我们使用了110篇文章来学习Rust。 真…...

pytorch引用halcon写数据集
****加粗样式虽然啰嗦一点,但好歹halcon自己熟悉,不会忘记,用os 和 pil会导致脑子记得东西太多 import halcon as ha import torch from torch.utils.data import Datasetpath0 rE:\BaiduNetdiskDownload\cell class MyDataset(Dataset):de…...

让文物“活”起来,以3D数字化技术传承文物历史文化!
文物,作为不可再生的宝贵资源,其任何毁损都是无法逆转的损失。然而,当前文物保护与修复领域仍大量依赖传统技术,同时,文物管理机构和专业团队的力量相对薄弱,亟需引入数字化管理手段以应对挑战。 积木易搭…...

aarch64 Ubuntu20.04 安装docker
安装 docker 依赖项:sudo apt-get update sudo apt-get install apt-transport-https ca-certificates curl gnupg lsb-release添加 Docker GPG 密钥:curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyr…...

JAVA:CloseableHttpClient 进行 HTTP 请求的技术指南
1、简述 CloseableHttpClient 是 Apache HttpComponents 提供的一个强大 HTTP 客户端库。它允许 Java 程序与 HTTP/HTTPS 服务交互,可以发送 GET、POST 等各种请求类型,并处理响应。该库广泛用于 REST API 调用、文件上传和下载等场景。 2、特性 Close…...

Mac上搭建k8s环境——Minikube
1、在mac上安装Minikube可执行程序 brew cask install minikub 安装后使用minikube version命令查看版本 2、安装docker环境 brew install --cask --appdir/Applications docker #安装docker open -a Docker #启动docker 3、安装kubectl curl -LO https://storage.g…...

经典排序算法复习----C语言
经典排序算法复习 分类 交换类 冒泡快排 分配类 计数排序基数排序 选择类 选择排序 堆排序 归并类 归并排序 插入类 直接插入排序 希尔排序 折半插入排序 冒泡排序 基于交换。每一轮找最大值放到数组尾部 //冒泡排序 void bubSort(int* arr,int size){bool sorte…...

自动驾驶数据集三剑客:nuScenes、nuImages 与 nuPlan 的技术矩阵与生态协同
目录 1、引言 2、主要内容 2.1、定位对比:感知与规划的全维覆盖 2.2、数据与技术特性对比 2.3、技术协同:构建全栈研发生态 2.4、应用场景与评估体系 2.5、总结与展望 3、参考文献 1、引言 随着自动驾驶技术向全栈化迈进,Motional 团…...

[LUA ERROR] bad light userdata pointer
Cocos2d项目,targetSdkVersion30,在 android 13 设备运行报错: [LUA ERROR] bad light userdata pointer ,导致黑屏。 参考 cocos2dx 适配64位 arm64-v8a 30 lua 提示 bad light userdata pointer 黑屏-CSDN博客的方法 下载最新的Cocos2dx …...

【Java八股】JVM
JVM 1. jvm内存区域分为哪些部分 线程私有的:程序计数器、虚拟机栈、本地方法栈 程序计数器:指示当前线程执行到的字节码文件的行号,是线程切换后保证线程能恢复到正确的执行位置的关键 虚拟机栈:用于存储方法调用的数据&…...

集成学习(一):从理论到实战(附代码)
一、引言 在机器学习领域,打造一个独立、强大的算法是解决问题的关键。然而,集成学习提供了一种不同的视角:通过组合多个“弱”学习器来创建一个更强大的模型。本文探讨集成学习的思想、方法及其应用。 二、机器学习 vs 集成学习思想 传统…...

Netty:高性能网络应用框架的深度解析
引言 Netty 是由 JBoss 提供的一个开源的 Java NIO 客户端/服务器框架,它用以快速开发网络应用程序,如协议服务器和客户端。它的设计目标是提供异步事件驱动的网络应用程序框架,支持高效的网络通信和数据处理。Netty 在性能、可扩展性、安全…...

神经网络常见激活函数 3-ReLU函数(修正线性单元)
文章目录 ReLU函数求导函数和导函数图像优缺点pytorch 中的 ReLU 函数tensorflow 中的ReLU函数 ReLU 修正线性单元 (Rectified Linear Unit) 函数求导 ReLU函数 ReLU max ( 0 , x ) { x x ≥ 0 0 x < 0 \begin{aligned} \operatorname{ReL…...

Android开发获取缓存,删除缓存
Android开发获取缓存,删除缓存 app设置中往往有清理缓存的功能。会显示当前缓存时多少,然后可以点击清理缓存 直接上代码: object CacheHelper {/*** 获取缓存大小* param context* return* throws Exception*/JvmStaticfun getTotalCache…...

如何通过PHP接入DeepSeek的API
想知道如何通过PHP接入DeepSeek的API。看起来他对之前的Python步骤比较熟悉,但这次想用PHP实现。 首先,我需要回顾一下DeepSeek API的文档,确认它支持哪些方法和参数。假设用户已经配置了环境变量,比如API密钥,接下来…...

一种基于Leaflet.Legend的图例动态更新方法
目录 前言 一、场景再现 1、需求描述 2、核心方法介绍 3、存在的问题 二、问题解决 1、重复解决办法 2、图例不展示解决办法 3、成果展示 三、总结 前言 在当今数字化时代,地理信息系统(GIS)技术已经广泛应用于各个领域,…...

Spring Boot: 使用 @Transactional 和 TransactionSynchronization 在事务提交后发送消息到 MQ
Spring Boot: 使用 Transactional 和 TransactionSynchronization 在事务提交后发送消息到 MQ 在微服务架构中,确保消息的可靠性和一致性非常重要,尤其是在涉及到分布式事务的场景中。本文将演示如何使用 Spring Boot 的事务机制和 TransactionSynchron…...

LQB(2)-python-枚举
前言 python中的枚举一般有两个说法,一个是枚举算法(暴力求解法,算法层面),一个是遍历使用enumerate()函数或者enum模块创建()。 暴力求解法在之前的博文里面讲过了👇,…...

MongoDB开发规范
分级名称定义P0核心系统需7*24不间断运行,一旦发生不可用,会直接影响核心业务的连续性,或影响公司名誉、品牌、集团战略、营销计划等,可能会造成P0-P2级事故发生。P1次核心系统这些系统降级或不可用,会间接影响用户使用…...

为什么DeepSeek服务器繁忙?
致敬DeepSeek 用户层面 用户数量激增:DeepSeek 免费且功能强大,对普通用户和开发者都极具吸引力124。尤其是在新功能推出、新模型上线或相关热门活动期间,大量用户会在短时间内涌入9。例如春节期间,DeepSeek 的用户量达到四千万7。…...

律所录音证据归集工具:基于PyQt6与多线程的自动化音频管理解决方案
在律所日常工作中,音频证据的整理与归集是一个高频且复杂的任务。面对大量的案件录音文件,如何实现快速且准确的分类与存档,成为了律所提高效率、降低出错率的关键。本文将通过技术角度解析一款名为律所录音证据归集工具的项目,详…...

【含开题报告+文档+PPT+源码】基于SpringBoot+Vue旅游管理网站
开题报告 本论文探讨了一款采用现代Web开发技术构建的台州市旅游综合信息与服务平台的设计与实现。该系统基于SpringBoot框架,以其轻量级、快速开发和强大的企业级应用支持能力为核心后端技术支撑,结合Vue.js前端框架及ElementUI组件库,为用…...

unity碰撞的监测和监听
1.创建一个地面 2.去资源商店下载一个火焰素材 3.把procedural fire导入到自己的项目包管理器中 4.给magic fire 0 挂在碰撞组件Rigidbody , Sphere Collider 5.创建脚本test 并挂在magic fire 0 脚本代码 using System.Collections; using System.Collections.Generic; usi…...

DeepSeek-R1 32B Windows+docker本地部署
最近国产大模型DeepSeek兴起,本地部署了一套deepseek同时集成Open WebUI界面,给大家出一期教程。 软件:Ollama、docker、Open WebUI 一、用Ollama下载模型 首先我们需要安装Ollama,它可以在本地运行和管理大模型。 到Ollama官网 https://ol…...

C++11新特性之unique_ptr智能指针
本节继续介绍智能指针,不了解的读者可以先阅读——C11新特性之shared_ptr智能指针-CSDN博客 1.介绍 unique_ptr是C11标准提供的另一种智能指针。与shared_ptr不同的是,unique_ptr指针指向的堆内存无法同其他unique_ptr共享,也就是每一片堆内…...

Vue与Konva:解锁Canvas绘图的无限可能
前言 在现代Web开发中,动态、交互式的图形界面已成为提升用户体验的关键要素。Vue.js,作为一款轻量级且高效的前端框架,凭借其响应式数据绑定和组件化开发模式,赢得了众多开发者的青睐。而当Vue.js邂逅Konva.js,两者结…...

python绘图之柱状堆积图的绘制
本节来学习用python来绘制柱状堆积图. 使用的库为matplotlib.pyplot,numpy 代码如下 # 导入必要的库 import matplotlib.pyplot as plt # 用于绘图 import numpy as np # 用于数值计算# 模拟一些数据 x [数值{}.format(i) for i in range(10)] # 创建一个包含10个元素的列…...