mysql8 从C++源码角度看sql生成抽象语法树

news/2025/2/9 5:44:36 标签: c++, sql, 开发语言, mysql

MySQL 8的C++源码中,SQL语句的词法分析和语法分析是通过一个复杂的解析器实现的,这个解析器将输入的SQL文本转换成抽象语法树(AST)。以下是该过程的主要步骤和相关组件:

主要组件
Lexer (词法分析器):
MySQL使用了一个称为MYSQL_LEX的类来进行词法分析。它负责将SQL查询字符串分解成一系列的标记(tokens),如关键字、标识符、操作符等。
在dispatch_sql_command函数中,调用parse_sql函数开始解析过程,这会触发词法分析器的工作。
Parser (语法分析器):
MySQL的语法分析器基于Bison生成,定义在.y文件中(例如sql_yacc.yy),这些文件定义了SQL语言的语法规则。
当词法分析器生成了一系列标记后,语法分析器根据这些标记和预定义的语法规则构建抽象语法树(AST)。
Abstract Syntax Tree (抽象语法树):
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进行一些重写操作(例如,应用插件或执行特定的优化)。
最终,解析后的命令会被传递给查询执行引擎(通过调用mysql_execute_command)来执行实际的操作。
关键点总结
词法分析: 将SQL文本分割成标记,识别出SQL语句中的各个组成部分。
语法分析: 根据SQL语言的语法规则检查这些标记,并构造抽象语法树。
抽象语法树: 表示SQL查询的逻辑结构,便于后续的查询优化和执行计划生成。

thd->sql_parser();
在这行代码执行时,内部会初始化并使用MYSQL_LEX对象进行词法分析。这意味着当调用thd->sql_parser()时,实际上启动了整个解析流程,包括词法分析步骤。

语法分析
语法分析则是基于Bison生成的解析器来实现的,这个解析器根据词法分析器产生的标记序列,按照SQL语法规则检查这些标记,并构建出抽象语法树(AST)。同样地,这一过程也是通过调用thd->sql_parser()触发的。在这个调用过程中,解析器会根据.y文件(例如sql_yacc.yy)中定义的规则来进行语法分析。

关键代码行
触发词法和语法分析:
Cpp
深色版本
const bool mysql_parse_status = thd->sql_parser();
这行代码是关键点,它触发了对输入SQL文本的解析过程,其中包含了词法分析和语法分析两个阶段的工作。具体来说,thd->sql_parser()调用了MySQL内部的解析机制,该机制首先通过词法分析将输入字符串转换为标记流,然后通过语法分析根据这些标记构建抽象语法树。

THD::sql_parser()函数实现中,词法分析和语法分析的具体实现细节被封装在了通过YACC从sql_yacc.yy生成的解析器函数中。虽然直接的词法分析和语法分析代码没有在这段代码中明确展示,但我们可以识别出触发这些过程的关键调用。

关键代码行
触发词法和语法分析:
Cpp
深色版本
extern int my_sql_parser_parse(class THD * thd, class Parse_tree_root * *root);
...
if (my_sql_parser_parse(this, &root) || is_error()) {
  cleanup_after_parse_error();
  return true;
}
这行代码 my_sql_parser_parse(this, &root); 是关键点,它调用了由YACC从sql_yacc.yy生成的解析器函数my_sql_parser_parse。这个函数负责执行SQL语句的词法分析和语法分析,并构建出解析树(parse tree)。

解析流程
词法分析:在my_sql_parser_parse内部,首先会使用MYSQL_LEX对象对输入的SQL字符串进行词法分析,将SQL文本分割成一系列标记(tokens)。每个标记代表SQL语句的一部分,如关键字、标识符、操作符等。
语法分析:接着,基于Bison生成的解析器根据这些标记以及预定义的SQL语法规则进行语法分析。如果标记序列符合语法规则,则解析器会构建出一个解析树(parse tree),并最终转换为抽象语法树(AST)。
返回结果:
如果解析成功,my_sql_parser_parse函数返回0,并且THD::is_error()为false。
如果解析失败,函数返回非零值,或者THD::is_error()为true。
总结
词法分析和语法分析都是在调用my_sql_parser_parse(this, &root);时发生的。这是整个解析过程中最关键的部分,它启动了MySQL内部的词法和语法分析机制。
具体的词法分析是由MYSQL_LEX类完成的,而语法分析则是由Bison生成的解析器根据.y文件(例如sql_yacc.yy)中的规则来处理的。

##

