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 是由多个类组成的复杂数据结构,主要由 LEXItem 类家族来表示。

  • LEX 对象:包含了整个查询的上下文信息,包括但不限于表名、列名、表达式等。

  • Item 类及其子类:用于表示查询中的各种元素,如表达式、条件、值等。

解析流程

  1. 初始化

    • dispatch_sql_command 函数中,首先调用了 lex_start(thd),这初始化了解析环境,为接下来的词法和语法分析做好准备。

  2. 词法分析

    • 调用 parse_sql(thd, parser_state, nullptr) 启动解析过程。这里,parser_state 包含了当前解析状态的信息,包括词法分析器的状态。

    • MYSQL_LEX 对象作为词法分析器的核心,逐字符读取 SQL 查询,并将其转换为标记序列。

  3. 语法分析

    • 使用 Bison 生成的解析器根据词法分析器产生的标记序列,按照 SQL 语法规则进行解析,并构建出抽象语法树(AST)。

    • 如果解析过程中遇到错误,比如语法错误,解析器会设置错误标志并返回相应的错误信息。

  4. 后续处理

    • 解析完成后,可能会对 AST 进行一些重写操作(例如,应用插件或执行特定的优化)。

    • 最终,解析后的命令会被传递给查询执行引擎(通过调用 mysql_execute_command)来执行实际的操作。

关键代码行

  1. 触发词法和语法分析

    cpp

    const bool mysql_parse_status = thd->sql_parser();

    这行代码是关键点,它触发了对输入 SQL 文本的解析过程,其中包含了词法分析和语法分析两个阶段的工作。

  2. 调用解析器函数

    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 命令(如 SELECTINSERT 等)会有对应的派生类(如 Sql_cmd_selectSql_cmd_insert 等)。

参数:
  • parse_tree:解析树的根节点,表示一个 SQL 语句的抽象语法树(AST)。

逻辑:
  1. will_contextualize 检查

    • 如果 will_contextualize 为 false,则直接返回 false,表示不需要进一步处理。

    • 这个标志通常用于控制是否需要将解析树转换为 Sql_cmd 对象。

  2. 创建 Sql_cmd 对象

    • 调用 parse_tree->make_cmd(thd),通过解析树创建对应的 Sql_cmd 对象。

    • 如果创建失败(返回 nullptr),则返回 true,表示出错。

  3. 断言检查

    • 使用 assert 检查 m_sql_cmd->sql_command_code() 是否与 sql_command 一致。

    • 这一步确保创建的 Sql_cmd 对象的命令类型与 LEX 中记录的 sql_command 一致。

  4. 返回值

    • 如果成功创建 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 对象,包含线程相关的上下文信息。

逻辑:
  1. 初始化 Parse_context

    • 创建一个 Parse_context 对象 pc,用于管理解析过程中的上下文信息。

    • thd->lex->current_query_block() 获取当前查询块(Query_block)。

  2. 设置 sql_command

    • 将 thd->lex->sql_command 设置为 m_sql_command

    • m_sql_command 是 PT_select_stmt 的成员变量,表示当前 SQL 命令的类型(如 SQLCOM_SELECT)。

  3. 上下文化查询表达式

    • 调用 m_qe->contextualize(&pc),对查询表达式进行上下文化处理。

    • 如果失败,则返回 nullptr

  4. 检查 INTO 子句

    • 检查是否存在多个 INTO 子句,如果存在则报错(ER_MULTIPLE_INTO_CLAUSES)。

    • 调用 contextualize_safe(&pc, m_into),对 INTO 子句进行上下文化处理。

  5. 完成查询表达式

    • 调用 pc.finalize_query_expression(),完成查询表达式的处理。

    • 如果失败,则返回 nullptr

  6. 处理警告

    • 如果存在 INTO 子句和锁子句(如 FOR UPDATE),则发出警告(ER_WARN_DEPRECATED_INNER_INTO)。

    • 对于 UNION 查询中的 INTO 子句,也会发出警告。

  7. 创建 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/69539.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

