bool Prepared_statement::prepare_query()

in sql/sql_prepare.cc [1204:1427]


bool Prepared_statement::prepare_query(THD *thd) {
  DBUG_TRACE;
  assert(m_lex == thd->lex);  // set_n_backup_active_arena() guarantees that
  Query_block *query_block = m_lex->query_block;
  enum enum_sql_command sql_command = m_lex->sql_command;
  int res = 0;
  DBUG_PRINT("enter",
             ("command: %d  param_count: %u", sql_command, m_param_count));

  m_lex->first_lists_tables_same();
  Table_ref *const tables = m_lex->query_tables;

  /* set context for commands which do not use setup_tables */
  query_block->context.resolve_in_table_list_only(
      query_block->get_table_list());

  /*
    For the optimizer trace, this is the symmetric, for statement preparation,
    of what is done at statement execution (in mysql_execute_command()).
  */
  Opt_trace_start ots(thd, tables, sql_command, &m_lex->var_list,
                      thd->query().str, thd->query().length, nullptr,
                      thd->variables.character_set_client);

  const Opt_trace_object trace_command(&thd->opt_trace);
  const Opt_trace_array trace_command_steps(&thd->opt_trace, "steps");

  if ((m_lex->keep_diagnostics == DA_KEEP_COUNTS) ||
      (m_lex->keep_diagnostics == DA_KEEP_DIAGNOSTICS &&
       // keep_diagnostics can be set if DECLARE EXIT HANDLER is
       // used in the event body. But this is ok since the body is
       // not prepared.
       sql_command != SQLCOM_CREATE_EVENT &&
       sql_command != SQLCOM_ALTER_EVENT)) {
    my_error(ER_UNSUPPORTED_PS, MYF(0));
    return true;
  }

  if (sql_command_flags[sql_command] & CF_HA_CLOSE)
    mysql_ha_rm_tables(thd, tables);

  /*
    Open temporary tables that are known now. Temporary tables added by
    prelocking will be opened afterwards (during open_tables()).
  */
  if (sql_command_flags[sql_command] & CF_PREOPEN_TMP_TABLES) {
    if (open_temporary_tables(thd, tables)) return true;
  }

  switch (sql_command) {
    case SQLCOM_CREATE_VIEW:
      if (m_lex->create_view_mode == enum_view_create_mode::VIEW_ALTER) {
        my_error(ER_UNSUPPORTED_PS, MYF(0));
        return true;
      }
      res = mysql_test_create_view(thd, this);
      break;

    case SQLCOM_SET_PASSWORD:
    case SQLCOM_SET_OPTION:
      res = mysql_test_set_fields(thd, this, tables, &m_lex->var_list);
      break;

      /*
        Note that we don't need to have cases in this list if they are
        marked with CF_STATUS_COMMAND in sql_command_flags
      */
    case SQLCOM_DROP_TABLE:
    case SQLCOM_RENAME_TABLE:
    case SQLCOM_ALTER_TABLE:
    case SQLCOM_COMMIT:
    case SQLCOM_CREATE_INDEX:
    case SQLCOM_DROP_INDEX:
    case SQLCOM_ROLLBACK:
    case SQLCOM_TRUNCATE:
    case SQLCOM_DROP_VIEW:
    case SQLCOM_REPAIR:
    case SQLCOM_ANALYZE:
    case SQLCOM_OPTIMIZE:
    case SQLCOM_CHANGE_REPLICATION_SOURCE:
    case SQLCOM_CHANGE_REPLICATION_FILTER:
    case SQLCOM_RESET:
    case SQLCOM_FLUSH:
    case SQLCOM_REPLICA_START:
    case SQLCOM_REPLICA_STOP:
    case SQLCOM_INSTALL_PLUGIN:
    case SQLCOM_UNINSTALL_PLUGIN:
    case SQLCOM_CREATE_DB:
    case SQLCOM_DROP_DB:
    case SQLCOM_CHECKSUM:
    case SQLCOM_CREATE_USER:
    case SQLCOM_RENAME_USER:
    case SQLCOM_DROP_USER:
    case SQLCOM_ALTER_USER:
    case SQLCOM_ASSIGN_TO_KEYCACHE:
    case SQLCOM_PRELOAD_KEYS:
    case SQLCOM_GRANT:
    case SQLCOM_GRANT_ROLE:
    case SQLCOM_REVOKE:
    case SQLCOM_REVOKE_ALL:
    case SQLCOM_REVOKE_ROLE:
    case SQLCOM_KILL:
    case SQLCOM_ALTER_INSTANCE:
    case SQLCOM_SET_ROLE:
    case SQLCOM_ALTER_USER_DEFAULT_ROLE:
      break;

    case SQLCOM_CREATE_TABLE:
      /*
        CREATE TABLE ... START TRANSACTION is not supported with
        prepared statements
      */
      if (m_lex->create_info->m_transactional_ddl) {
        my_error(ER_UNSUPPORTED_PS, MYF(0));
        return true;
      }
#if defined(__has_cpp_attribute)
#if __has_cpp_attribute(fallthrough)
      [[fallthrough]];
#endif
#endif
    case SQLCOM_CREATE_EVENT:
    case SQLCOM_ALTER_EVENT:
    case SQLCOM_DROP_EVENT:
    case SQLCOM_CREATE_LIBRARY:
    case SQLCOM_DROP_LIBRARY:
    case SQLCOM_ALTER_LIBRARY:
    case SQLCOM_SELECT:
    case SQLCOM_DO:
    case SQLCOM_DELETE:
    case SQLCOM_DELETE_MULTI:
    case SQLCOM_UPDATE:
    case SQLCOM_UPDATE_MULTI:
    case SQLCOM_INSERT:
    case SQLCOM_INSERT_SELECT:
    case SQLCOM_REPLACE:
    case SQLCOM_REPLACE_SELECT:
    case SQLCOM_CALL:
    case SQLCOM_SHOW_BINLOG_EVENTS:
    case SQLCOM_SHOW_BINLOGS:
    case SQLCOM_SHOW_CHARSETS:
    case SQLCOM_SHOW_COLLATIONS:
    case SQLCOM_SHOW_CREATE_DB:
    case SQLCOM_SHOW_CREATE_EVENT:
    case SQLCOM_SHOW_CREATE_FUNC:
    case SQLCOM_SHOW_CREATE_PROC:
    case SQLCOM_SHOW_CREATE:
    case SQLCOM_SHOW_CREATE_LIBRARY:
    case SQLCOM_SHOW_CREATE_TRIGGER:
    case SQLCOM_SHOW_CREATE_USER:
    case SQLCOM_SHOW_DATABASES:
    case SQLCOM_SHOW_ENGINE_LOGS:
    case SQLCOM_SHOW_ENGINE_MUTEX:
    case SQLCOM_SHOW_ENGINE_STATUS:
    case SQLCOM_SHOW_ERRORS:
    case SQLCOM_SHOW_EVENTS:
    case SQLCOM_SHOW_FIELDS:
    case SQLCOM_SHOW_FUNC_CODE:
    case SQLCOM_SHOW_GRANTS:
    case SQLCOM_SHOW_KEYS:
    case SQLCOM_SHOW_BINLOG_STATUS:
    case SQLCOM_SHOW_OPEN_TABLES:
    case SQLCOM_SHOW_PLUGINS:
    case SQLCOM_SHOW_PRIVILEGES:
    case SQLCOM_SHOW_PROC_CODE:
    case SQLCOM_SHOW_PROCESSLIST:
    case SQLCOM_SHOW_PROFILE:
    case SQLCOM_SHOW_PROFILES:
    case SQLCOM_SHOW_RELAYLOG_EVENTS:
    case SQLCOM_SHOW_REPLICAS:
    case SQLCOM_SHOW_REPLICA_STATUS:
    case SQLCOM_SHOW_STATUS:
    case SQLCOM_SHOW_STATUS_PROC:
    case SQLCOM_SHOW_STATUS_FUNC:
    case SQLCOM_SHOW_STATUS_LIBRARY:
    case SQLCOM_SHOW_STORAGE_ENGINES:
    case SQLCOM_SHOW_TABLE_STATUS:
    case SQLCOM_SHOW_TABLES:
    case SQLCOM_SHOW_TRIGGERS:
    case SQLCOM_SHOW_VARIABLES:
    case SQLCOM_SET_RESOURCE_GROUP:
    case SQLCOM_SHOW_WARNS:
      res = m_lex->m_sql_cmd->prepare(thd);
      break;

    case SQLCOM_PREPARE:
    case SQLCOM_EXECUTE:
    case SQLCOM_DEALLOCATE_PREPARE:
    default:
      /*
        Trivial check of all status commands and diagnostic commands.
        This is easier than having things in the above case list,
        as it's less chance for mistakes.
      */
      if (!(sql_command_flags[sql_command] & CF_STATUS_COMMAND) ||
          (sql_command_flags[sql_command] & CF_DIAGNOSTIC_STMT)) {
        /* All other statements are not supported yet. */
        my_error(ER_UNSUPPORTED_PS, MYF(0));
        return true;
      }
      break;
  }
  if (res) return true;

  trace_parameter_types(thd);

  if (is_sql_prepare()) return false;

  mem_root_deque<Item *> *types = nullptr;
  Query_result *result = nullptr;
  uint no_columns = 0;

  if ((sql_command_flags[sql_command] & CF_HAS_RESULT_SET) &&
      !m_lex->is_explain()) {
    Query_expression *unit = m_lex->unit;
    result = unit->query_result();
    if (result == nullptr) result = unit->first_query_block()->query_result();
    if (result == nullptr) result = m_lex->result;
    types = unit->get_unit_column_types();
    no_columns = result->field_count(*types);
  }

  return send_statement(thd, this, no_columns, result, types);
}