diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/contributors.h | 1 | ||||
-rw-r--r-- | sql/field.cc | 2 | ||||
-rw-r--r-- | sql/handler.cc | 99 | ||||
-rw-r--r-- | sql/item.cc | 54 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 3 | ||||
-rw-r--r-- | sql/item_strfunc.cc | 30 | ||||
-rw-r--r-- | sql/item_strfunc.h | 129 | ||||
-rw-r--r-- | sql/item_sum.h | 1 | ||||
-rw-r--r-- | sql/log.cc | 2 | ||||
-rw-r--r-- | sql/log_event.cc | 1 | ||||
-rw-r--r-- | sql/mysqld.cc | 32 | ||||
-rw-r--r-- | sql/net_serv.cc | 4 | ||||
-rw-r--r-- | sql/parse_file.h | 6 | ||||
-rw-r--r-- | sql/signal_handler.cc | 4 | ||||
-rw-r--r-- | sql/sql_acl.cc | 8 | ||||
-rw-r--r-- | sql/sql_admin.cc | 14 | ||||
-rw-r--r-- | sql/sql_base.cc | 1 | ||||
-rw-r--r-- | sql/sql_cache.cc | 3 | ||||
-rw-r--r-- | sql/sql_cache.h | 9 | ||||
-rw-r--r-- | sql/sql_class.cc | 2 | ||||
-rw-r--r-- | sql/sql_class.h | 8 | ||||
-rw-r--r-- | sql/sql_parse.cc | 7 | ||||
-rw-r--r-- | sql/sql_plugin.cc | 241 | ||||
-rw-r--r-- | sql/sql_plugin.h | 7 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 48 | ||||
-rw-r--r-- | sql/sql_select.cc | 30 | ||||
-rw-r--r-- | sql/table_cache.cc | 2 | ||||
-rw-r--r-- | sql/wsrep_mysqld.cc | 13 | ||||
-rw-r--r-- | sql/wsrep_sst.cc | 8 |
29 files changed, 485 insertions, 284 deletions
diff --git a/sql/contributors.h b/sql/contributors.h index f52d3243453..0359ec54022 100644 --- a/sql/contributors.h +++ b/sql/contributors.h @@ -46,6 +46,7 @@ struct show_table_contributors_st show_table_contributors[]= { {"Auttomattic", "https://automattic.com", "Bronze Sponsor of the MariaDB Foundation"}, {"Verkkokauppa.com", "https://virtuozzo.com", "Bronze Sponsor of the MariaDB Foundation"}, {"Virtuozzo", "https://virtuozzo.com/", "Bronze Sponsor of the MariaDB Foundation"}, + {"Tencent Game DBA", "http://tencentdba.com/about/", "Bronze Sponsor of the MariaDB Foundation"}, /* Sponsors of important features */ {"Google", "USA", "Sponsoring encryption, parallel replication and GTID"}, diff --git a/sql/field.cc b/sql/field.cc index ae815187019..b909d14ec8f 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -355,7 +355,7 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_LONGLONG, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 - MYSQL_TYPE_LONGLONG, MYSQL_TYPE_LONG, + MYSQL_TYPE_LONGLONG, MYSQL_TYPE_LONGLONG, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR diff --git a/sql/handler.cc b/sql/handler.cc index de456f703f1..802364cb6e5 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -5875,6 +5875,30 @@ int handler::ha_reset() } +static int check_wsrep_max_ws_rows() +{ +#ifdef WITH_WSREP + if (wsrep_max_ws_rows) + { + THD *thd= current_thd; + + if (!WSREP(thd)) + return 0; + + thd->wsrep_affected_rows++; + if (thd->wsrep_exec_mode != REPL_RECV && + thd->wsrep_affected_rows > wsrep_max_ws_rows) + { + trans_rollback_stmt(thd) || trans_rollback(thd); + my_message(ER_ERROR_DURING_COMMIT, "wsrep_max_ws_rows exceeded", MYF(0)); + return ER_ERROR_DURING_COMMIT; + } + } +#endif /* WITH_WSREP */ + return 0; +} + + int handler::ha_write_row(uchar *buf) { int error; @@ -5897,20 +5921,9 @@ int handler::ha_write_row(uchar *buf) rows_changed++; if (unlikely(error= binlog_log_row(table, 0, buf, log_func))) DBUG_RETURN(error); /* purecov: inspected */ -#ifdef WITH_WSREP - current_thd->wsrep_affected_rows++; - if (wsrep_max_ws_rows && - current_thd->wsrep_exec_mode != REPL_RECV && - current_thd->wsrep_affected_rows > wsrep_max_ws_rows) - { - trans_rollback_stmt(current_thd) || trans_rollback(current_thd); - my_message(ER_ERROR_DURING_COMMIT, "wsrep_max_ws_rows exceeded", MYF(0)); - DBUG_RETURN(ER_ERROR_DURING_COMMIT); - } -#endif /* WITH_WSREP */ DEBUG_SYNC_C("ha_write_row_end"); - DBUG_RETURN(0); + DBUG_RETURN(check_wsrep_max_ws_rows()); } @@ -5941,18 +5954,7 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data) rows_changed++; if (unlikely(error= binlog_log_row(table, old_data, new_data, log_func))) return error; -#ifdef WITH_WSREP - current_thd->wsrep_affected_rows++; - if (wsrep_max_ws_rows && - current_thd->wsrep_exec_mode != REPL_RECV && - current_thd->wsrep_affected_rows > wsrep_max_ws_rows) - { - trans_rollback_stmt(current_thd) || trans_rollback(current_thd); - my_message(ER_ERROR_DURING_COMMIT, "wsrep_max_ws_rows exceeded", MYF(0)); - return ER_ERROR_DURING_COMMIT; - } -#endif /* WITH_WSREP */ - return 0; + return check_wsrep_max_ws_rows(); } int handler::ha_delete_row(const uchar *buf) @@ -5979,18 +5981,7 @@ int handler::ha_delete_row(const uchar *buf) rows_changed++; if (unlikely(error= binlog_log_row(table, buf, 0, log_func))) return error; -#ifdef WITH_WSREP - current_thd->wsrep_affected_rows++; - if (wsrep_max_ws_rows && - current_thd->wsrep_exec_mode != REPL_RECV && - current_thd->wsrep_affected_rows > wsrep_max_ws_rows) - { - trans_rollback_stmt(current_thd) || trans_rollback(current_thd); - my_message(ER_ERROR_DURING_COMMIT, "wsrep_max_ws_rows exceeded", MYF(0)); - return ER_ERROR_DURING_COMMIT; - } -#endif /* WITH_WSREP */ - return 0; + return check_wsrep_max_ws_rows(); } @@ -6105,6 +6096,13 @@ void handler::set_lock_type(enum thr_lock_type lock) @note Aborting the transaction does NOT end it, it still has to be rolled back with hton->rollback(). + @note It is safe to abort from one thread (bf_thd) the transaction, + running in another thread (victim_thd), because InnoDB's lock_sys and + trx_mutex guarantee the necessary protection. However, its not safe + to access victim_thd->transaction, because it's not protected from + concurrent accesses. And it's an overkill to take LOCK_plugin and + iterate the whole installed_htons[] array every time. + @param bf_thd brute force THD asking for the abort @param victim_thd victim THD to be aborted @@ -6121,29 +6119,16 @@ int ha_abort_transaction(THD *bf_thd, THD *victim_thd, my_bool signal) DBUG_RETURN(0); } - /* Try statement transaction if standard one is not set. */ - THD_TRANS *trans= (victim_thd->transaction.all.ha_list) ? - &victim_thd->transaction.all : &victim_thd->transaction.stmt; - - Ha_trx_info *ha_info= trans->ha_list, *ha_info_next; - - for (; ha_info; ha_info= ha_info_next) + handlerton *hton= installed_htons[DB_TYPE_INNODB]; + if (hton && hton->abort_transaction) { - handlerton *hton= ha_info->ht(); - if (!hton->abort_transaction) - { - /* Skip warning for binlog & wsrep. */ - if (hton->db_type != DB_TYPE_BINLOG && hton != wsrep_hton) - { - WSREP_WARN("Cannot abort transaction."); - } - } - else - { - hton->abort_transaction(hton, bf_thd, victim_thd, signal); - } - ha_info_next= ha_info->next(); + hton->abort_transaction(hton, bf_thd, victim_thd, signal); + } + else + { + WSREP_WARN("Cannot abort InnoDB transaction"); } + DBUG_RETURN(0); } diff --git a/sql/item.cc b/sql/item.cc index 71df93f3ffc..47635b14f46 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -57,6 +57,17 @@ bool cmp_items(Item *a, Item *b) } +/** + Set max_sum_func_level if it is needed +*/ +inline void set_max_sum_func_level(THD *thd, SELECT_LEX *select) +{ + if (thd->lex->in_sum_func && + thd->lex->in_sum_func->nest_level >= select->nest_level) + set_if_bigger(thd->lex->in_sum_func->max_sum_func_level, + select->nest_level - 1); +} + /***************************************************************************** ** Item functions *****************************************************************************/ @@ -2662,9 +2673,28 @@ void Item_field::fix_after_pullout(st_select_lex *new_parent, Item **ref) if (context) { Name_resolution_context *ctx= new Name_resolution_context(); - ctx->outer_context= NULL; // We don't build a complete name resolver - ctx->table_list= NULL; // We rely on first_name_resolution_table instead + if (context->select_lex == new_parent) + { + /* + This field was pushed in then pulled out + (for example left part of IN) + */ + ctx->outer_context= context->outer_context; + } + else if (context->outer_context) + { + /* just pull to the upper context */ + ctx->outer_context= context->outer_context->outer_context; + } + else + { + /* No upper context (merging Derived/VIEW where context chain ends) */ + ctx->outer_context= NULL; + } + ctx->table_list= context->first_name_resolution_table; ctx->select_lex= new_parent; + if (context->select_lex == NULL) + ctx->select_lex= NULL; ctx->first_name_resolution_table= context->first_name_resolution_table; ctx->last_name_resolution_table= context->last_name_resolution_table; ctx->error_processor= context->error_processor; @@ -4885,6 +4915,11 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) if (rf->fix_fields(thd, reference) || rf->check_cols(1)) return -1; + /* + We can not "move" aggregate function in the place where + its arguments are not defined. + */ + set_max_sum_func_level(thd, select); mark_as_dependent(thd, last_checked_context->select_lex, context->select_lex, rf, rf); @@ -4893,6 +4928,11 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) } else { + /* + We can not "move" aggregate function in the place where + its arguments are not defined. + */ + set_max_sum_func_level(thd, select); mark_as_dependent(thd, last_checked_context->select_lex, context->select_lex, this, (Item_ident*)*reference); @@ -5024,6 +5064,11 @@ bool Item_field::fix_fields(THD *thd, Item **reference) return(1); } + /* + We can not "move" aggregate function in the place where + its arguments are not defined. + */ + set_max_sum_func_level(thd, thd->lex->current_select); set_field(new_field); return 0; } @@ -5048,6 +5093,11 @@ bool Item_field::fix_fields(THD *thd, Item **reference) select->parsing_place == IN_GROUP_BY && alias_name_used ? *rf->ref : rf); + /* + We can not "move" aggregate function in the place where + its arguments are not defined. + */ + set_max_sum_func_level(thd, thd->lex->current_select); return FALSE; } } diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index bd1e8b72157..a222335cf97 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -4507,7 +4507,8 @@ Item_cond::fix_fields(THD *thd, Item **ref) was: <field> become: <field> = 1 */ - if (item->type() == FIELD_ITEM) + Item::Type type= item->type(); + if (type == Item::FIELD_ITEM || type == Item::REF_ITEM) { Query_arena backup, *arena; Item *new_item; diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 75b034cbd4f..43770a88cdc 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -165,31 +165,6 @@ String *Item_func_md5::val_str_ascii(String *str) } -/* - The MD5()/SHA() functions treat their parameter as being a case sensitive. - Thus we set binary collation on it so different instances of MD5() will be - compared properly. -*/ -static CHARSET_INFO *get_checksum_charset(const char *csname) -{ - CHARSET_INFO *cs= get_charset_by_csname(csname, MY_CS_BINSORT, MYF(0)); - if (!cs) - { - // Charset has no binary collation: use my_charset_bin. - cs= &my_charset_bin; - } - return cs; -} - - -void Item_func_md5::fix_length_and_dec() -{ - CHARSET_INFO *cs= get_checksum_charset(args[0]->collation.collation->csname); - args[0]->collation.set(cs, DERIVATION_COERCIBLE); - fix_length_and_charset(32, default_charset()); -} - - String *Item_func_sha::val_str_ascii(String *str) { DBUG_ASSERT(fixed == 1); @@ -215,8 +190,6 @@ String *Item_func_sha::val_str_ascii(String *str) void Item_func_sha::fix_length_and_dec() { - CHARSET_INFO *cs= get_checksum_charset(args[0]->collation.collation->csname); - args[0]->collation.set(cs, DERIVATION_COERCIBLE); // size of hex representation of hash fix_length_and_charset(SHA1_HASH_SIZE * 2, default_charset()); } @@ -346,9 +319,6 @@ void Item_func_sha2::fix_length_and_dec() "sha2"); } - CHARSET_INFO *cs= get_checksum_charset(args[0]->collation.collation->csname); - args[0]->collation.set(cs, DERIVATION_COERCIBLE); - #else THD *thd= current_thd; push_warning_printf(thd, diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 65c286819d3..a56e100a956 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -96,50 +96,99 @@ public: }; -class Item_func_md5 :public Item_str_ascii_func +/** + Functions that return a checksum or a hash of the argument, + or somehow else encode or decode the argument, + returning an ASCII-repertoire string. +*/ +class Item_str_ascii_checksum_func: public Item_str_ascii_func +{ +public: + Item_str_ascii_checksum_func(THD *thd, Item *a) + :Item_str_ascii_func(thd, a) { } + Item_str_ascii_checksum_func(THD *thd, Item *a, Item *b) + :Item_str_ascii_func(thd, a, b) { } + bool eq(const Item *item, bool binary_cmp) const + { + // Always use binary argument comparison: MD5('x') != MD5('X') + return Item_func::eq(item, true); + } +}; + + +/** + Functions that return a checksum or a hash of the argument, + or somehow else encode or decode the argument, + returning a binary string. +*/ +class Item_str_binary_checksum_func: public Item_str_func +{ +public: + Item_str_binary_checksum_func(THD *thd, Item *a) + :Item_str_func(thd, a) { } + Item_str_binary_checksum_func(THD *thd, Item *a, Item *b) + :Item_str_func(thd, a, b) { } + bool eq(const Item *item, bool binary_cmp) const + { + /* + Always use binary argument comparison: + FROM_BASE64('test') != FROM_BASE64('TEST') + */ + return Item_func::eq(item, true); + } +}; + + +class Item_func_md5 :public Item_str_ascii_checksum_func { String tmp_value; public: - Item_func_md5(THD *thd, Item *a): Item_str_ascii_func(thd, a) {} + Item_func_md5(THD *thd, Item *a): Item_str_ascii_checksum_func(thd, a) {} String *val_str_ascii(String *); - void fix_length_and_dec(); + void fix_length_and_dec() + { + fix_length_and_charset(32, default_charset()); + } const char *func_name() const { return "md5"; } }; -class Item_func_sha :public Item_str_ascii_func +class Item_func_sha :public Item_str_ascii_checksum_func { public: - Item_func_sha(THD *thd, Item *a): Item_str_ascii_func(thd, a) {} + Item_func_sha(THD *thd, Item *a): Item_str_ascii_checksum_func(thd, a) {} String *val_str_ascii(String *); void fix_length_and_dec(); const char *func_name() const { return "sha"; } }; -class Item_func_sha2 :public Item_str_ascii_func +class Item_func_sha2 :public Item_str_ascii_checksum_func { public: - Item_func_sha2(THD *thd, Item *a, Item *b): Item_str_ascii_func(thd, a, b) {} + Item_func_sha2(THD *thd, Item *a, Item *b) + :Item_str_ascii_checksum_func(thd, a, b) {} String *val_str_ascii(String *); void fix_length_and_dec(); const char *func_name() const { return "sha2"; } }; -class Item_func_to_base64 :public Item_str_ascii_func +class Item_func_to_base64 :public Item_str_ascii_checksum_func { String tmp_value; public: - Item_func_to_base64(THD *thd, Item *a): Item_str_ascii_func(thd, a) {} + Item_func_to_base64(THD *thd, Item *a) + :Item_str_ascii_checksum_func(thd, a) {} String *val_str_ascii(String *); void fix_length_and_dec(); const char *func_name() const { return "to_base64"; } }; -class Item_func_from_base64 :public Item_str_func +class Item_func_from_base64 :public Item_str_binary_checksum_func { String tmp_value; public: - Item_func_from_base64(THD *thd, Item *a): Item_str_func(thd, a) {} + Item_func_from_base64(THD *thd, Item *a) + :Item_str_binary_checksum_func(thd, a) { } String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "from_base64"; } @@ -147,7 +196,7 @@ public: #include <my_crypt.h> -class Item_aes_crypt :public Item_str_func +class Item_aes_crypt :public Item_str_binary_checksum_func { enum { AES_KEY_LENGTH = 128 }; void create_key(String *user_key, uchar* key); @@ -155,7 +204,8 @@ class Item_aes_crypt :public Item_str_func protected: int what; public: - Item_aes_crypt(THD *thd, Item *a, Item *b): Item_str_func(thd, a, b) {} + Item_aes_crypt(THD *thd, Item *a, Item *b) + :Item_str_binary_checksum_func(thd, a, b) {} String *val_str(String *); }; @@ -431,7 +481,7 @@ public: authentication procedure works, see comments in password.c. */ -class Item_func_password :public Item_str_ascii_func +class Item_func_password :public Item_str_ascii_checksum_func { public: enum PW_Alg {OLD, NEW}; @@ -441,9 +491,9 @@ private: bool deflt; public: Item_func_password(THD *thd, Item *a): - Item_str_ascii_func(thd, a), alg(NEW), deflt(1) {} + Item_str_ascii_checksum_func(thd, a), alg(NEW), deflt(1) {} Item_func_password(THD *thd, Item *a, PW_Alg al): - Item_str_ascii_func(thd, a), alg(al), deflt(0) {} + Item_str_ascii_checksum_func(thd, a), alg(al), deflt(0) {} String *val_str_ascii(String *str); bool fix_fields(THD *thd, Item **ref); void fix_length_and_dec() @@ -461,12 +511,14 @@ public: -class Item_func_des_encrypt :public Item_str_func +class Item_func_des_encrypt :public Item_str_binary_checksum_func { String tmp_value,tmp_arg; public: - Item_func_des_encrypt(THD *thd, Item *a): Item_str_func(thd, a) {} - Item_func_des_encrypt(THD *thd, Item *a, Item *b): Item_str_func(thd, a, b) {} + Item_func_des_encrypt(THD *thd, Item *a) + :Item_str_binary_checksum_func(thd, a) {} + Item_func_des_encrypt(THD *thd, Item *a, Item *b) + :Item_str_binary_checksum_func(thd, a, b) {} String *val_str(String *); void fix_length_and_dec() { @@ -477,12 +529,14 @@ public: const char *func_name() const { return "des_encrypt"; } }; -class Item_func_des_decrypt :public Item_str_func +class Item_func_des_decrypt :public Item_str_binary_checksum_func { String tmp_value; public: - Item_func_des_decrypt(THD *thd, Item *a): Item_str_func(thd, a) {} - Item_func_des_decrypt(THD *thd, Item *a, Item *b): Item_str_func(thd, a, b) {} + Item_func_des_decrypt(THD *thd, Item *a) + :Item_str_binary_checksum_func(thd, a) {} + Item_func_des_decrypt(THD *thd, Item *a, Item *b) + :Item_str_binary_checksum_func(thd, a, b) {} String *val_str(String *); void fix_length_and_dec() { @@ -495,7 +549,13 @@ public: const char *func_name() const { return "des_decrypt"; } }; -class Item_func_encrypt :public Item_str_func + +/** + QQ: Item_func_encrypt should derive from Item_str_ascii_checksum_func. + However, it should be fixed to handle UCS2, UTF16, UTF32 properly first, + as the underlying crypt() call expects a null-terminated input string. +*/ +class Item_func_encrypt :public Item_str_binary_checksum_func { String tmp_value; @@ -505,11 +565,12 @@ class Item_func_encrypt :public Item_str_func collation.set(&my_charset_bin); } public: - Item_func_encrypt(THD *thd, Item *a): Item_str_func(thd, a) + Item_func_encrypt(THD *thd, Item *a): Item_str_binary_checksum_func(thd, a) { constructor_helper(); } - Item_func_encrypt(THD *thd, Item *a, Item *b): Item_str_func(thd, a, b) + Item_func_encrypt(THD *thd, Item *a, Item *b) + :Item_str_binary_checksum_func(thd, a, b) { constructor_helper(); } @@ -525,7 +586,7 @@ public: #include "sql_crypt.h" -class Item_func_encode :public Item_str_func +class Item_func_encode :public Item_str_binary_checksum_func { private: /** Whether the PRNG has already been seeded. */ @@ -534,7 +595,7 @@ protected: SQL_CRYPT sql_crypt; public: Item_func_encode(THD *thd, Item *a, Item *seed_arg): - Item_str_func(thd, a, seed_arg) {} + Item_str_binary_checksum_func(thd, a, seed_arg) {} String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "encode"; } @@ -803,12 +864,12 @@ public: }; -class Item_func_hex :public Item_str_ascii_func +class Item_func_hex :public Item_str_ascii_checksum_func { String tmp_value; public: Item_func_hex(THD *thd, Item *a): - Item_str_ascii_func(thd, a) {} + Item_str_ascii_checksum_func(thd, a) {} const char *func_name() const { return "hex"; } String *val_str_ascii(String *); void fix_length_and_dec() @@ -1149,21 +1210,23 @@ public: #define ZLIB_DEPENDED_FUNCTION { null_value=1; return 0; } #endif -class Item_func_compress: public Item_str_func +class Item_func_compress: public Item_str_binary_checksum_func { String buffer; public: - Item_func_compress(THD *thd, Item *a): Item_str_func(thd, a) {} + Item_func_compress(THD *thd, Item *a) + :Item_str_binary_checksum_func(thd, a) {} void fix_length_and_dec(){max_length= (args[0]->max_length*120)/100+12;} const char *func_name() const{return "compress";} String *val_str(String *) ZLIB_DEPENDED_FUNCTION }; -class Item_func_uncompress: public Item_str_func +class Item_func_uncompress: public Item_str_binary_checksum_func { String buffer; public: - Item_func_uncompress(THD *thd, Item *a): Item_str_func(thd, a) {} + Item_func_uncompress(THD *thd, Item *a) + :Item_str_binary_checksum_func(thd, a) {} void fix_length_and_dec(){ maybe_null= 1; max_length= MAX_BLOB_WIDTH; } const char *func_name() const{return "uncompress";} String *val_str(String *) ZLIB_DEPENDED_FUNCTION diff --git a/sql/item_sum.h b/sql/item_sum.h index 8568eaae907..7ad037c046b 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -1084,7 +1084,6 @@ public: fixed= true; } table_map used_tables() const { return (table_map) 1L; } - void set_result_field(Field *) { DBUG_ASSERT(0); } void save_in_result_field(bool no_conversions) { DBUG_ASSERT(0); } }; diff --git a/sql/log.cc b/sql/log.cc index be24bcd718a..45ab5c8827b 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -3102,7 +3102,7 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, if (! write_error) { write_error= 1; - sql_print_error(ER_THD(thd, ER_ERROR_ON_WRITE), name, error); + sql_print_error(ER_THD(thd, ER_ERROR_ON_WRITE), name, tmp_errno); } } } diff --git a/sql/log_event.cc b/sql/log_event.cc index afa58afc21d..de6528638f0 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -6022,7 +6022,6 @@ int Load_log_event::do_apply_event(NET* net, rpl_group_info *rgi, new_db.str= (char *) rpl_filter->get_rewrite_db(db, &new_db.length); thd->set_db(new_db.str, new_db.length); DBUG_ASSERT(thd->query() == 0); - thd->reset_query_inner(); // Should not be needed thd->is_slave_error= 0; clear_all_errors(thd, const_cast<Relay_log_info*>(rli)); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index a71d6d10042..2f0fd46845c 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4096,6 +4096,7 @@ static int init_common_variables() max_system_variables.pseudo_thread_id= (ulong)~0; server_start_time= flush_status_time= my_time(0); + my_disable_copystat_in_redel= 1; global_rpl_filter= new Rpl_filter; binlog_filter= new Rpl_filter; @@ -4167,6 +4168,8 @@ static int init_common_variables() return 1; } + opt_log_basename= const_cast<char *>("mysql"); + if (gethostname(glob_hostname,sizeof(glob_hostname)) < 0) { /* @@ -4176,9 +4179,8 @@ static int init_common_variables() strmake(glob_hostname, STRING_WITH_LEN("localhost")); sql_print_warning("gethostname failed, using '%s' as hostname", glob_hostname); - opt_log_basename= const_cast<char *>("mysql"); } - else + else if (is_filename_allowed(glob_hostname, strlen(glob_hostname), FALSE)) opt_log_basename= glob_hostname; strmake(pidfile_name, opt_log_basename, sizeof(pidfile_name)-5); @@ -5075,6 +5077,12 @@ static int init_server_components() } /* + Since some wsrep threads (THDs) are create before plugins are + initialized, LOCK_plugin mutex needs to be initialized here. + */ + plugin_mutex_init(); + + /* Wsrep initialization must happen at this point, because: - opt_bin_logname must be known when starting replication since SST may need it @@ -5302,6 +5310,17 @@ static int init_server_components() #endif #ifdef WITH_WSREP + /* + Now is the right time to initialize members of wsrep startup threads + that rely on plugins and other related global system variables to be + initialized. This initialization was not possible before, as plugins + (and thus some global system variables) are initialized after wsrep + startup threads are created. + Note: This only needs to be done for rsync, xtrabackup based SST methods. + */ + if (wsrep_before_SE()) + wsrep_plugins_post_init(); + if (WSREP_ON && !opt_bin_log) { wsrep_emulate_bin_log= 1; @@ -8970,9 +8989,10 @@ mysqld_get_one_option(int optid, const struct my_option *opt, char *argument) case (int) OPT_LOG_BASENAME: { if (opt_log_basename[0] == 0 || strchr(opt_log_basename, FN_EXTCHAR) || - strchr(opt_log_basename,FN_LIBCHAR)) + strchr(opt_log_basename,FN_LIBCHAR) || + !is_filename_allowed(opt_log_basename, strlen(opt_log_basename), FALSE)) { - sql_print_error("Wrong argument for --log-basename. It can't be empty or contain '.' or '" FN_DIRSEP "'"); + sql_print_error("Wrong argument for --log-basename. It can't be empty or contain '.' or '" FN_DIRSEP "'. It must be valid filename."); return 1; } if (log_error_file_ptr != disabled_my_option) @@ -9867,9 +9887,9 @@ static int test_if_case_insensitive(const char *dir_name) MY_STAT stat_info; DBUG_ENTER("test_if_case_insensitive"); - fn_format(buff, glob_hostname, dir_name, ".lower-test", + fn_format(buff, opt_log_basename, dir_name, ".lower-test", MY_UNPACK_FILENAME | MY_REPLACE_EXT | MY_REPLACE_DIR); - fn_format(buff2, glob_hostname, dir_name, ".LOWER-TEST", + fn_format(buff2, opt_log_basename, dir_name, ".LOWER-TEST", MY_UNPACK_FILENAME | MY_REPLACE_EXT | MY_REPLACE_DIR); mysql_file_delete(key_file_casetest, buff2, MYF(0)); if ((file= mysql_file_create(key_file_casetest, diff --git a/sql/net_serv.cc b/sql/net_serv.cc index ef7a46a7109..da3c5646e84 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. - Copyright (c) 2010, 2014, SkySQL Ab. +/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. + Copyright (c) 2012, 2016, 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 diff --git a/sql/parse_file.h b/sql/parse_file.h index e4756e6c8af..87917dbd71b 100644 --- a/sql/parse_file.h +++ b/sql/parse_file.h @@ -42,9 +42,9 @@ enum file_opt_type { struct File_option { - LEX_STRING name; /**< Name of the option */ - int offset; /**< offset to base address of value */ - file_opt_type type; /**< Option type */ + LEX_STRING name; /**< Name of the option */ + my_ptrdiff_t offset; /**< offset to base address of value */ + file_opt_type type; /**< Option type */ }; diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc index 9dd3e532d1e..f72eb676743 100644 --- a/sql/signal_handler.cc +++ b/sql/signal_handler.cc @@ -64,13 +64,13 @@ extern "C" sig_handler handle_fatal_signal(int sig) struct tm tm; #ifdef HAVE_STACKTRACE THD *thd; -#endif /* This flag remembers if the query pointer was found invalid. We will try and print the query at the end of the signal handler, in case we're wrong. */ bool print_invalid_query_pointer= false; +#endif if (segfaulted) { @@ -276,6 +276,7 @@ extern "C" sig_handler handle_fatal_signal(int sig) "\"mlockall\" bugs.\n"); } +#ifdef HAVE_STACKTRACE if (print_invalid_query_pointer) { my_safe_printf_stderr( @@ -285,6 +286,7 @@ extern "C" sig_handler handle_fatal_signal(int sig) my_write_stderr(thd->query(), MY_MIN(65536U, thd->query_length())); my_safe_printf_stderr("\n\n"); } +#endif #ifdef HAVE_WRITE_CORE if (test_flags & TEST_CORE_ON_SIGNAL) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 23302e1847e..48c57303805 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2797,7 +2797,7 @@ bool change_password(THD *thd, LEX_USER *user) if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0)) { - thd->set_query_inner(buff, query_length, system_charset_info); + thd->set_query(buff, query_length, system_charset_info); WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, (char*)"user", NULL); } @@ -2858,7 +2858,7 @@ error: // this label is used in WSREP_TO_ISOLATION_BEGIN { WSREP_TO_ISOLATION_END; - thd->set_query_inner(query_save); + thd->set_query(query_save); thd->wsrep_exec_mode = LOCAL_STATE; } #endif /* WITH_WSREP */ @@ -2913,7 +2913,7 @@ int acl_set_default_role(THD *thd, const char *host, const char *user, if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0)) { - thd->set_query_inner(buff, query_length, system_charset_info); + thd->set_query(buff, query_length, system_charset_info); WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, (char*)"user", NULL); } @@ -3009,7 +3009,7 @@ error: // this label is used in WSREP_TO_ISOLATION_END { WSREP_TO_ISOLATION_END; - thd->set_query_inner(query_save); + thd->set_query(query_save); thd->wsrep_exec_mode = LOCAL_STATE; } #endif /* WITH_WSREP */ diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index b974075b442..1f4426f2043 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -466,7 +466,19 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, } thd->prepare_derived_at_open= FALSE; - table->next_global= save_next_global; + /* + MERGE engine may adjust table->next_global chain, thus we have to + append save_next_global after merge children. + */ + if (save_next_global) + { + TABLE_LIST *table_list_iterator= table; + while (table_list_iterator->next_global) + table_list_iterator= table_list_iterator->next_global; + table_list_iterator->next_global= save_next_global; + save_next_global->prev_global= &table_list_iterator->next_global; + } + table->next_local= save_next_local; thd->open_options&= ~extra_open_options; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 9ea5b20dce6..b4a3cc27d2c 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -9267,6 +9267,7 @@ open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, */ lex->reset_n_backup_query_tables_list(&query_tables_list_backup); thd->reset_n_backup_open_tables_state(backup); + thd->lex->sql_command= SQLCOM_SELECT; if (open_and_lock_tables(thd, table_list, FALSE, MYSQL_OPEN_IGNORE_FLUSH | diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 91dd8ad7325..c69303c5273 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -961,7 +961,7 @@ inline void Query_cache_query::unlock_reading() void Query_cache_query::init_n_lock() { DBUG_ENTER("Query_cache_query::init_n_lock"); - res=0; wri = 0; len = 0; + res=0; wri = 0; len = 0; ready= 0; mysql_rwlock_init(key_rwlock_query_cache_query_lock, &lock); lock_writing(); DBUG_PRINT("qcache", ("inited & locked query for block 0x%lx", @@ -1226,6 +1226,7 @@ void Query_cache::end_of_result(THD *thd) query_cache.split_block(last_result_block,len); header->found_rows(limit_found_rows); + header->set_results_ready(); // signal for plugin header->result()->type= Query_cache_block::RESULT; /* Drop the writer. */ diff --git a/sql/sql_cache.h b/sql/sql_cache.h index 00ba9bf59d8..657caf4a5bc 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -156,8 +156,9 @@ struct Query_cache_query Query_cache_block *res; Query_cache_tls *wri; ulong len; - uint8 tbls_type; unsigned int last_pkt_nr; + uint8 tbls_type; + uint8 ready; Query_cache_query() {} /* Remove gcc warning */ inline void init_n_lock(); @@ -177,6 +178,12 @@ struct Query_cache_query { return (((uchar*)this) + ALIGN_SIZE(sizeof(Query_cache_query))); } + /** + following used to check if result ready in plugin without + locking rw_lock of the query. + */ + inline void set_results_ready() { ready= 1; } + inline bool is_results_ready() { return ready; } void lock_writing(); void lock_reading(); bool try_lock_writing(); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 430191cee5d..4143d2cc419 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -5828,9 +5828,11 @@ int THD::decide_logging_format(TABLE_LIST *tables) { static const char *prelocked_mode_name[] = { "NON_PRELOCKED", + "LOCK_TABLES", "PRELOCKED", "PRELOCKED_UNDER_LOCK_TABLES", }; + compile_time_assert(array_elements(prelocked_mode_name) == LTM_always_last); DBUG_PRINT("debug", ("prelocked_mode: %s", prelocked_mode_name[locked_tables_mode])); } diff --git a/sql/sql_class.h b/sql/sql_class.h index b22dc8142d8..da885c3dbac 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1262,7 +1262,8 @@ enum enum_locked_tables_mode LTM_NONE= 0, LTM_LOCK_TABLES, LTM_PRELOCKED, - LTM_PRELOCKED_UNDER_LOCK_TABLES + LTM_PRELOCKED_UNDER_LOCK_TABLES, + LTM_always_last }; @@ -4610,6 +4611,11 @@ public: save_copy_field_end= copy_field_end= NULL; } } + void free_copy_field_data() + { + for (Copy_field *ptr= copy_field ; ptr != copy_field_end ; ptr++) + ptr->tmp.free(); + } }; class select_union :public select_result_interceptor diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index c6d80e1ba97..a15f3da8e12 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -7298,10 +7298,9 @@ void mysql_parse(THD *thd, char *rawbuf, uint length, Note that we don't need LOCK_thread_count to modify query_length. */ if (found_semicolon && (ulong) (found_semicolon - thd->query())) - thd->set_query_inner(thd->query(), - (uint32) (found_semicolon - - thd->query() - 1), - thd->charset()); + thd->set_query(thd->query(), + (uint32) (found_semicolon - thd->query() - 1), + thd->charset()); /* Actually execute the query */ if (found_semicolon) { diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 60248f3fef4..5b16482ae66 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1536,10 +1536,6 @@ int plugin_init(int *argc, char **argv, int flags) dlopen_count =0; -#ifdef HAVE_PSI_INTERFACE - init_plugin_psi_keys(); -#endif - init_alloc_root(&plugin_mem_root, 4096, 4096, MYF(0)); init_alloc_root(&plugin_vars_mem_root, 4096, 4096, MYF(0)); init_alloc_root(&tmp_root, 4096, 4096, MYF(0)); @@ -1548,9 +1544,6 @@ int plugin_init(int *argc, char **argv, int flags) get_bookmark_hash_key, NULL, HASH_UNIQUE)) goto err; - - mysql_mutex_init(key_LOCK_plugin, &LOCK_plugin, MY_MUTEX_INIT_FAST); - if (my_init_dynamic_array(&plugin_dl_array, sizeof(struct st_plugin_dl *), 16, 16, MYF(0)) || my_init_dynamic_array(&plugin_array, @@ -2849,6 +2842,22 @@ static st_bookmark *find_bookmark(const char *plugin, const char *name, } +static size_t var_storage_size(int flags) +{ + switch (flags & PLUGIN_VAR_TYPEMASK) { + case PLUGIN_VAR_BOOL: return sizeof(my_bool); + case PLUGIN_VAR_INT: return sizeof(int); + case PLUGIN_VAR_LONG: return sizeof(long); + case PLUGIN_VAR_ENUM: return sizeof(long); + case PLUGIN_VAR_LONGLONG: return sizeof(ulonglong); + case PLUGIN_VAR_SET: return sizeof(ulonglong); + case PLUGIN_VAR_STR: return sizeof(char*); + case PLUGIN_VAR_DOUBLE: return sizeof(double); + default: DBUG_ASSERT(0); return 0; + } +} + + /* returns a bookmark for thd-local variables, creating if neccessary. returns null for non thd-local variables. @@ -2857,39 +2866,13 @@ static st_bookmark *find_bookmark(const char *plugin, const char *name, static st_bookmark *register_var(const char *plugin, const char *name, int flags) { - uint length= strlen(plugin) + strlen(name) + 3, size= 0, offset, new_size; + uint length= strlen(plugin) + strlen(name) + 3, size, offset, new_size; st_bookmark *result; char *varname, *p; - if (!(flags & PLUGIN_VAR_THDLOCAL)) - return NULL; - - switch (flags & PLUGIN_VAR_TYPEMASK) { - case PLUGIN_VAR_BOOL: - size= sizeof(my_bool); - break; - case PLUGIN_VAR_INT: - size= sizeof(int); - break; - case PLUGIN_VAR_LONG: - case PLUGIN_VAR_ENUM: - size= sizeof(long); - break; - case PLUGIN_VAR_LONGLONG: - case PLUGIN_VAR_SET: - size= sizeof(ulonglong); - break; - case PLUGIN_VAR_STR: - size= sizeof(char*); - break; - case PLUGIN_VAR_DOUBLE: - size= sizeof(double); - break; - default: - DBUG_ASSERT(0); - return NULL; - }; + DBUG_ASSERT(flags & PLUGIN_VAR_THDLOCAL); + size= var_storage_size(flags); varname= ((char*) my_alloca(length)); strxmov(varname + 1, plugin, "_", name, NullS); for (p= varname + 1; *p; p++) @@ -2983,25 +2966,17 @@ void sync_dynamic_session_variables(THD* thd, bool global_lock) */ for (idx= 0; idx < bookmark_hash.records; idx++) { - sys_var_pluginvar *pi; - sys_var *var; st_bookmark *v= (st_bookmark*) my_hash_element(&bookmark_hash,idx); if (v->version <= thd->variables.dynamic_variables_version) continue; /* already in thd->variables */ - if (!(var= intern_find_sys_var(v->key + 1, v->name_len)) || - !(pi= var->cast_pluginvar()) || - v->key[0] != plugin_var_bookmark_key(pi->plugin_var->flags)) - continue; - /* Here we do anything special that may be required of the data types */ - if ((pi->plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR && - pi->plugin_var->flags & PLUGIN_VAR_MEMALLOC) + if ((v->key[0] & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR && + v->key[0] & BOOKMARK_MEMALLOC) { - int offset= ((thdvar_str_t *)(pi->plugin_var))->offset; - char **pp= (char**) (thd->variables.dynamic_variables_ptr + offset); + char **pp= (char**) (thd->variables.dynamic_variables_ptr + v->offset); if (*pp) *pp= my_strdup(*pp, MYF(MY_WME|MY_FAE)); } @@ -3116,28 +3091,19 @@ void plugin_thdvar_init(THD *thd) thd->variables.dynamic_variables_size= 0; thd->variables.dynamic_variables_ptr= 0; - if (IF_WSREP((!WSREP(thd) || !thd->wsrep_applier),1)) - { - mysql_mutex_lock(&LOCK_plugin); - thd->variables.table_plugin= - intern_plugin_lock(NULL, global_system_variables.table_plugin); - if (global_system_variables.tmp_table_plugin) - thd->variables.tmp_table_plugin= - intern_plugin_lock(NULL, global_system_variables.tmp_table_plugin); - if (global_system_variables.enforced_table_plugin) - thd->variables.enforced_table_plugin= - intern_plugin_lock(NULL, global_system_variables.enforced_table_plugin); - intern_plugin_unlock(NULL, old_table_plugin); - intern_plugin_unlock(NULL, old_tmp_table_plugin); - intern_plugin_unlock(NULL, old_enforced_table_plugin); - mysql_mutex_unlock(&LOCK_plugin); - } - else - { - thd->variables.table_plugin= NULL; - thd->variables.tmp_table_plugin= NULL; - thd->variables.enforced_table_plugin= NULL; - } + mysql_mutex_lock(&LOCK_plugin); + thd->variables.table_plugin= + intern_plugin_lock(NULL, global_system_variables.table_plugin); + if (global_system_variables.tmp_table_plugin) + thd->variables.tmp_table_plugin= + intern_plugin_lock(NULL, global_system_variables.tmp_table_plugin); + if (global_system_variables.enforced_table_plugin) + thd->variables.enforced_table_plugin= + intern_plugin_lock(NULL, global_system_variables.enforced_table_plugin); + intern_plugin_unlock(NULL, old_table_plugin); + intern_plugin_unlock(NULL, old_tmp_table_plugin); + intern_plugin_unlock(NULL, old_enforced_table_plugin); + mysql_mutex_unlock(&LOCK_plugin); DBUG_VOID_RETURN; } @@ -3448,69 +3414,58 @@ bool sys_var_pluginvar::session_update(THD *thd, set_var *var) return false; } -bool sys_var_pluginvar::global_update(THD *thd, set_var *var) +static const void *var_def_ptr(st_mysql_sys_var *pv) { - DBUG_ASSERT(!is_readonly()); - mysql_mutex_assert_owner(&LOCK_global_system_variables); - - void *tgt= real_value_ptr(thd, OPT_GLOBAL); - const void *src= &var->save_result; - - if (!var->value) - { - switch (plugin_var->flags & (PLUGIN_VAR_TYPEMASK | PLUGIN_VAR_THDLOCAL)) { + switch (pv->flags & (PLUGIN_VAR_TYPEMASK | PLUGIN_VAR_THDLOCAL)) { case PLUGIN_VAR_INT: - src= &((sysvar_uint_t*) plugin_var)->def_val; - break; + return &((sysvar_uint_t*) pv)->def_val; case PLUGIN_VAR_LONG: - src= &((sysvar_ulong_t*) plugin_var)->def_val; - break; + return &((sysvar_ulong_t*) pv)->def_val; case PLUGIN_VAR_LONGLONG: - src= &((sysvar_ulonglong_t*) plugin_var)->def_val; - break; + return &((sysvar_ulonglong_t*) pv)->def_val; case PLUGIN_VAR_ENUM: - src= &((sysvar_enum_t*) plugin_var)->def_val; - break; + return &((sysvar_enum_t*) pv)->def_val; case PLUGIN_VAR_SET: - src= &((sysvar_set_t*) plugin_var)->def_val; - break; + return &((sysvar_set_t*) pv)->def_val; case PLUGIN_VAR_BOOL: - src= &((sysvar_bool_t*) plugin_var)->def_val; - break; + return &((sysvar_bool_t*) pv)->def_val; case PLUGIN_VAR_STR: - src= &((sysvar_str_t*) plugin_var)->def_val; - break; + return &((sysvar_str_t*) pv)->def_val; case PLUGIN_VAR_DOUBLE: - src= &((sysvar_double_t*) plugin_var)->def_val; - break; + return &((sysvar_double_t*) pv)->def_val; case PLUGIN_VAR_INT | PLUGIN_VAR_THDLOCAL: - src= &((thdvar_uint_t*) plugin_var)->def_val; - break; + return &((thdvar_uint_t*) pv)->def_val; case PLUGIN_VAR_LONG | PLUGIN_VAR_THDLOCAL: - src= &((thdvar_ulong_t*) plugin_var)->def_val; - break; + return &((thdvar_ulong_t*) pv)->def_val; case PLUGIN_VAR_LONGLONG | PLUGIN_VAR_THDLOCAL: - src= &((thdvar_ulonglong_t*) plugin_var)->def_val; - break; + return &((thdvar_ulonglong_t*) pv)->def_val; case PLUGIN_VAR_ENUM | PLUGIN_VAR_THDLOCAL: - src= &((thdvar_enum_t*) plugin_var)->def_val; - break; + return &((thdvar_enum_t*) pv)->def_val; case PLUGIN_VAR_SET | PLUGIN_VAR_THDLOCAL: - src= &((thdvar_set_t*) plugin_var)->def_val; - break; + return &((thdvar_set_t*) pv)->def_val; case PLUGIN_VAR_BOOL | PLUGIN_VAR_THDLOCAL: - src= &((thdvar_bool_t*) plugin_var)->def_val; - break; + return &((thdvar_bool_t*) pv)->def_val; case PLUGIN_VAR_STR | PLUGIN_VAR_THDLOCAL: - src= &((thdvar_str_t*) plugin_var)->def_val; - break; + return &((thdvar_str_t*) pv)->def_val; case PLUGIN_VAR_DOUBLE | PLUGIN_VAR_THDLOCAL: - src= &((thdvar_double_t*) plugin_var)->def_val; - break; + return &((thdvar_double_t*) pv)->def_val; default: DBUG_ASSERT(0); + return NULL; } - } +} + + +bool sys_var_pluginvar::global_update(THD *thd, set_var *var) +{ + DBUG_ASSERT(!is_readonly()); + mysql_mutex_assert_owner(&LOCK_global_system_variables); + + void *tgt= real_value_ptr(thd, OPT_GLOBAL); + const void *src= &var->save_result; + + if (!var->value) + src= var_def_ptr(plugin_var); plugin_var->update(thd, plugin_var, tgt, src); return false; @@ -3863,7 +3818,18 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp, *(int*)(opt + 1)= offset= v->offset; if (opt->flags & PLUGIN_VAR_NOCMDOPT) + { + char *val= global_system_variables.dynamic_variables_ptr + offset; + if (((opt->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR) && + (opt->flags & PLUGIN_VAR_MEMALLOC)) + { + char *def_val= *(char**)var_def_ptr(opt); + *(char**)val= def_val ? my_strdup(def_val, MYF(0)) : NULL; + } + else + memcpy(val, var_def_ptr(opt), var_storage_size(opt->flags)); continue; + } optname= (char*) memdup_root(mem_root, v->key + 1, (optnamelen= v->name_len) + 1); @@ -4261,3 +4227,54 @@ int thd_setspecific(MYSQL_THD thd, MYSQL_THD_KEY_T key, void *value) return 0; } +void plugin_mutex_init() +{ +#ifdef HAVE_PSI_INTERFACE + init_plugin_psi_keys(); +#endif + mysql_mutex_init(key_LOCK_plugin, &LOCK_plugin, MY_MUTEX_INIT_FAST); +} + +#ifdef WITH_WSREP + +/* + Placeholder for global_system_variables.table_plugin required during + initialization of startup wsrep threads. +*/ +static st_plugin_int wsrep_dummy_plugin; +static st_plugin_int *wsrep_dummy_plugin_ptr; + +/* + Initialize wsrep_dummy_plugin and assign it to + global_system_variables.table_plugin. +*/ +void wsrep_plugins_pre_init() +{ + wsrep_dummy_plugin_ptr= &wsrep_dummy_plugin; + wsrep_dummy_plugin.state= PLUGIN_IS_DISABLED; + global_system_variables.table_plugin= + plugin_int_to_ref(wsrep_dummy_plugin_ptr); +} + +/* + This function is intended to be called after the plugins and related + global system variables are initialized. It re-initializes some data + members of wsrep startup threads with correct values, as these value + were not available at the time these threads were created. +*/ +void wsrep_plugins_post_init() +{ + THD *thd; + I_List_iterator<THD> it(threads); + + while ((thd= it++)) + { + if (IF_WSREP(thd->wsrep_applier,1)) + { + plugin_thdvar_init(thd); + } + } + + return; +} +#endif /* WITH_WSREP */ diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h index 77ec24bbe9c..d11c449962a 100644 --- a/sql/sql_plugin.h +++ b/sql/sql_plugin.h @@ -180,6 +180,7 @@ sys_var *find_plugin_sysvar(st_plugin_int *plugin, st_mysql_sys_var *var); void plugin_opt_set_limits(struct my_option *, const struct st_mysql_sys_var *); extern SHOW_COMP_OPTION plugin_status(const char *name, size_t len, int type); extern bool check_valid_path(const char *path, size_t length); +extern void plugin_mutex_init(); typedef my_bool (plugin_foreach_func)(THD *thd, plugin_ref plugin, @@ -194,3 +195,9 @@ extern bool plugin_dl_foreach(THD *thd, const LEX_STRING *dl, extern void sync_dynamic_session_variables(THD* thd, bool global_lock); #endif + +#ifdef WITH_WSREP +extern void wsrep_plugins_pre_init(); +extern void wsrep_plugins_post_init(); +#endif /* WITH_WSREP */ + diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 0db1daa378e..d381825851d 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -3030,7 +3030,36 @@ void mysql_sql_stmt_execute(THD *thd) DBUG_PRINT("info",("stmt: 0x%lx", (long) stmt)); + /* + thd->free_list can already have some Items, + e.g. for a query like this: + PREPARE stmt FROM 'INSERT INTO t1 VALUES (@@max_sort_length)'; + SET STATEMENT max_sort_length=2048 FOR EXECUTE stmt; + thd->free_list contains a pointer to Item_int corresponding to 2048. + + If Prepared_statement::execute() notices that the table metadata for "t1" + has changed since PREPARE, it returns an error asking the calling + Prepared_statement::execute_loop() to re-prepare the statement. + Before returning the error, Prepared_statement::execute() + calls Prepared_statement::cleanup_stmt(), + which calls thd->cleanup_after_query(), + which calls Query_arena::free_items(). + + We hide "external" Items, e.g. those created while parsing the + "SET STATEMENT" part of the query, + so they don't get freed in case of re-prepare. + See MDEV-10702 Crash in SET STATEMENT FOR EXECUTE + */ + Item *free_list_backup= thd->free_list; + thd->free_list= NULL; // Hide the external (e.g. "SET STATEMENT") Items (void) stmt->execute_loop(&expanded_query, FALSE, NULL, NULL); + thd->free_items(); // Free items created by execute_loop() + /* + Now restore the "external" (e.g. "SET STATEMENT") Item list. + It will be freed normaly in THD::cleanup_after_query(). + */ + thd->free_list= free_list_backup; + stmt->lex->restore_set_statement_var(); DBUG_VOID_RETURN; } @@ -3853,9 +3882,14 @@ Prepared_statement::execute_loop(String *expanded_query, Reprepare_observer reprepare_observer; bool error; int reprepare_attempt= 0; -#ifndef DBUG_OFF - Item *free_list_state= thd->free_list; -#endif + + /* + - In mysql_sql_stmt_execute() we hide all "external" Items + e.g. those created in the "SET STATEMENT" part of the "EXECUTE" query. + - In case of mysqld_stmt_execute() there should not be "external" Items. + */ + DBUG_ASSERT(thd->free_list == NULL); + thd->select_number= select_number_after_prepare; /* Check if we got an error when sending long data */ if (state == Query_arena::STMT_ERROR) @@ -3877,12 +3911,8 @@ Prepared_statement::execute_loop(String *expanded_query, #endif reexecute: - /* - If the free_list is not empty, we'll wrongly free some externally - allocated items when cleaning up after validation of the prepared - statement. - */ - DBUG_ASSERT(thd->free_list == free_list_state); + // Make sure that reprepare() did not create any new Items. + DBUG_ASSERT(thd->free_list == NULL); /* Install the metadata observer. If some metadata version is diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 5dc50c92104..239e5b6b5d2 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -9172,9 +9172,26 @@ JOIN::make_simple_join(JOIN *parent, TABLE *temp_table) We need to destruct the copy_field (allocated in create_tmp_table()) before setting it to 0 if the join is not "reusable". */ - if (!tmp_join || tmp_join != this) - tmp_table_param.cleanup(); - tmp_table_param.copy_field= tmp_table_param.copy_field_end=0; + if (!tmp_join || tmp_join != this) + tmp_table_param.cleanup(); + else + { + /* + Free data buffered in copy_fields, but keep data pointed by copy_field + around for next iteration (possibly stored in save_copy_fields). + + It would be logically simpler to not clear copy_field + below, but as we have loops that runs over copy_field to + copy_field_end that should not be done anymore, it's simpler to + just clear the pointers. + + Another option would be to just clear copy_field_end and not run + the loops if this is not set or to have tmp_table_param.cleanup() + to run cleanup on save_copy_field if copy_field is not set. + */ + tmp_table_param.free_copy_field_data(); + tmp_table_param.copy_field= tmp_table_param.copy_field_end=0; + } first_record= sort_and_group=0; send_records= (ha_rows) 0; @@ -11890,7 +11907,7 @@ void JOIN::join_free() /** Free resources of given join. - @param fill true if we should free all resources, call with full==1 + @param full true if we should free all resources, call with full==1 should be last, before it this function can be called with full==0 @@ -12010,7 +12027,7 @@ void JOIN::cleanup(bool full) /* If we have tmp_join and 'this' JOIN is not tmp_join and tmp_table_param.copy_field's of them are equal then we have to remove - pointer to tmp_table_param.copy_field from tmp_join, because it qill + pointer to tmp_table_param.copy_field from tmp_join, because it will be removed in tmp_table_param.cleanup(). */ if (tmp_join && @@ -16114,6 +16131,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, case Item::VARBIN_ITEM: case Item::CACHE_ITEM: case Item::EXPR_CACHE_ITEM: + case Item::PARAM_ITEM: if (make_copy_field) { DBUG_ASSERT(((Item_result_field*)item)->result_field); @@ -22917,7 +22935,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, err: if (copy) delete [] param->copy_field; // This is never 0 - param->copy_field=0; + param->copy_field= 0; err2: DBUG_RETURN(TRUE); } diff --git a/sql/table_cache.cc b/sql/table_cache.cc index 2dd368a1945..16a47b37417 100644 --- a/sql/table_cache.cc +++ b/sql/table_cache.cc @@ -778,6 +778,8 @@ void tdc_release_share(TABLE_SHARE *share) mysql_mutex_lock(&share->tdc->LOCK_table_share); if (--share->tdc->ref_count) { + if (!share->is_view) + mysql_cond_broadcast(&share->tdc->COND_release); mysql_mutex_unlock(&share->tdc->LOCK_table_share); mysql_mutex_unlock(&LOCK_unused_shares); DBUG_VOID_RETURN; diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index 776bf4a3ab2..0deb19dfc77 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -36,6 +36,7 @@ #include <cstdlib> #include "log_event.h" #include <slave.h> +#include "sql_plugin.h" /* wsrep_plugins_pre_init() */ wsrep_t *wsrep = NULL; /* @@ -771,7 +772,6 @@ void wsrep_thr_init() mysql_mutex_init(key_LOCK_wsrep_config_state, &LOCK_wsrep_config_state, MY_MUTEX_INIT_FAST); } - void wsrep_init_startup (bool first) { if (wsrep_init()) unireg_abort(1); @@ -782,6 +782,17 @@ void wsrep_init_startup (bool first) wsrep_debug, wsrep_convert_LOCK_to_trx, (wsrep_on_fun)wsrep_on); + /* + Pre-initialize global_system_variables.table_plugin with a dummy engine + (placeholder) required during the initialization of wsrep threads (THDs). + (see: plugin_thdvar_init()) + Note: This only needs to be done for rsync & xtrabackup based SST methods. + In case of mysqldump SST method, the wsrep threads are created after the + server plugins & global system variables are initialized. + */ + if (wsrep_before_SE()) + wsrep_plugins_pre_init(); + /* Skip replication start if dummy wsrep provider is loaded */ if (!strcmp(wsrep_provider, WSREP_NONE)) return; diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc index d88263d75cb..7c366435472 100644 --- a/sql/wsrep_sst.cc +++ b/sql/wsrep_sst.cc @@ -476,13 +476,11 @@ static void* sst_joiner_thread (void* a) } else { // Scan state ID first followed by wsrep_gtid_domain_id. - char uuid[512]; unsigned long int domain_id; - size_t len= pos - out + 1; - if (len > sizeof(uuid)) goto err; // safety check - memcpy(uuid, out, len); // including '\0' - err= sst_scan_uuid_seqno (uuid, &ret_uuid, &ret_seqno); + // Null-terminate the state-id. + out[pos - out]= 0; + err= sst_scan_uuid_seqno (out, &ret_uuid, &ret_seqno); if (err) { |