excel合并表格

上一章说到excel拆分表格&#xff0c;可以按一列的不重复数据自动拆分成多个表格。这个功能主要适用于有多个下级机构的部门分发表格使用。表格分发完成&#xff0c;下级单位修改后&#xff0c;上传到我们这里。我们还得把这些表格合并成一个表。如果利用复制粘性&#xff0c;工…

区块链100问之加密算法

区块链100问之加密算法 文章目录 区块链100问之加密算法哈希算法是什么&#xff1f;有什么特征&#xff1f;哈希碰撞是什么?雪崩效应呢&#xff1f;如何解决&#xff1f;哈希算法的作用&#xff1f;对称加密和非对称加密有什么区别&#xff1f;为什么会引入非对称加密&#xf…

模型压缩中的四大核心技术 —— 量化、剪枝、知识蒸馏和二值化

一、量化 (Quantization) 量化的目标在于将原始以 32 位浮点数表示的模型参数和中间激活,转换为低精度(如 FP16、INT8、甚至更低位宽)的数值表示,从而在减少模型存储占用和内存带宽的同时,加速推理运算,特别适用于移动、嵌入式和边缘计算场景。 1.1 概念与目标 基本思想…

【LLM】o1/R1系列LLM数据篇

关于思维链推理的10开源数据集&#xff1a; 目前开源的数据主要有如下&#xff1a; 1、Magpie-Reasoning-V2数据集&#xff0c;其中包含DeepSeek-R1生成的250K思路链推理样本&#xff0c;这些示例涵盖了数学推理、编码和一般问题解决等各种任务。https://huggingface.co/datas…

elasticsearch实战应用从入门到高效使用java集成es快速上手

Elasticsearch 因其出色的性能、可扩展性和易用性,成为了处理大规模数据和构建搜索引擎的首选工具。本文将通过一个实际案例,详细讲解如何在 Spring Boot 项目中集成 Elasticsearch,进行数据索引、搜索、聚合分析等操作。 一、Elasticsearch 简介 Elasticsearch 是一个基于…

Centos Stream 10 根目录下的文件夹结构

/ ├── bin -> usr/bin ├── boot ├── dev ├── etc ├── home ├── lib -> usr/lib ├── lib64 -> usr/lib64 ├── lostfound ├── media ├── mnt ├── opt ├── proc ├── root ├── run ├── sbin -> usr/sbin ├── srv ├─…

旋转位置编码(RoPE)讲解和代码实现

旋转位置编码(Rotary Position Embedding:RoPE)讲解和代码实现 1. 什么是位置编码? 在 Transformer 模型中,位置编码的作用是为模型提供序列中每个 token 的位置信息。因为 Transformer 本身没有像 RNN 那样的顺序结构,所以需要通过位置编码来告诉模型 token 的顺序。 …

绘制中国平安股价的交互式 K 线图

在本文中,探索如何使用 Python 的强大库进行股市数据分析与可视化。我们将以中国平安(股票代码:sh601318)为例,展示如何获取其股票数据,并绘制一张交互式 K 线图。 K 线图是股市分析中不可或缺的工具,它能够直观地显示股票的波动情况,包括开盘价、收盘价、最高价和最低…

HTML应用指南:利用GET请求获取全国盒马门店位置信息

随着新零售业态的发展,门店位置信息的获取变得至关重要。作为新零售领域的先锋,盒马鲜生不仅在商业模式创新上持续领先,还积极构建广泛的门店网络,以支持其不断增长的用户群体。本篇文章,我们将继续探究GET请求的实际应用,我们使用Python的requests库通过GET请求,从盒马…

(原创,可用)SSH实现内外网安全穿透(安全不怕防火墙)

目前有A、B终端和一台服务器&#xff0c;A、B机器不能直接访问&#xff0c;服务器不能直接访问A、B终端但是A、B终端可以访问服务器&#xff0c;这个场景很像我们有一台电脑在单位内网&#xff0c;外机器想访问内网系统&#xff0c;可能大家目前想到的就是frp之类穿透工具&…

