diff options
author | Oleksandr Byelkin <sanja@mariadb.com> | 2019-06-14 07:36:47 +0200 |
---|---|---|
committer | Oleksandr Byelkin <sanja@mariadb.com> | 2019-06-14 07:36:47 +0200 |
commit | 4a3d51c76c131e7b5348d7c714a619f82de32d39 (patch) | |
tree | 4fb180861c733e364af930529565a7b799c4833a /sql | |
parent | d9fe615ef6862c85c5aada96d4f5b62b7093177c (diff) | |
parent | 50653e021f1678c3c28c6b5886fadb9fcf8d87ff (diff) | |
download | mariadb-git-4a3d51c76c131e7b5348d7c714a619f82de32d39.tar.gz |
Merge branch '10.2' into 10.3
Diffstat (limited to 'sql')
-rw-r--r-- | sql/handler.cc | 28 | ||||
-rw-r--r-- | sql/my_json_writer.cc | 7 | ||||
-rw-r--r-- | sql/my_json_writer.h | 1 | ||||
-rw-r--r-- | sql/rpl_mi.cc | 2 | ||||
-rw-r--r-- | sql/sql_alter.cc | 12 | ||||
-rw-r--r-- | sql/sql_alter.h | 5 | ||||
-rw-r--r-- | sql/sql_base.cc | 11 | ||||
-rw-r--r-- | sql/sql_base.h | 14 | ||||
-rw-r--r-- | sql/sql_cmd.h | 51 | ||||
-rw-r--r-- | sql/sql_explain.cc | 8 | ||||
-rw-r--r-- | sql/sql_join_cache.cc | 3 | ||||
-rw-r--r-- | sql/sql_lex.cc | 5 | ||||
-rw-r--r-- | sql/sql_parse.cc | 285 | ||||
-rw-r--r-- | sql/sql_select.cc | 110 | ||||
-rw-r--r-- | sql/sql_table.cc | 311 | ||||
-rw-r--r-- | sql/sql_update.cc | 227 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 67 | ||||
-rw-r--r-- | sql/sql_yacc_ora.yy | 67 | ||||
-rw-r--r-- | sql/table.cc | 23 |
19 files changed, 697 insertions, 540 deletions
diff --git a/sql/handler.cc b/sql/handler.cc index 993ba5947dc..37811e08ac8 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2016, Oracle and/or its affiliates. - Copyright (c) 2009, 2018, MariaDB Corporation. + Copyright (c) 2009, 2019, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -213,6 +213,32 @@ redo: } +bool +Storage_engine_name::resolve_storage_engine_with_error(THD *thd, + handlerton **ha, + bool tmp_table) +{ + if (plugin_ref plugin= ha_resolve_by_name(thd, &m_storage_engine_name, + tmp_table)) + { + *ha= plugin_hton(plugin); + return false; + } + + *ha= NULL; + if (thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION) + { + my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), m_storage_engine_name.str); + return true; + } + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_UNKNOWN_STORAGE_ENGINE, + ER_THD(thd, ER_UNKNOWN_STORAGE_ENGINE), + m_storage_engine_name.str); + return false; +} + + plugin_ref ha_lock_engine(THD *thd, const handlerton *hton) { if (hton) diff --git a/sql/my_json_writer.cc b/sql/my_json_writer.cc index c96fc9fc0e1..656023d4033 100644 --- a/sql/my_json_writer.cc +++ b/sql/my_json_writer.cc @@ -129,6 +129,13 @@ void Json_writer::add_ll(longlong val) add_unquoted_str(buf); } +void Json_writer::add_ull(ulonglong val) +{ + char buf[64]; + my_snprintf(buf, sizeof(buf), "%llu", val); + add_unquoted_str(buf); +} + /* Add a memory size, printing in Kb, Kb, Gb if necessary */ void Json_writer::add_size(longlong val) diff --git a/sql/my_json_writer.h b/sql/my_json_writer.h index da56396d7d9..4c7ca46ef7c 100644 --- a/sql/my_json_writer.h +++ b/sql/my_json_writer.h @@ -108,6 +108,7 @@ public: void add_str(const String &str); void add_ll(longlong val); + void add_ull(ulonglong val); void add_size(longlong val); void add_double(double val); void add_bool(bool val); diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index c11e96abe38..50c5c7b1969 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -1233,7 +1233,7 @@ bool Master_info_index::init_all_master_info() if (!err_num) // No Error on read Master_info { if (global_system_variables.log_warnings > 1) - sql_print_information("Reading of all Master_info entries succeded"); + sql_print_information("Reading of all Master_info entries succeeded"); DBUG_RETURN(0); } if (succ_num) // Have some Error and some Success diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc index 0de39e4970f..b3a036eda9e 100644 --- a/sql/sql_alter.cc +++ b/sql/sql_alter.cc @@ -359,6 +359,18 @@ bool Sql_cmd_alter_table::execute(THD *thd) SELECT_LEX *select_lex= &lex->select_lex; /* first table of first SELECT_LEX */ TABLE_LIST *first_table= (TABLE_LIST*) select_lex->table_list.first; + + 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())) + return true; // Engine not found, substitution is not allowed + if (!lex->create_info.db_type) // Not found, but substitution is allowed + lex->create_info.used_fields&= ~HA_CREATE_USED_ENGINE; + } + /* Code in mysql_alter_table() may modify its HA_CREATE_INFO argument, so we have to use a copy of this structure to make execution diff --git a/sql/sql_alter.h b/sql/sql_alter.h index a40c980b692..10aafe1ab37 100644 --- a/sql/sql_alter.h +++ b/sql/sql_alter.h @@ -355,7 +355,8 @@ protected: Sql_cmd_alter_table represents the generic ALTER TABLE statement. @todo move Alter_info and other ALTER specific structures from Lex here. */ -class Sql_cmd_alter_table : public Sql_cmd_common_alter_table +class Sql_cmd_alter_table : public Sql_cmd_common_alter_table, + public Storage_engine_name { public: /** @@ -367,6 +368,8 @@ public: ~Sql_cmd_alter_table() {} + Storage_engine_name *option_storage_engine_name() { return this; } + bool execute(THD *thd); }; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index b1fa78b5ec6..b7294fbc304 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -4045,8 +4045,7 @@ open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start, */ bool open_tables(THD *thd, const DDL_options_st &options, - TABLE_LIST **start, uint *counter, - Sroutine_hash_entry **sroutine_to_open_list, uint flags, + TABLE_LIST **start, uint *counter, uint flags, Prelocking_strategy *prelocking_strategy) { /* @@ -4089,9 +4088,10 @@ restart: has_prelocking_list= thd->lex->requires_prelocking(); table_to_open= start; - sroutine_to_open= sroutine_to_open_list; + sroutine_to_open= &thd->lex->sroutines_list.first; *counter= 0; THD_STAGE_INFO(thd, stage_opening_tables); + prelocking_strategy->reset(thd); /* If we are executing LOCK TABLES statement or a DDL statement @@ -4149,8 +4149,7 @@ restart: elements in prelocking list/set. */ while (*table_to_open || - (thd->locked_tables_mode <= LTM_LOCK_TABLES && - *sroutine_to_open)) + (thd->locked_tables_mode <= LTM_LOCK_TABLES && *sroutine_to_open)) { /* For every table in the list of tables to open, try to find or open @@ -4270,6 +4269,8 @@ restart: } } } + if ((error= prelocking_strategy->handle_end(thd))) + goto error; } /* diff --git a/sql/sql_base.h b/sql/sql_base.h index bd84cfe2336..c9fb9bc6a62 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -241,17 +241,7 @@ lock_table_names(THD *thd, TABLE_LIST *table_list, } bool open_tables(THD *thd, const DDL_options_st &options, TABLE_LIST **tables, uint *counter, - Sroutine_hash_entry **sroutine_to_open, uint flags, - Prelocking_strategy *prelocking_strategy); - -static inline bool -open_tables(THD *thd, const DDL_options_st &options, TABLE_LIST **tables, - uint *counter, uint flags, Prelocking_strategy *prelocking_strategy) -{ - return open_tables(thd, options, tables, counter, - &thd->lex->sroutines_list.first, flags, - prelocking_strategy); -} + uint flags, Prelocking_strategy *prelocking_strategy); static inline bool open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags, @@ -400,6 +390,7 @@ class Prelocking_strategy public: virtual ~Prelocking_strategy() { } + virtual void reset(THD *thd) { }; virtual bool handle_routine(THD *thd, Query_tables_list *prelocking_ctx, Sroutine_hash_entry *rt, sp_head *sp, bool *need_prelocking) = 0; @@ -407,6 +398,7 @@ public: TABLE_LIST *table_list, bool *need_prelocking) = 0; virtual bool handle_view(THD *thd, Query_tables_list *prelocking_ctx, TABLE_LIST *table_list, bool *need_prelocking)= 0; + virtual bool handle_end(THD *thd) { return 0; }; }; diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h index a9d8178d3f8..f5df73216d8 100644 --- a/sql/sql_cmd.h +++ b/sql/sql_cmd.h @@ -117,6 +117,32 @@ enum enum_sql_command { SQLCOM_END }; + +class Storage_engine_name +{ +protected: + LEX_CSTRING m_storage_engine_name; +public: + Storage_engine_name() + { + m_storage_engine_name.str= NULL; + m_storage_engine_name.length= 0; + } + Storage_engine_name(const LEX_CSTRING &name) + :m_storage_engine_name(name) + { } + Storage_engine_name(const LEX_STRING &name) + { + m_storage_engine_name.str= name.str; + m_storage_engine_name.length= name.length; + } + bool resolve_storage_engine_with_error(THD *thd, + handlerton **ha, + bool tmp_table); + bool is_set() { return m_storage_engine_name.str != NULL; } +}; + + /** @class Sql_cmd - Representation of an SQL command. @@ -160,6 +186,11 @@ public: */ virtual bool execute(THD *thd) = 0; + virtual Storage_engine_name *option_storage_engine_name() + { + return NULL; + } + protected: Sql_cmd() {} @@ -176,6 +207,26 @@ protected: } }; +class Sql_cmd_create_table_like: public Sql_cmd, + public Storage_engine_name +{ +public: + Storage_engine_name *option_storage_engine_name() { return this; } + bool execute(THD *thd); +}; + +class Sql_cmd_create_table: public Sql_cmd_create_table_like +{ +public: + enum_sql_command sql_command_code() const { return SQLCOM_CREATE_TABLE; } +}; + +class Sql_cmd_create_sequence: public Sql_cmd_create_table_like +{ +public: + enum_sql_command sql_command_code() const { return SQLCOM_CREATE_SEQUENCE; } +}; + /** Sql_cmd_call represents the CALL statement. diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc index a1c1156cd27..ca538752627 100644 --- a/sql/sql_explain.cc +++ b/sql/sql_explain.cc @@ -1282,7 +1282,7 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai if (rows_set) { item_list.push_back(new (mem_root) - Item_int(thd, (longlong) (ulonglong) rows, + Item_int(thd, (ulonglong) rows, MY_INT64_NUM_DECIMAL_DIGITS), mem_root); } @@ -1657,7 +1657,7 @@ void Explain_table_access::print_explain_json(Explain_query *query, /* `rows` */ if (rows_set) - writer->add_member("rows").add_ll(rows); + writer->add_member("rows").add_ull(rows); /* `r_rows` */ if (is_analyze) @@ -2295,7 +2295,7 @@ void Explain_update::print_explain_json(Explain_query *query, } /* `rows` */ - writer->add_member("rows").add_ll(rows); + writer->add_member("rows").add_ull(rows); if (mrr_type.length() != 0) @@ -2324,7 +2324,7 @@ void Explain_update::print_explain_json(Explain_query *query, r_rows= 0; r_filtered= buf_tracker.get_filtered_after_where() * 100.0; } - writer->add_member("r_rows").add_ll(r_rows); + writer->add_member("r_rows").add_ull(r_rows); writer->add_member("r_filtered").add_double(r_filtered); } else /* Not doing buffering */ diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index 8986e4c42ac..3b051259ad6 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -2147,7 +2147,8 @@ enum_nested_loop_state JOIN_CACHE::join_records(bool skip_last) } finish: - if (outer_join_first_inner) + if (outer_join_first_inner && + join_tab->first_inner == join_tab->first_unmatched) { /* All null complemented rows have been already generated for all diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index ef58dfb2b28..bd9725cbe0f 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -4745,7 +4745,10 @@ void SELECT_LEX::increase_derived_records(ha_rows records) break; default: // usual UNION - result->est_records+= records; + if (HA_ROWS_MAX - records > result->est_records) + result->est_records+= records; + else + result->est_records= HA_ROWS_MAX; break; } } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 4ca8abb64a4..01d13ac8789 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4064,289 +4064,6 @@ mysql_execute_command(THD *thd) res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_MUTEX); break; } - case SQLCOM_CREATE_SEQUENCE: - case SQLCOM_CREATE_TABLE: - { - 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; - - 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 (unlikely(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 - thd->work_part_info= 0; - { - 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.str, - create_table->table_name.str, "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 (unlikely(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 (unlikely((duplicate= unique_table(thd, lex->query_tables, - lex->query_tables->next_global, - CHECK_DUP_FOR_CREATE | - CHECK_DUP_SKIP_TEMP_TABLE)))) - { - 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 (unlikely((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 - { - if (create_info.vers_fix_system_fields(thd, &alter_info, *create_table) || - create_info.vers_check_system_fields(thd, &alter_info, *create_table)) - goto end_with_restore_list; - - /* - 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.str, create_table->table_name.str, - &create_info) && - (!thd->is_current_stmt_binlog_format_row() || - !create_info.tmp_table())) - { - WSREP_TO_ISOLATION_BEGIN(create_table->db.str, create_table->table_name.str, 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; - /* in case of create temp tables if @@session_track_state_change is - ON then send session state notification in OK packet */ - if(create_info.options & HA_LEX_CREATE_TMP_TABLE) - { - SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); - } - my_ok(thd); - } - } - -end_with_restore_list: - break; - } case SQLCOM_CREATE_INDEX: case SQLCOM_DROP_INDEX: /* @@ -6287,6 +6004,8 @@ end_with_restore_list: case SQLCOM_OPTIMIZE: case SQLCOM_REPAIR: case SQLCOM_TRUNCATE: + case SQLCOM_CREATE_TABLE: + case SQLCOM_CREATE_SEQUENCE: case SQLCOM_ALTER_TABLE: DBUG_ASSERT(first_table == all_tables && first_table != 0); /* fall through */ diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 1876f6067fa..426fcd6dbb4 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -214,7 +214,8 @@ static COND *make_cond_for_table_from_pred(THD *thd, Item *root_cond, table_map used_table, int join_tab_idx_arg, bool exclude_expensive_cond, - bool retain_ref_cond); + bool retain_ref_cond, + bool is_top_and_level); static Item* part_of_refkey(TABLE *form,Field *field); uint find_shortest_key(TABLE *table, const key_map *usable_keys); @@ -5078,7 +5079,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, DBUG_RETURN(TRUE); /* purecov: inspected */ { - ha_rows records= 1; + double records= 1; SELECT_LEX_UNIT *unit= join->select_lex->master_unit(); /* Find an optimal join order of the non-constant tables. */ @@ -5103,10 +5104,11 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, table/view. */ for (i= 0; i < join->table_count ; i++) - records*= join->best_positions[i].records_read ? - (ha_rows)join->best_positions[i].records_read : 1; - set_if_smaller(records, unit->select_limit_cnt); - join->select_lex->increase_derived_records(records); + if (double rr= join->best_positions[i].records_read) + records= COST_MULT(records, rr); + ha_rows rows= records > HA_ROWS_MAX ? HA_ROWS_MAX : (ha_rows) records; + set_if_smaller(rows, unit->select_limit_cnt); + join->select_lex->increase_derived_records(rows); } } @@ -8258,18 +8260,23 @@ double JOIN::get_examined_rows() { double examined_rows; double prev_fanout= 1; + double records; JOIN_TAB *tab= first_breadth_first_tab(); JOIN_TAB *prev_tab= tab; - examined_rows= (double)tab->get_examined_rows(); + records= (double)tab->get_examined_rows(); while ((tab= next_breadth_first_tab(first_breadth_first_tab(), top_join_tab_count, tab))) { - prev_fanout *= prev_tab->records_read; - examined_rows+= tab->get_examined_rows() * prev_fanout; + prev_fanout= COST_MULT(prev_fanout, prev_tab->records_read); + records= + COST_ADD(records, + COST_MULT((double) (tab->get_examined_rows()), prev_fanout)); prev_tab= tab; } + examined_rows= (double) + (records > (double) HA_ROWS_MAX ? HA_ROWS_MAX : (ha_rows) records); return examined_rows; } @@ -10582,12 +10589,6 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) RAND_TABLE_BIT; } - /* - Following force including random expression in last table condition. - It solve problem with select like SELECT * FROM t1 WHERE rand() > 0.5 - */ - if (tab == join->join_tab + last_top_base_tab_idx) - current_map|= RAND_TABLE_BIT; used_tables|=current_map; if (tab->type == JT_REF && tab->quick && @@ -10629,6 +10630,20 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) { tmp= make_cond_for_table(thd, cond, used_tables, current_map, i, FALSE, FALSE); + if (tab == join->join_tab + last_top_base_tab_idx) + { + /* + This pushes conjunctive conditions of WHERE condition such that: + - their used_tables() contain RAND_TABLE_BIT + - the conditions does not refer to any fields + (such like rand() > 0.5) + */ + table_map rand_table_bit= (table_map) RAND_TABLE_BIT; + COND *rand_cond= make_cond_for_table(thd, cond, used_tables, + rand_table_bit, -1, + FALSE, FALSE); + add_cond_and_fix(thd, &tmp, rand_cond); + } } /* Add conditions added by add_not_null_conds(). */ if (tab->select_cond) @@ -10964,6 +10979,21 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) current_map, /*(inner_tab - first_tab)*/ -1, FALSE, FALSE); + if (tab == last_tab) + { + /* + This pushes conjunctive conditions of ON expression of an outer + join such that: + - their used_tables() contain RAND_TABLE_BIT + - the conditions does not refer to any fields + (such like rand() > 0.5) + */ + table_map rand_table_bit= (table_map) RAND_TABLE_BIT; + COND *rand_cond= make_cond_for_table(thd, on_expr, used_tables2, + rand_table_bit, -1, + FALSE, FALSE); + add_cond_and_fix(thd, &tmp_cond, rand_cond); + } bool is_sjm_lookup_tab= FALSE; if (inner_tab->bush_children) { @@ -12589,6 +12619,8 @@ ha_rows JOIN_TAB::get_examined_rows() else examined_rows= records_read; + if (examined_rows >= (double) HA_ROWS_MAX) + return HA_ROWS_MAX; return (ha_rows) examined_rows; } @@ -21282,7 +21314,7 @@ make_cond_for_table(THD *thd, Item *cond, table_map tables, return make_cond_for_table_from_pred(thd, cond, cond, tables, used_table, join_tab_idx_arg, exclude_expensive_cond, - retain_ref_cond); + retain_ref_cond, true); } @@ -21292,9 +21324,12 @@ make_cond_for_table_from_pred(THD *thd, Item *root_cond, Item *cond, int join_tab_idx_arg, bool exclude_expensive_cond __attribute__ ((unused)), - bool retain_ref_cond) + bool retain_ref_cond, + bool is_top_and_level) { + table_map rand_table_bit= (table_map) RAND_TABLE_BIT; + if (used_table && !(cond->used_tables() & used_table)) return (COND*) 0; // Already checked @@ -21310,11 +21345,28 @@ make_cond_for_table_from_pred(THD *thd, Item *root_cond, Item *cond, Item *item; while ((item=li++)) { + /* + Special handling of top level conjuncts with RAND_TABLE_BIT: + if such a conjunct contains a reference to a field that is not + an outer field then it is pushed to the corresponding table by + the same rule as all other conjuncts. Otherwise, if the conjunct + is used in WHERE is is pushed to the last joined table, if is it + is used in ON condition of an outer join it is pushed into the + last inner table of the outer join. Such conjuncts are pushed in + a call of make_cond_for_table_from_pred() with the + parameter 'used_table' equal to PSEUDO_TABLE_BITS. + */ + if (is_top_and_level && used_table == rand_table_bit && + (item->used_tables() & ~OUTER_REF_TABLE_BIT) != rand_table_bit) + { + /* The conjunct with RAND_TABLE_BIT has been allready pushed */ + continue; + } Item *fix=make_cond_for_table_from_pred(thd, root_cond, item, tables, used_table, - join_tab_idx_arg, + join_tab_idx_arg, exclude_expensive_cond, - retain_ref_cond); + retain_ref_cond, false); if (fix) new_cond->argument_list()->push_back(fix, thd->mem_root); } @@ -21339,6 +21391,13 @@ make_cond_for_table_from_pred(THD *thd, Item *root_cond, Item *cond, } else { // Or list + if (is_top_and_level && used_table == rand_table_bit && + (cond->used_tables() & ~OUTER_REF_TABLE_BIT) != rand_table_bit) + { + /* This top level formula with RAND_TABLE_BIT has been already pushed */ + return (COND*) 0; + } + Item_cond_or *new_cond=new (thd->mem_root) Item_cond_or(thd); if (!new_cond) return (COND*) 0; // OOM /* purecov: inspected */ @@ -21350,7 +21409,7 @@ make_cond_for_table_from_pred(THD *thd, Item *root_cond, Item *cond, tables, 0L, join_tab_idx_arg, exclude_expensive_cond, - retain_ref_cond); + retain_ref_cond, false); if (!fix) return (COND*) 0; // Always true new_cond->argument_list()->push_back(fix, thd->mem_root); @@ -21367,6 +21426,13 @@ make_cond_for_table_from_pred(THD *thd, Item *root_cond, Item *cond, } } + if (is_top_and_level && used_table == rand_table_bit && + (cond->used_tables() & ~OUTER_REF_TABLE_BIT) != rand_table_bit) + { + /* This top level formula with RAND_TABLE_BIT has been already pushed */ + return (COND*) 0; + } + /* Because the following test takes a while and it can be done table_count times, we mark each item that we have examined with the result @@ -25377,10 +25443,10 @@ bool JOIN_TAB::save_explain_data(Explain_table_access *eta, } else { - double examined_rows= (double)get_examined_rows(); + ha_rows examined_rows= get_examined_rows(); eta->rows_set= true; - eta->rows= (ha_rows) examined_rows; + eta->rows= examined_rows; /* "filtered" */ float f= 0.0; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index ad86cdf8514..9da889852b2 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -10830,3 +10830,314 @@ bool check_engine(THD *thd, const char *db_name, DBUG_RETURN(false); } + + +bool Sql_cmd_create_table_like::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.str); + } + } + + 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 (unlikely(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 + thd->work_part_info= 0; + { + 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.str, + create_table->table_name.str, "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 (unlikely(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()) + { + if (TABLE_LIST *duplicate= unique_table(thd, lex->query_tables, + lex->query_tables->next_global, + CHECK_DUP_FOR_CREATE | + CHECK_DUP_SKIP_TEMP_TABLE)) + { + 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 + { + if (create_info.vers_fix_system_fields(thd, &alter_info, *create_table) || + create_info.vers_check_system_fields(thd, &alter_info, *create_table)) + goto end_with_restore_list; + + /* + 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.str, create_table->table_name.str, + &create_info) && + (!thd->is_current_stmt_binlog_format_row() || + !create_info.tmp_table())) + { + WSREP_TO_ISOLATION_BEGIN(create_table->db.str, create_table->table_name.str, 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; + /* in case of create temp tables if @@session_track_state_change is + ON then send session state notification in OK packet */ + if (create_info.options & HA_LEX_CREATE_TMP_TABLE) + { + SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); + } + my_ok(thd); + } + } + +end_with_restore_list: + DBUG_RETURN(res); + +WSREP_ERROR_LABEL: + DBUG_RETURN(true); +} diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 681e3bb0b64..e846424ef13 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1516,108 +1516,81 @@ static bool multi_update_check_table_access(THD *thd, TABLE_LIST *table, } -/* - make update specific preparation and checks after opening tables +class Multiupdate_prelocking_strategy : public DML_prelocking_strategy +{ + bool done; + bool has_prelocking_list; +public: + void reset(THD *thd); + bool handle_end(THD *thd); +}; + +void Multiupdate_prelocking_strategy::reset(THD *thd) +{ + done= false; + has_prelocking_list= thd->lex->requires_prelocking(); +} - SYNOPSIS - mysql_multi_update_prepare() - thd thread handler +/** + Determine what tables could be updated in the multi-update - RETURN - FALSE OK - TRUE Error + For these tables we'll need to open triggers and continue prelocking + until all is open. */ - -int mysql_multi_update_prepare(THD *thd) +bool Multiupdate_prelocking_strategy::handle_end(THD *thd) { - LEX *lex= thd->lex; - TABLE_LIST *table_list= lex->query_tables; - TABLE_LIST *tl; - List<Item> *fields= &lex->select_lex.item_list; - table_map tables_for_update; - bool update_view= 0; - DML_prelocking_strategy prelocking_strategy; - bool has_prelocking_list= thd->lex->requires_prelocking(); + DBUG_ENTER("Multiupdate_prelocking_strategy::handle_end"); + if (done) + DBUG_RETURN(0); - /* - if this multi-update was converted from usual update, here is table - counter else junk will be assigned here, but then replaced with real - count in open_tables() - */ - uint table_count= lex->table_count; - const bool using_lock_tables= thd->locked_tables_mode != LTM_NONE; - bool original_multiupdate= (thd->lex->sql_command == SQLCOM_UPDATE_MULTI); - DBUG_ENTER("mysql_multi_update_prepare"); + LEX *lex= thd->lex; + SELECT_LEX *select_lex= &lex->select_lex; + TABLE_LIST *table_list= lex->query_tables, *tl; - /* following need for prepared statements, to run next time multi-update */ - thd->lex->sql_command= SQLCOM_UPDATE_MULTI; + done= true; - /* - Open tables and create derived ones, but do not lock and fill them yet. + if (mysql_handle_derived(lex, DT_INIT) || + mysql_handle_derived(lex, DT_MERGE_FOR_INSERT) || + mysql_handle_derived(lex, DT_PREPARE)) + DBUG_RETURN(1); - During prepare phase acquire only S metadata locks instead of SW locks to - keep prepare of multi-UPDATE compatible with concurrent LOCK TABLES WRITE - and global read lock. - */ - if ((original_multiupdate && open_tables(thd, &table_list, &table_count, - thd->stmt_arena->is_stmt_prepare() - ? MYSQL_OPEN_FORCE_SHARED_MDL : 0, - &prelocking_strategy)) || - mysql_handle_derived(lex, DT_INIT)) - DBUG_RETURN(TRUE); /* setup_tables() need for VIEWs. JOIN::prepare() will call setup_tables() second time, but this call will do nothing (there are check for second call in setup_tables()). */ - //We need to merge for insert prior to prepare. - if (mysql_handle_derived(lex, DT_MERGE_FOR_INSERT)) - DBUG_RETURN(TRUE); - - if (mysql_handle_derived(lex, DT_PREPARE)) - DBUG_RETURN(TRUE); - - if (setup_tables_and_check_access(thd, &lex->select_lex.context, - &lex->select_lex.top_join_list, - table_list, - lex->select_lex.leaf_tables, FALSE, - UPDATE_ACL, SELECT_ACL, FALSE)) - DBUG_RETURN(TRUE); + if (setup_tables_and_check_access(thd, &select_lex->context, + &select_lex->top_join_list, table_list, select_lex->leaf_tables, + FALSE, UPDATE_ACL, SELECT_ACL, FALSE)) + DBUG_RETURN(1); - if (lex->select_lex.handle_derived(thd->lex, DT_MERGE)) - DBUG_RETURN(TRUE); + if (select_lex->handle_derived(thd->lex, DT_MERGE)) + DBUG_RETURN(1); + List<Item> *fields= &lex->select_lex.item_list; if (setup_fields_with_no_wrap(thd, Ref_ptr_array(), *fields, MARK_COLUMNS_WRITE, 0, 0)) - DBUG_RETURN(TRUE); + DBUG_RETURN(1); + // Check if we have a view in the list ... for (tl= table_list; tl ; tl= tl->next_local) - { if (tl->view) - { - update_view= 1; break; - } - } - - if (check_fields(thd, *fields, update_view)) - { - DBUG_RETURN(TRUE); - } - - thd->table_map_for_update= tables_for_update= get_table_map(fields); + // ... and pass this knowlage in check_fields call + if (check_fields(thd, *fields, tl != NULL )) + DBUG_RETURN(1); - if (unsafe_key_update(lex->select_lex.leaf_tables, tables_for_update)) - DBUG_RETURN(true); + table_map tables_for_update= thd->table_map_for_update= get_table_map(fields); - TABLE_LIST **new_tables= lex->query_tables_last; - DBUG_ASSERT(*new_tables== NULL); + if (unsafe_key_update(select_lex->leaf_tables, tables_for_update)) + DBUG_RETURN(1); /* Setup timestamp handling and locking mode */ - List_iterator<TABLE_LIST> ti(lex->select_lex.leaf_tables); + List_iterator<TABLE_LIST> ti(select_lex->leaf_tables); + const bool using_lock_tables= thd->locked_tables_mode != LTM_NONE; while ((tl= ti++)) { TABLE *table= tl->table; @@ -1632,7 +1605,7 @@ int mysql_multi_update_prepare(THD *thd) { my_error(ER_NON_UPDATABLE_TABLE, MYF(0), tl->top_table()->alias.str, "UPDATE"); - DBUG_RETURN(TRUE); + DBUG_RETURN(1); } DBUG_PRINT("info",("setting table `%s` for update", @@ -1644,8 +1617,8 @@ int mysql_multi_update_prepare(THD *thd) tl->updating= 1; if (tl->belong_to_view) tl->belong_to_view->updating= 1; - if (extend_table_list(thd, tl, &prelocking_strategy, has_prelocking_list)) - DBUG_RETURN(TRUE); + if (extend_table_list(thd, tl, this, has_prelocking_list)) + DBUG_RETURN(1); } else { @@ -1677,19 +1650,6 @@ int mysql_multi_update_prepare(THD *thd) through all leaf tables but also through all view hierarchy. */ - uint addon_table_count= 0; - if (*new_tables) - { - Sroutine_hash_entry **new_routines= thd->lex->sroutines_list.next; - DBUG_ASSERT(*new_routines == NULL); - if (open_tables(thd, thd->lex->create_info, new_tables, - &addon_table_count, new_routines, - thd->stmt_arena->is_stmt_prepare() - ? MYSQL_OPEN_FORCE_SHARED_MDL : 0, - &prelocking_strategy)) - DBUG_RETURN(TRUE); - } - for (tl= table_list; tl; tl= tl->next_local) { bool not_used= false; @@ -1702,23 +1662,67 @@ int mysql_multi_update_prepare(THD *thd) /* check single table update for view compound from several tables */ for (tl= table_list; tl; tl= tl->next_local) { + TABLE_LIST *for_update= 0; if (tl->is_jtbm()) continue; - if (tl->is_merged_derived()) + if (tl->is_merged_derived() && + tl->check_single_table(&for_update, tables_for_update, tl)) { - TABLE_LIST *for_update= 0; - if (tl->check_single_table(&for_update, tables_for_update, tl)) - { - my_error(ER_VIEW_MULTIUPDATE, MYF(0), - tl->view_db.str, tl->view_name.str); - DBUG_RETURN(-1); - } + my_error(ER_VIEW_MULTIUPDATE, MYF(0), tl->view_db.str, tl->view_name.str); + DBUG_RETURN(1); } } + DBUG_RETURN(0); +} + +/* + make update specific preparation and checks after opening tables + + SYNOPSIS + mysql_multi_update_prepare() + thd thread handler + + RETURN + FALSE OK + TRUE Error +*/ + +int mysql_multi_update_prepare(THD *thd) +{ + LEX *lex= thd->lex; + TABLE_LIST *table_list= lex->query_tables; + TABLE_LIST *tl; + Multiupdate_prelocking_strategy prelocking_strategy; + uint table_count= lex->table_count; + DBUG_ENTER("mysql_multi_update_prepare"); + + /* + Open tables and create derived ones, but do not lock and fill them yet. + + During prepare phase acquire only S metadata locks instead of SW locks to + keep prepare of multi-UPDATE compatible with concurrent LOCK TABLES WRITE + and global read lock. + */ + if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI) + { + if (open_tables(thd, &table_list, &table_count, + thd->stmt_arena->is_stmt_prepare() ? MYSQL_OPEN_FORCE_SHARED_MDL : 0, + &prelocking_strategy)) + DBUG_RETURN(TRUE); + } + else + { + /* following need for prepared statements, to run next time multi-update */ + thd->lex->sql_command= SQLCOM_UPDATE_MULTI; + prelocking_strategy.reset(thd); + if (prelocking_strategy.handle_end(thd)) + DBUG_RETURN(TRUE); + } + /* now lock and fill tables */ if (!thd->stmt_arena->is_stmt_prepare() && - lock_tables(thd, table_list, table_count + addon_table_count, 0)) + lock_tables(thd, table_list, table_count, 0)) { DBUG_RETURN(TRUE); } @@ -1731,7 +1735,7 @@ int mysql_multi_update_prepare(THD *thd) */ lex->select_lex.exclude_from_table_unique_test= TRUE; /* We only need SELECT privilege for columns in the values list */ - ti.rewind(); + List_iterator<TABLE_LIST> ti(lex->select_lex.leaf_tables); while ((tl= ti++)) { if (tl->is_jtbm()) @@ -1764,25 +1768,18 @@ int mysql_multi_update_prepare(THD *thd) Setup multi-update handling and call SELECT to do the join */ -bool mysql_multi_update(THD *thd, - TABLE_LIST *table_list, - List<Item> *fields, - List<Item> *values, - COND *conds, - ulonglong options, +bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, List<Item> *fields, + List<Item> *values, COND *conds, ulonglong options, enum enum_duplicates handle_duplicates, - bool ignore, - SELECT_LEX_UNIT *unit, - SELECT_LEX *select_lex, - multi_update **result) + bool ignore, SELECT_LEX_UNIT *unit, + SELECT_LEX *select_lex, multi_update **result) { bool res; DBUG_ENTER("mysql_multi_update"); - + if (!(*result= new (thd->mem_root) multi_update(thd, table_list, &thd->lex->select_lex.leaf_tables, - fields, values, - handle_duplicates, ignore))) + fields, values, handle_duplicates, ignore))) { DBUG_RETURN(TRUE); } @@ -1792,8 +1789,8 @@ bool mysql_multi_update(THD *thd, res= mysql_select(thd, table_list, select_lex->with_wild, total_list, conds, - select_lex->order_list.elements, select_lex->order_list.first, - (ORDER *)NULL, (Item *) NULL, (ORDER *)NULL, + select_lex->order_list.elements, + select_lex->order_list.first, NULL, NULL, NULL, options | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | OPTION_SETUP_TABLES_DONE, *result, unit, select_lex); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index a333bd79dd3..814017f0231 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1,6 +1,6 @@ /* Copyright (c) 2000, 2015, Oracle and/or its affiliates. - Copyright (c) 2010, 2016, MariaDB + Copyright (c) 2010, 2019, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -2693,6 +2693,8 @@ create: create_or_replace opt_temporary TABLE_SYM opt_if_not_exists table_ident { LEX *lex= thd->lex; + if (!(lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_create_table())) + MYSQL_YYABORT; lex->create_info.init(); if (unlikely(lex->set_command_with_check(SQLCOM_CREATE_TABLE, $2, $1 | $4))) @@ -2716,21 +2718,13 @@ create: { LEX *lex= thd->lex; lex->current_select= &lex->select_lex; - if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) && - !lex->create_info.db_type) - { - 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, - $5->table.str); - } create_table_set_open_action_and_adjust_tables(lex); } | create_or_replace opt_temporary SEQUENCE_SYM opt_if_not_exists table_ident { LEX *lex= thd->lex; + if (!(lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_create_sequence())) + MYSQL_YYABORT; lex->create_info.init(); if (unlikely(lex->set_command_with_check(SQLCOM_CREATE_SEQUENCE, $2, $1 | $4))) @@ -2777,17 +2771,6 @@ create: Lex->create_info.sequence= 1; lex->current_select= &lex->select_lex; - if (unlikely((lex->create_info.used_fields & - HA_CREATE_USED_ENGINE) && - !lex->create_info.db_type)) - { - 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, - $5->table.str); - } create_table_set_open_action_and_adjust_tables(lex); } | create_or_replace opt_unique INDEX_SYM opt_if_not_exists ident @@ -6222,10 +6205,20 @@ create_table_options: ; create_table_option: - ENGINE_SYM opt_equal storage_engines + ENGINE_SYM opt_equal ident_or_text { - Lex->create_info.db_type= $3; - Lex->create_info.used_fields|= HA_CREATE_USED_ENGINE; + LEX *lex= Lex; + if (!lex->m_sql_cmd) + { + DBUG_ASSERT(lex->sql_command == SQLCOM_ALTER_TABLE); + if (!(lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_alter_table())) + MYSQL_YYABORT; + } + Storage_engine_name *opt= + lex->m_sql_cmd->option_storage_engine_name(); + DBUG_ASSERT(opt); // Expect a proper Sql_cmd + *opt= Storage_engine_name($3); + lex->create_info.used_fields|= HA_CREATE_USED_ENGINE; } | MAX_ROWS opt_equal ulonglong_num { @@ -6520,21 +6513,10 @@ default_collation: storage_engines: ident_or_text { - plugin_ref plugin= ha_resolve_by_name(thd, &$1, - thd->lex->create_info.tmp_table()); - - if (likely(plugin)) - $$= plugin_hton(plugin); - else - { - if (thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION) - my_yyabort_error((ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str)); - $$= 0; - push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, - ER_UNKNOWN_STORAGE_ENGINE, - ER_THD(thd, ER_UNKNOWN_STORAGE_ENGINE), - $1.str); - } + if (Storage_engine_name($1). + resolve_storage_engine_with_error(thd, &$$, + thd->lex->create_info.tmp_table())) + MYSQL_YYABORT; } ; @@ -8454,11 +8436,6 @@ alter_list_item: { LEX *lex=Lex; lex->alter_info.flags|= ALTER_OPTIONS; - if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) && - !lex->create_info.db_type) - { - lex->create_info.used_fields&= ~HA_CREATE_USED_ENGINE; - } } | FORCE_SYM { diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 7d561e7c34a..fcf0dc01562 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -1,6 +1,6 @@ /* Copyright (c) 2000, 2015, Oracle and/or its affiliates. - Copyright (c) 2010, 2016, MariaDB + Copyright (c) 2010, 2019, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -2115,6 +2115,8 @@ create: create_or_replace opt_temporary TABLE_SYM opt_if_not_exists table_ident { LEX *lex= thd->lex; + if (!(lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_create_table())) + MYSQL_YYABORT; lex->create_info.init(); if (unlikely(lex->set_command_with_check(SQLCOM_CREATE_TABLE, $2, $1 | $4))) @@ -2138,21 +2140,13 @@ create: { LEX *lex= thd->lex; lex->current_select= &lex->select_lex; - if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) && - !lex->create_info.db_type) - { - 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, - $5->table.str); - } create_table_set_open_action_and_adjust_tables(lex); } | create_or_replace opt_temporary SEQUENCE_SYM opt_if_not_exists table_ident { LEX *lex= thd->lex; + if (!(lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_create_sequence())) + MYSQL_YYABORT; lex->create_info.init(); if (unlikely(lex->set_command_with_check(SQLCOM_CREATE_SEQUENCE, $2, $1 | $4))) @@ -2199,17 +2193,6 @@ create: Lex->create_info.sequence= 1; lex->current_select= &lex->select_lex; - if (unlikely((lex->create_info.used_fields & - HA_CREATE_USED_ENGINE) && - !lex->create_info.db_type)) - { - 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, - $5->table.str); - } create_table_set_open_action_and_adjust_tables(lex); } | create_or_replace opt_unique INDEX_SYM opt_if_not_exists ident @@ -6068,10 +6051,20 @@ create_table_options: ; create_table_option: - ENGINE_SYM opt_equal storage_engines + ENGINE_SYM opt_equal ident_or_text { - Lex->create_info.db_type= $3; - Lex->create_info.used_fields|= HA_CREATE_USED_ENGINE; + LEX *lex= Lex; + if (!lex->m_sql_cmd) + { + DBUG_ASSERT(lex->sql_command == SQLCOM_ALTER_TABLE); + if (!(lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_alter_table())) + MYSQL_YYABORT; + } + Storage_engine_name *opt= + lex->m_sql_cmd->option_storage_engine_name(); + DBUG_ASSERT(opt); // Expect a proper Sql_cmd + *opt= Storage_engine_name($3); + lex->create_info.used_fields|= HA_CREATE_USED_ENGINE; } | MAX_ROWS opt_equal ulonglong_num { @@ -6366,21 +6359,10 @@ default_collation: storage_engines: ident_or_text { - plugin_ref plugin= ha_resolve_by_name(thd, &$1, - thd->lex->create_info.tmp_table()); - - if (likely(plugin)) - $$= plugin_hton(plugin); - else - { - if (thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION) - my_yyabort_error((ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str)); - $$= 0; - push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, - ER_UNKNOWN_STORAGE_ENGINE, - ER_THD(thd, ER_UNKNOWN_STORAGE_ENGINE), - $1.str); - } + if (Storage_engine_name($1). + resolve_storage_engine_with_error(thd, &$$, + thd->lex->create_info.tmp_table())) + MYSQL_YYABORT; } ; @@ -8391,11 +8373,6 @@ alter_list_item: { LEX *lex=Lex; lex->alter_info.flags|= ALTER_OPTIONS; - if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) && - !lex->create_info.db_type) - { - lex->create_info.used_fields&= ~HA_CREATE_USED_ENGINE; - } } | FORCE_SYM { diff --git a/sql/table.cc b/sql/table.cc index 4805017972c..8f60d3ff1d5 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2017, Oracle and/or its affiliates. - Copyright (c) 2008, 2018, MariaDB + Copyright (c) 2008, 2019, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -1386,8 +1386,9 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, } if (!share->table_charset) { + const CHARSET_INFO *cs= thd->variables.collation_database; /* unknown charset in frm_image[38] or pre-3.23 frm */ - if (use_mb(default_charset_info)) + if (use_mb(cs)) { /* Warn that we may be changing the size of character columns */ sql_print_warning("'%s' had no or invalid character set, " @@ -1395,7 +1396,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, "so character column sizes may have changed", share->path.str); } - share->table_charset= default_charset_info; + share->table_charset= cs; } share->db_record_offset= 1; @@ -2720,8 +2721,20 @@ static bool sql_unusable_for_discovery(THD *thd, handlerton *engine, if (create_info->data_file_name || create_info->index_file_name) return 1; // ... engine - if (create_info->db_type && create_info->db_type != engine) - return 1; + DBUG_ASSERT(lex->m_sql_cmd); + if (lex->create_info.used_fields & HA_CREATE_USED_ENGINE) + { + /* + TODO: we could just compare engine names here, without resolving. + But this optimization is too late for 10.1. + */ + Storage_engine_name *opt= lex->m_sql_cmd->option_storage_engine_name(); + DBUG_ASSERT(opt); // lex->m_sql_cmd must be an Sql_cmd_create_table instance + if (opt->resolve_storage_engine_with_error(thd, &create_info->db_type, + false) || + (create_info->db_type && create_info->db_type != engine)) + return 1; + } // ... WITH SYSTEM VERSIONING if (create_info->versioned()) return 1; |