/**
  Parse an SQL command from a text string and pass the resulting AST to the
  query 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 zero
  if (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 we
      execute only first. We set it to not see the ';' otherwise it would get
      into 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 Schema
      statement tables. (Raw logging happened earlier.)

      Sub-routines of mysql_rewrite_query() should try to only rewrite when
      necessary (e.g. not do password obfuscation when query contains no
      password).

      If rewriting does not happen here, thd->m_rewritten_query is still
      empty 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 below

      thd->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));
          else
            my_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 did
      not 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 query
           containing a password will have the password exposed. This is very
           unlikely, and this behavior can be documented. Remediation is to
           use a new password when retyping the corrected query.

        b) If we do not log the query, finding broken queries in the client
           application will be much more difficult. This is much more likely.

      Considering that broken queries can typically be generated by attempts at
      SQL injection, finding the source of the SQL injection is critical, so the
      design 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 the
  supplied parser state and object creation context.

  This is a wrapper() for THD::sql_parser() and should generally be used for AST
  construction.

  The function may optionally generate a query digest, invoke this function as
  follows:


  @verbatim
    THD *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 digest
          set 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 parsing
    whether the current command is a diagnostic statement, in which case
    we'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 become
    the 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 we
    can answer questions about it.  This specifically means
    that repeatedly asking about a DA won't clear it.

    Otherwise, it's a regular command with no issues during
    parsing, so we'll just clear the DA in preparation for
    the processing of this command.
  */

  if (parser_da->current_statement_cond_count() != 0) {
    /*
      Error/warning during parsing: top DA should contain parse error(s)!  Any
      pre-existing conditions will be replaced. The exception is diagnostics
      statements, in which case we wish to keep the errors so they can be sent
      to 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 it
      now contains not the results of the previous executions, but
      a 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 an
    internal error handler is set.

    The assert will not catch a situation where parsing fails without an
    error reported if an error handler exists. The problem is that the
    error handler might have intercepted the error, so thd->is_error() is
    not set. However, there is no way to be 100% sure here (the error
    handler 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 parsing
      a stored procedure. We must ensure that a parsing error
      does 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;
}

##/home/yym/mysql8/mysql-8.1.0/sql/sql_yacc.cc

#define yyparse         my_sql_parser_parse

##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)


http://www.niftyadmin.cn/n/5845607.html

相关文章

C++ 23 的栈踪迹库(stacktrace)

1 Boost.Stacktrace ​ 当程序发生错误的时候&#xff0c;能提供的信息越多&#xff0c;对错误的定位就越有利。C#、Pyrhon、Java 等编程语言都提供调用栈踪迹回溯的功能&#xff0c;在错误发生的时候&#xff0c;除了报告错误发生的位置&#xff0c;还能输出函数调用栈信息。…

大模型推理——MLA实现方案

1.整体流程 先上一张图来整体理解下MLA的计算过程 2.实现代码 import math import torch import torch.nn as nn# rms归一化 class RMSNorm(nn.Module):""""""def __init__(self, hidden_size, eps1e-6):super().__init__()self.weight nn.Pa…

Qt实现简易视频播放器

使用Qt6实现简易音乐播放器&#xff0c;效果如下&#xff1a; github&#xff1a; Gabriel-gxb/VideoPlayer: qt6实现简易视频播放器 一、整体架构 该代码整体架构围绕着MainWindow类构建一个媒体播放器相关的应用程序。 主要组件 &#xff08;一&#xff09;界面组件&…

携手AWS,零成本在EKS上体验AutoMQ企业版

01 前言 AutoMQ是一款贯彻云优先理念来设计的 Kafka 替代产品。AutoMQ 创新地对 Apache Kafka 的存储层进行了基于云的重新设计&#xff0c;在 100% 兼容 Kafka 的基础上通过将持久性分离至 EBS 和 S3 带来了 10x 的成本降低以及 100x 的弹性能力提升&#xff0c;并且相比 Apa…

springcloud gateway 负载均衡

Spring Cloud Gateway的负载均衡是Spring Cloud生态系统中一个非常重要的功能&#xff0c;它使得微服务架构中的服务调用能够更加高效和均衡。以下是关于Spring Cloud Gateway负载均衡的详细解析&#xff1a; 一、Spring Cloud Gateway简介 Spring Cloud Gateway是一个基于Sp…

使用 Apifox、Postman 测试 Dubbo 服务,Apache Dubbo OpenAPI 即将发布

作者&#xff1a;何亮&#xff0c;Apache Dubbo Contributor Apache Dubbo OpenAPI 简介 设计背景 在微服务体系中&#xff0c;RPC 服务的文档管理、测试、调用协作一直都是影响研发效能的关键一环&#xff0c;这些难题通常是由于 RPC 的特性所决定的&#xff1a;RPC 服务的…

SpringSecurity:授权服务器与客户端应用(入门案例)

文章目录 一、需求概述二、开发授权服务器1、pom依赖2、yml配置3、启动服务端 三、开发客户端应用1、pom依赖2、yml配置3、SecurityConfig4、接口5、测试 一、需求概述 maven需要3.6.0以上版本 二、开发授权服务器 1、pom依赖 <dependency><groupId>org.springfr…

leetcode_深度遍历和广度遍历 100. 相同的树

100. 相同的树 给你两棵二叉树的根节点 p 和 q &#xff0c;编写一个函数来检验这两棵树是否相同。 如果两棵树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是相同的。 思路: (递归法) 返回True的情况: 两棵树都为空两棵树相同 返回False的情况: 两棵…