运维_Mac环境单体服务Docker部署实战手册

Docker部署 本小节&#xff0c;讲解如何将前端 后端项目&#xff0c;使用 Docker 容器&#xff0c;部署到 dev 开发环境下的一台 Mac 电脑上。 1 环境准备 需要安装如下环境&#xff1a; Docker&#xff1a;容器MySQL&#xff1a;数据库Redis&#xff1a;缓存Nginx&#x…

keil5显示[NO J-link found]解决办法——【J-LINK】驱动下载安装教程

打开电脑的设备管理器&#xff0c;检查通用串行总线控制器 看这里发现没有J-link driver&#xff0c;于是开始安装下驱动。 一、下载 打开J-link下载官网链接&#xff1a; SEGGER - The Embedded Experts - Downloads 点击左下角下载按钮 二、安装 等待下载完成以后双击下载…

Maven的dependencyManagements锁定版本依赖

Pom工程的父项目定义的依赖 <dependencyManagement><dependencies><!--springcloud 2023.0.0--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version&g…

基于 Nginx 的 CDN 基础实现

概览 本文是对基于Nginx的CDN网络的学习笔记&#xff0c;阅读的代码为&#xff1a;https://github.com/leandromoreira/cdn-up-and-running 其中&#xff0c;先确定CDN中的一些基础概念&#xff1a; Balancer&#xff1a;负载均衡&#xff0c;即请求数据的流量最开始打到Bal…

Coze(扣子)+ Deepseek:多Agents智能体协作开发新范式

前言 在当今数字化浪潮中&#xff0c;人工智能&#xff08;AI&#xff09;技术的迅猛发展正深刻改变着我们的生活和工作方式。从智能语音助手到自动化流程机器人&#xff0c;AI 的应用无处不在&#xff0c;为我们提供了更加便捷、高效的服务。然而&#xff0c;对于非专业人士来…

user、assistant、system三大角色在大语言模型中的作用(通俗解释)

1 概述 在大语言模型中&#xff0c;通常涉及到三种角色&#xff1a;用户&#xff08;user&#xff09;、助手&#xff08;assistant&#xff09;和系统&#xff08;system&#xff09;。简单来说&#xff0c;和大模型对话其实是三个人的电影。 2 角色定义 2.1 系统&#xf…

【文献讲解】《Non-local Neural Networks》

一、引言 传统的深度学习方法(如卷积神经网络CNN和循环神经网络RNN)在捕捉长距离依赖关系时存在局限性。CNN主要关注局部邻域的特征,而RNN则依赖于序列的递归计算,无法直接捕捉全局信息。为了解决这一问题,本文提出了一种非局部神经网络(Non-local Neural Networks),通…

BiGRU双向门控循环单元多变量多步预测,光伏功率预测(Matlab完整源码和数据)

代码地址&#xff1a;BiGRU双向门控循环单元多变量多步预测&#xff0c;光伏功率预测&#xff08;Matlab完整源码和数据) BiGRU双向门控循环单元多变量多步预测&#xff0c;光伏功率预测 一、引言 1.1、研究背景和意义 随着全球对可再生能源需求的不断增长&#xff0c;光伏…

经典题型:求数组中逆序对的数量

经典题型&#xff1a;求数组中逆序对的数量 题目思路代码 题目 链接 思路 步骤一&#xff1a;两个指针指向两个有序区间&#xff0c;进行合并操作。 步骤二&#xff1a;1与前面的区间&#xff0c;构成逆序对&#xff0c;个数就是前面区间的个数。 步骤三&#xff1a;前面数组…

安卓逆向(Xposed-Hook)

关于参数问题 &#x1f4cc; 如果方法是这样 public int addNumbers(int a, int b) {return a b; }&#x1f4cc; Hook 代码 XposedHelpers.findAndHookMethod("com.example.app.Calculator",lpparam.classLoader,"addNumbers",int.class, int.class, …