1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-27 18:02:13 +03:00

MDEV-19653 Add class Sql_cmd_create_table

This commit is contained in:
Alexander Barkov
2019-05-31 15:24:40 +04:00
parent dd939d6f7e
commit 5a19908b95
8 changed files with 425 additions and 304 deletions

View File

@ -10116,3 +10116,303 @@ bool check_engine(THD *thd, const char *db_name,
DBUG_RETURN(false);
}
bool Sql_cmd_create_table::execute(THD *thd)
{
DBUG_ENTER("Sql_cmd_create_table::execute");
LEX *lex= thd->lex;
TABLE_LIST *all_tables= lex->query_tables;
SELECT_LEX *select_lex= &lex->select_lex;
TABLE_LIST *first_table= select_lex->table_list.first;
DBUG_ASSERT(first_table == all_tables && first_table != 0);
bool link_to_local;
TABLE_LIST *create_table= first_table;
TABLE_LIST *select_tables= lex->create_last_non_select_table->next_global;
/* most outer SELECT_LEX_UNIT of query */
SELECT_LEX_UNIT *unit= &lex->unit;
int res= 0;
const bool used_engine= lex->create_info.used_fields & HA_CREATE_USED_ENGINE;
DBUG_ASSERT((m_storage_engine_name.str != NULL) == used_engine);
if (used_engine)
{
if (resolve_storage_engine_with_error(thd, &lex->create_info.db_type,
lex->create_info.tmp_table()))
DBUG_RETURN(true); // Engine not found, substitution is not allowed
if (!lex->create_info.db_type) // Not found, but substitution is allowed
{
lex->create_info.use_default_db_type(thd);
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_USING_OTHER_HANDLER,
ER_THD(thd, ER_WARN_USING_OTHER_HANDLER),
hton_name(lex->create_info.db_type)->str,
create_table->table_name);
}
}
if (lex->tmp_table())
{
status_var_decrement(thd->status_var.com_stat[SQLCOM_CREATE_TABLE]);
status_var_increment(thd->status_var.com_create_tmp_table);
}
/*
Code below (especially in mysql_create_table() and select_create
methods) may modify HA_CREATE_INFO structure in LEX, so we have to
use a copy of this structure to make execution prepared statement-
safe. A shallow copy is enough as this code won't modify any memory
referenced from this structure.
*/
Table_specification_st create_info(lex->create_info);
/*
We need to copy alter_info for the same reasons of re-execution
safety, only in case of Alter_info we have to do (almost) a deep
copy.
*/
Alter_info alter_info(lex->alter_info, thd->mem_root);
if (thd->is_fatal_error)
{
/* If out of memory when creating a copy of alter_info. */
res= 1;
goto end_with_restore_list;
}
/* Check privileges */
if ((res= create_table_precheck(thd, select_tables, create_table)))
goto end_with_restore_list;
/* Might have been updated in create_table_precheck */
create_info.alias= create_table->alias;
/* Fix names if symlinked or relocated tables */
if (append_file_to_dir(thd, &create_info.data_file_name,
create_table->table_name) ||
append_file_to_dir(thd, &create_info.index_file_name,
create_table->table_name))
goto end_with_restore_list;
/*
If no engine type was given, work out the default now
rather than at parse-time.
*/
if (!(create_info.used_fields & HA_CREATE_USED_ENGINE))
create_info.use_default_db_type(thd);
/*
If we are using SET CHARSET without DEFAULT, add an implicit
DEFAULT to not confuse old users. (This may change).
*/
if ((create_info.used_fields &
(HA_CREATE_USED_DEFAULT_CHARSET | HA_CREATE_USED_CHARSET)) ==
HA_CREATE_USED_CHARSET)
{
create_info.used_fields&= ~HA_CREATE_USED_CHARSET;
create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET;
create_info.default_table_charset= create_info.table_charset;
create_info.table_charset= 0;
}
/*
If we are a slave, we should add OR REPLACE if we don't have
IF EXISTS. This will help a slave to recover from
CREATE TABLE OR EXISTS failures by dropping the table and
retrying the create.
*/
if (thd->slave_thread &&
slave_ddl_exec_mode_options == SLAVE_EXEC_MODE_IDEMPOTENT &&
!lex->create_info.if_not_exists())
{
create_info.add(DDL_options_st::OPT_OR_REPLACE);
create_info.add(DDL_options_st::OPT_OR_REPLACE_SLAVE_GENERATED);
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
{
partition_info *part_info= thd->lex->part_info;
if (part_info && !(part_info= part_info->get_clone(thd)))
{
res= -1;
goto end_with_restore_list;
}
thd->work_part_info= part_info;
}
#endif
if (select_lex->item_list.elements) // With select
{
select_result *result;
/*
CREATE TABLE...IGNORE/REPLACE SELECT... can be unsafe, unless
ORDER BY PRIMARY KEY clause is used in SELECT statement. We therefore
use row based logging if mixed or row based logging is available.
TODO: Check if the order of the output of the select statement is
deterministic. Waiting for BUG#42415
*/
if(lex->ignore)
lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_CREATE_IGNORE_SELECT);
if(lex->duplicates == DUP_REPLACE)
lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_CREATE_REPLACE_SELECT);
/*
If:
a) we inside an SP and there was NAME_CONST substitution,
b) binlogging is on (STMT mode),
c) we log the SP as separate statements
raise a warning, as it may cause problems
(see 'NAME_CONST issues' in 'Binary Logging of Stored Programs')
*/
if (thd->query_name_consts && mysql_bin_log.is_open() &&
thd->wsrep_binlog_format() == BINLOG_FORMAT_STMT &&
!mysql_bin_log.is_query_in_union(thd, thd->query_id))
{
List_iterator_fast<Item> it(select_lex->item_list);
Item *item;
uint splocal_refs= 0;
/* Count SP local vars in the top-level SELECT list */
while ((item= it++))
{
if (item->get_item_splocal())
splocal_refs++;
}
/*
If it differs from number of NAME_CONST substitution applied,
we may have a SOME_FUNC(NAME_CONST()) in the SELECT list,
that may cause a problem with binary log (see BUG#35383),
raise a warning.
*/
if (splocal_refs != thd->query_name_consts)
push_warning(thd,
Sql_condition::WARN_LEVEL_WARN,
ER_UNKNOWN_ERROR,
"Invoked routine ran a statement that may cause problems with "
"binary log, see 'NAME_CONST issues' in 'Binary Logging of Stored Programs' "
"section of the manual.");
}
select_lex->options|= SELECT_NO_UNLOCK;
unit->set_limit(select_lex);
/*
Disable non-empty MERGE tables with CREATE...SELECT. Too
complicated. See Bug #26379. Empty MERGE tables are read-only
and don't allow CREATE...SELECT anyway.
*/
if (create_info.used_fields & HA_CREATE_USED_UNION)
{
my_error(ER_WRONG_OBJECT, MYF(0), create_table->db,
create_table->table_name, "BASE TABLE");
res= 1;
goto end_with_restore_list;
}
/* Copy temporarily the statement flags to thd for lock_table_names() */
uint save_thd_create_info_options= thd->lex->create_info.options;
thd->lex->create_info.options|= create_info.options;
res= open_and_lock_tables(thd, create_info, lex->query_tables, TRUE, 0);
thd->lex->create_info.options= save_thd_create_info_options;
if (res)
{
/* Got error or warning. Set res to 1 if error */
if (!(res= thd->is_error()))
my_ok(thd); // CREATE ... IF NOT EXISTS
goto end_with_restore_list;
}
/* Ensure we don't try to create something from which we select from */
if (create_info.or_replace() && !create_info.tmp_table())
{
TABLE_LIST *duplicate;
if ((duplicate= unique_table(thd, lex->query_tables,
lex->query_tables->next_global,
CHECK_DUP_FOR_CREATE)))
{
update_non_unique_table_error(lex->query_tables, "CREATE",
duplicate);
res= TRUE;
goto end_with_restore_list;
}
}
{
/*
Remove target table from main select and name resolution
context. This can't be done earlier as it will break view merging in
statements like "CREATE TABLE IF NOT EXISTS existing_view SELECT".
*/
lex->unlink_first_table(&link_to_local);
/* Store reference to table in case of LOCK TABLES */
create_info.table= create_table->table;
/*
select_create is currently not re-execution friendly and
needs to be created for every execution of a PS/SP.
Note: In wsrep-patch, CTAS is handled like a regular transaction.
*/
if ((result= new (thd->mem_root) select_create(thd, create_table,
&create_info,
&alter_info,
select_lex->item_list,
lex->duplicates,
lex->ignore,
select_tables)))
{
/*
CREATE from SELECT give its SELECT_LEX for SELECT,
and item_list belong to SELECT
*/
if (!(res= handle_select(thd, lex, result, 0)))
{
if (create_info.tmp_table())
thd->variables.option_bits|= OPTION_KEEP_LOG;
}
delete result;
}
lex->link_first_table_back(create_table, link_to_local);
}
}
else
{
/* regular create */
if (create_info.like())
{
/* CREATE TABLE ... LIKE ... */
res= mysql_create_like_table(thd, create_table, select_tables,
&create_info);
}
else
{
/*
In STATEMENT format, we probably have to replicate also temporary
tables, like mysql replication does. Also check if the requested
engine is allowed/supported.
*/
if (WSREP(thd) &&
!check_engine(thd, create_table->db, create_table->table_name,
&create_info) &&
(!thd->is_current_stmt_binlog_format_row() ||
!create_info.tmp_table()))
{
WSREP_TO_ISOLATION_BEGIN(create_table->db, create_table->table_name, NULL)
}
/* Regular CREATE TABLE */
res= mysql_create_table(thd, create_table, &create_info, &alter_info);
}
if (!res)
{
/* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
if (create_info.tmp_table())
thd->variables.option_bits|= OPTION_KEEP_LOG;
my_ok(thd);
}
}
end_with_restore_list:
DBUG_RETURN(res);
WSREP_ERROR_LABEL:
DBUG_RETURN(true);
}