diff options
Diffstat (limited to 'sql')
58 files changed, 1578 insertions, 592 deletions
diff --git a/sql/create_options.cc b/sql/create_options.cc index 5adcb2f1e9e..a8d997efaf4 100644 --- a/sql/create_options.cc +++ b/sql/create_options.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2010, 2017, MariaDB Corporation Ab +/* Copyright (C) 2010, 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 @@ -132,7 +132,8 @@ static bool set_one_value(ha_create_table_option *opt, switch (opt->type) { case HA_OPTION_TYPE_SYSVAR: - DBUG_ASSERT(0); // HA_OPTION_TYPE_SYSVAR's are replaced in resolve_sysvars() + // HA_OPTION_TYPE_SYSVAR's are replaced in resolve_sysvars() + break; // to DBUG_ASSERT(0) case HA_OPTION_TYPE_ULL: { ulonglong *val= (ulonglong*)value_ptr(base, opt); diff --git a/sql/field.cc b/sql/field.cc index dbd9a30b244..be97ceeb227 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -7314,6 +7314,28 @@ void Field_string::sql_type(String &res) const res.append(STRING_WITH_LEN(" binary")); } +/** + For fields which are associated with character sets their length is provided + in octets and their character set information is also provided as part of + type information. + + @param res String which contains filed type and length. +*/ +void Field_string::sql_rpl_type(String *res) const +{ + CHARSET_INFO *cs=charset(); + if (Field_string::has_charset()) + { + size_t length= cs->cset->snprintf(cs, (char*) res->ptr(), + res->alloced_length(), + "char(%u octets) character set %s", + field_length, + charset()->csname); + res->length(length); + } + else + Field_string::sql_type(*res); + } uchar *Field_string::pack(uchar *to, const uchar *from, uint max_length) { @@ -7754,6 +7776,29 @@ void Field_varstring::sql_type(String &res) const res.append(STRING_WITH_LEN(" binary")); } +/** + For fields which are associated with character sets their length is provided + in octets and their character set information is also provided as part of + type information. + + @param res String which contains filed type and length. +*/ +void Field_varstring::sql_rpl_type(String *res) const +{ + CHARSET_INFO *cs=charset(); + if (Field_varstring::has_charset()) + { + size_t length= cs->cset->snprintf(cs, (char*) res->ptr(), + res->alloced_length(), + "varchar(%u octets) character set %s", + field_length, + charset()->csname); + res->length(length); + } + else + Field_varstring::sql_type(*res); +} + uint32 Field_varstring::data_length() { diff --git a/sql/field.h b/sql/field.h index 58bac152ea2..5ae8838c303 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1,7 +1,7 @@ #ifndef FIELD_INCLUDED #define FIELD_INCLUDED /* Copyright (c) 2000, 2015, Oracle and/or its affiliates. - Copyright (c) 2008, 2017, MariaDB Corporation. + Copyright (c) 2008, 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 @@ -579,6 +579,7 @@ public: /* Flag indicating that the field is physically stored in the database */ bool stored_in_db; bool utf8; /* Already in utf8 */ + bool automatic_name; Item *expr; Lex_ident name; /* Name of constraint */ /* see VCOL_* (VCOL_FIELD_REF, ...) */ @@ -588,7 +589,7 @@ public: :Type_handler_hybrid_field_type(&type_handler_null), vcol_type((enum_vcol_info_type)VCOL_TYPE_NONE), in_partitioning_expr(FALSE), stored_in_db(FALSE), - utf8(TRUE), expr(NULL), flags(0) + utf8(TRUE), automatic_name(FALSE), expr(NULL), flags(0) { name.str= NULL; name.length= 0; @@ -1198,6 +1199,7 @@ public: in str and restore it with set() if needed */ virtual void sql_type(String &str) const =0; + virtual void sql_rpl_type(String *str) const { sql_type(*str); } virtual uint size_of() const =0; // For new field inline bool is_null(my_ptrdiff_t row_offset= 0) const { @@ -3700,6 +3702,7 @@ public: st->m_fixed_string_total_length+= pack_length(); } void sql_type(String &str) const; + void sql_rpl_type(String*) const; bool is_equal(const Column_definition &new_field) const; bool can_be_converted_by_engine(const Column_definition &new_type) const { @@ -3822,6 +3825,7 @@ public: uint get_key_image(uchar *buff,uint length, imagetype type); void set_key_image(const uchar *buff,uint length); void sql_type(String &str) const; + void sql_rpl_type(String*) const; virtual uchar *pack(uchar *to, const uchar *from, uint max_length); virtual const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end, uint param_data); diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index b19e86c27a6..45ea9eb1552 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -8303,6 +8303,7 @@ int ha_partition::info(uint flag) ulonglong max_records= 0; uint32 i= 0; uint32 handler_instance= 0; + bool handler_instance_set= 0; file_array= m_file; do @@ -8315,8 +8316,9 @@ int ha_partition::info(uint flag) !bitmap_is_set(&(m_part_info->read_partitions), (uint) (file_array - m_file))) file->info(HA_STATUS_VARIABLE | no_lock_flag | extra_var_flag); - if (file->stats.records > max_records) + if (file->stats.records > max_records || !handler_instance_set) { + handler_instance_set= 1; max_records= file->stats.records; handler_instance= i; } diff --git a/sql/handler.cc b/sql/handler.cc index 5f2a1a573ba..5f0c8e0b5ec 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -2026,9 +2026,9 @@ static char* xid_to_str(char *buf, XID *xid) } #endif -#ifdef WITH_WSREP static my_xid wsrep_order_and_check_continuity(XID *list, int len) { +#ifdef WITH_WSREP wsrep_sort_xid_array(list, len); wsrep::gtid cur_position= wsrep_get_SE_checkpoint(); long long cur_seqno= cur_position.seqno().get(); @@ -2046,8 +2046,10 @@ static my_xid wsrep_order_and_check_continuity(XID *list, int len) } WSREP_INFO("Last wsrep seqno to be recovered %lld", cur_seqno); return (cur_seqno < 0 ? 0 : cur_seqno); -} +#else + return 0; #endif /* WITH_WSREP */ +} /** recover() step of xa. @@ -2085,7 +2087,6 @@ static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin, { sql_print_information("Found %d prepared transaction(s) in %s", got, hton_name(hton)->str); -#ifdef WITH_WSREP /* If wsrep_on=ON, XIDs are first ordered and then the range of recovered XIDs is checked for continuity. All the XIDs which are in continuous range can be safely committed if binlog @@ -2101,12 +2102,10 @@ static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin, crashes after T2 finishes prepare step but before T1 starts the prepare. */ - my_xid wsrep_limit= 0; + my_xid wsrep_limit __attribute__((unused))= 0; if (WSREP_ON) - { wsrep_limit= wsrep_order_and_check_continuity(info->list, got); - } -#endif /* WITH_WSREP */ + for (int i=0; i < got; i ++) { my_xid x= IF_WSREP(WSREP_ON && wsrep_is_wsrep_xid(&info->list[i]) ? @@ -2115,10 +2114,10 @@ static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin, info->list[i].get_my_xid()); if (!x) // not "mine" - that is generated by external TM { -#ifndef DBUG_OFF - char buf[XIDDATASIZE*4+6]; // see xid_to_str - DBUG_PRINT("info", ("ignore xid %s", xid_to_str(buf, info->list+i))); -#endif + DBUG_EXECUTE("info",{ + char buf[XIDDATASIZE*4+6]; + _db_doprnt_("ignore xid %s", xid_to_str(buf, info->list+i)); + }); xid_cache_insert(info->list + i); info->found_foreign_xids++; continue; @@ -2139,32 +2138,25 @@ static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin, my_hash_search(info->commit_list, (uchar *)&x, sizeof(x)) != 0 : tc_heuristic_recover == TC_HEURISTIC_RECOVER_COMMIT)) { -#ifndef DBUG_OFF - int rc= -#endif - hton->commit_by_xid(hton, info->list+i); -#ifndef DBUG_OFF + int rc= hton->commit_by_xid(hton, info->list+i); if (rc == 0) { - char buf[XIDDATASIZE*4+6]; // see xid_to_str - DBUG_PRINT("info", ("commit xid %s", xid_to_str(buf, info->list+i))); + DBUG_EXECUTE("info",{ + char buf[XIDDATASIZE*4+6]; + _db_doprnt_("commit xid %s", xid_to_str(buf, info->list+i)); + }); } -#endif } else { -#ifndef DBUG_OFF - int rc= -#endif - hton->rollback_by_xid(hton, info->list+i); -#ifndef DBUG_OFF + int rc= hton->rollback_by_xid(hton, info->list+i); if (rc == 0) { - char buf[XIDDATASIZE*4+6]; // see xid_to_str - DBUG_PRINT("info", ("rollback xid %s", - xid_to_str(buf, info->list+i))); + DBUG_EXECUTE("info",{ + char buf[XIDDATASIZE*4+6]; + _db_doprnt_("rollback xid %s", xid_to_str(buf, info->list+i)); + }); } -#endif } } if (got < info->len) diff --git a/sql/item.cc b/sql/item.cc index 98bc56c9025..7d1c71901f2 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -3782,6 +3782,20 @@ my_decimal *Item_null::val_decimal(my_decimal *decimal_value) } +longlong Item_null::val_datetime_packed(THD *) +{ + null_value= true; + return 0; +} + + +longlong Item_null::val_time_packed(THD *) +{ + null_value= true; + return 0; +} + + bool Item_null::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { set_zero_time(ltime, MYSQL_TIMESTAMP_NONE); @@ -8223,6 +8237,24 @@ bool Item_ref::val_native(THD *thd, Native *to) } +longlong Item_ref::val_datetime_packed(THD *thd) +{ + DBUG_ASSERT(fixed); + longlong tmp= (*ref)->val_datetime_packed(thd); + null_value= (*ref)->null_value; + return tmp; +} + + +longlong Item_ref::val_time_packed(THD *thd) +{ + DBUG_ASSERT(fixed); + longlong tmp= (*ref)->val_time_packed(thd); + null_value= (*ref)->null_value; + return tmp; +} + + my_decimal *Item_ref::val_decimal(my_decimal *decimal_value) { my_decimal *val= (*ref)->val_decimal_result(decimal_value); diff --git a/sql/item.h b/sql/item.h index 6e1f1e3891e..b9a0287482e 100644 --- a/sql/item.h +++ b/sql/item.h @@ -767,7 +767,7 @@ public: /* Cache of the result of is_expensive(). */ int8 is_expensive_cache; - /* Reuse size, only used by SP local variable assignment, otherwize 0 */ + /* Reuse size, only used by SP local variable assignment, otherwise 0 */ uint rsize; protected: @@ -3556,6 +3556,8 @@ public: String *val_str(String *str); my_decimal *val_decimal(my_decimal *); bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + longlong val_datetime_packed(THD *); + longlong val_time_packed(THD *); int save_in_field(Field *field, bool no_conversions); int save_safe_in_field(Field *field); bool send(Protocol *protocol, st_value *buffer); @@ -5140,6 +5142,8 @@ public: bool val_native(THD *thd, Native *to); bool is_null(); bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + longlong val_datetime_packed(THD *); + longlong val_time_packed(THD *); double val_result(); longlong val_int_result(); String *str_result(String* tmp); diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index c9f4d731318..a311c32c5a6 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -132,6 +132,9 @@ public: int compare_e_json_str(); int compare_e_str_json(); + void min_max_update_field_native(THD *thd, Field *field, Item *item, + int cmp_sign); + Item** cache_converted_constant(THD *thd, Item **value, Item **cache, const Type_handler *type); inline bool is_owner_equal_func() diff --git a/sql/item_func.cc b/sql/item_func.cc index af908d9d553..eaf6113e10e 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2350,6 +2350,42 @@ void Item_func_round::fix_arg_double() } +void Item_func_round::fix_arg_temporal(const Type_handler *h, + uint int_part_length) +{ + set_handler(h); + if (args[1]->const_item() && !args[1]->is_expensive()) + { + Longlong_hybrid_null dec= args[1]->to_longlong_hybrid_null(); + fix_attributes_temporal(int_part_length, + dec.is_null() ? args[0]->decimals : + dec.to_uint(TIME_SECOND_PART_DIGITS)); + } + else + fix_attributes_temporal(int_part_length, args[0]->decimals); +} + + +void Item_func_round::fix_arg_time() +{ + fix_arg_temporal(&type_handler_time2, MIN_TIME_WIDTH); +} + + +void Item_func_round::fix_arg_datetime() +{ + /* + Day increment operations are not supported for '0000-00-00', + see get_date_from_daynr() for details. Therefore, expressions like + ROUND('0000-00-00 23:59:59.999999') + return NULL. + */ + if (!truncate) + maybe_null= true; + fix_arg_temporal(&type_handler_datetime2, MAX_DATETIME_WIDTH); +} + + void Item_func_round::fix_arg_int() { if (args[1]->const_item()) @@ -2489,6 +2525,36 @@ my_decimal *Item_func_round::decimal_op(my_decimal *decimal_value) } +bool Item_func_round::time_op(THD *thd, MYSQL_TIME *to) +{ + DBUG_ASSERT(args[0]->type_handler()->mysql_timestamp_type() == + MYSQL_TIMESTAMP_TIME); + Time::Options opt(Time::default_flags_for_get_date(), + truncate ? TIME_FRAC_TRUNCATE : TIME_FRAC_ROUND, + Time::DATETIME_TO_TIME_DISALLOW); + Longlong_hybrid_null dec= args[1]->to_longlong_hybrid_null(); + Time *tm= new (to) Time(thd, args[0], opt, + dec.to_uint(TIME_SECOND_PART_DIGITS)); + null_value= !tm->is_valid_time() || dec.is_null(); + DBUG_ASSERT(maybe_null || !null_value); + return null_value; +} + + +bool Item_func_round::date_op(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate) +{ + DBUG_ASSERT(args[0]->type_handler()->mysql_timestamp_type() == + MYSQL_TIMESTAMP_DATETIME); + Datetime::Options opt(thd, truncate ? TIME_FRAC_TRUNCATE : TIME_FRAC_ROUND); + Longlong_hybrid_null dec= args[1]->to_longlong_hybrid_null(); + Datetime *tm= new (to) Datetime(thd, args[0], opt, + dec.to_uint(TIME_SECOND_PART_DIGITS)); + null_value= !tm->is_valid_datetime() || dec.is_null(); + DBUG_ASSERT(maybe_null || !null_value); + return null_value; +} + + void Item_func_rand::seed_random(Item *arg) { /* diff --git a/sql/item_func.h b/sql/item_func.h index 22832543242..f1212033b6c 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1715,21 +1715,36 @@ public: /* This handles round and truncate */ -class Item_func_round :public Item_func_numhybrid +class Item_func_round :public Item_func_hybrid_field_type { bool truncate; void fix_length_and_dec_decimal(uint decimals_to_set); void fix_length_and_dec_double(uint decimals_to_set); public: Item_func_round(THD *thd, Item *a, Item *b, bool trunc_arg) - :Item_func_numhybrid(thd, a, b), truncate(trunc_arg) {} + :Item_func_hybrid_field_type(thd, a, b), truncate(trunc_arg) {} const char *func_name() const { return truncate ? "truncate" : "round"; } double real_op(); longlong int_op(); my_decimal *decimal_op(my_decimal *); + bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + bool time_op(THD *thd, MYSQL_TIME *ltime); + bool native_op(THD *thd, Native *to) + { + DBUG_ASSERT(0); + return true; + } + String *str_op(String *str) + { + DBUG_ASSERT(0); + return NULL; + } void fix_arg_decimal(); void fix_arg_int(); void fix_arg_double(); + void fix_arg_time(); + void fix_arg_datetime(); + void fix_arg_temporal(const Type_handler *h, uint int_part_length); bool fix_length_and_dec() { return args[0]->type_handler()->Item_func_round_fix_length_and_dec(this); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 87f3e1d3345..5ff31898a0d 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -3089,18 +3089,32 @@ void Item_sum_min_max::update_field() tmp_item= args[0]; args[0]= direct_item; } - switch (result_type()) { - case STRING_RESULT: - min_max_update_str_field(); - break; - case INT_RESULT: - min_max_update_int_field(); - break; - case DECIMAL_RESULT: - min_max_update_decimal_field(); - break; - default: - min_max_update_real_field(); + if (Item_sum_min_max::type_handler()->is_val_native_ready()) + { + /* + TODO-10.5: change Item_sum_min_max to use val_native() for all data types + - make all type handlers val_native() ready + - use min_max_update_native_field() for all data types + - remove Item_sum_min_max::min_max_update_{str|real|int|decimal}_field() + */ + min_max_update_native_field(); + } + else + { + switch (Item_sum_min_max::type_handler()->cmp_type()) { + case STRING_RESULT: + case TIME_RESULT: + min_max_update_str_field(); + break; + case INT_RESULT: + min_max_update_int_field(); + break; + case DECIMAL_RESULT: + min_max_update_decimal_field(); + break; + default: + min_max_update_real_field(); + } } if (unlikely(direct_added)) { @@ -3111,6 +3125,40 @@ void Item_sum_min_max::update_field() } +void Arg_comparator::min_max_update_field_native(THD *thd, + Field *field, + Item *item, + int cmp_sign) +{ + DBUG_ENTER("Arg_comparator::min_max_update_field_native"); + if (!item->val_native(current_thd, &m_native2)) + { + if (field->is_null()) + field->store_native(m_native2); // The first non-null value + else + { + field->val_native(&m_native1); + if ((cmp_sign * m_compare_handler->cmp_native(m_native2, m_native1)) < 0) + field->store_native(m_native2); + } + field->set_notnull(); + } + DBUG_VOID_RETURN; +} + + +void +Item_sum_min_max::min_max_update_native_field() +{ + DBUG_ENTER("Item_sum_min_max::min_max_update_native_field"); + DBUG_ASSERT(cmp); + DBUG_ASSERT(type_handler_for_comparison() == cmp->compare_type_handler()); + THD *thd= current_thd; + cmp->min_max_update_field_native(thd, result_field, args[0], cmp_sign); + DBUG_VOID_RETURN; +} + + void Item_sum_min_max::min_max_update_str_field() { diff --git a/sql/item_sum.h b/sql/item_sum.h index 16cc8d131b8..50c7b524d6c 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -1117,6 +1117,7 @@ public: void min_max_update_real_field(); void min_max_update_int_field(); void min_max_update_decimal_field(); + void min_max_update_native_field(); void cleanup(); bool any_value() { return was_values; } void no_rows_in_result(); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 689258158a1..d621a3be1cd 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1590,7 +1590,7 @@ static my_bool kill_thread_phase_2(THD *thd, void *) /* associated with the kill thread phase 1 */ static my_bool warn_threads_active_after_phase_1(THD *thd, void *) { - if (!thd->is_binlog_dump_thread()) + if (!thd->is_binlog_dump_thread() && thd->vio_ok()) sql_print_warning("%s: Thread %llu (user : '%s') did not exit\n", my_progname, (ulonglong) thd->thread_id, (thd->main_security_ctx.user ? @@ -6493,6 +6493,10 @@ struct my_option my_long_options[]= 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #endif /* HAVE_REPLICATION */ #ifndef DBUG_OFF + {"debug-assert", 0, + "Allow DBUG_ASSERT() to invoke assert()", + &my_assert, &my_assert, + 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, {"debug-assert-on-error", 0, "Do an assert in various functions if we get a fatal error", &my_assert_on_error, &my_assert_on_error, diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index f00d0ed019d..de999559b8c 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -2192,12 +2192,15 @@ int pull_out_semijoin_tables(JOIN *join) TABLE_LIST *sj_nest; DBUG_ENTER("pull_out_semijoin_tables"); List_iterator<TABLE_LIST> sj_list_it(join->select_lex->sj_nests); - + /* Try pulling out of the each of the semi-joins */ while ((sj_nest= sj_list_it++)) { List_iterator<TABLE_LIST> child_li(sj_nest->nested_join->join_list); TABLE_LIST *tbl; + Json_writer_object trace_wrapper(join->thd); + Json_writer_object trace(join->thd, "semijoin_table_pullout"); + Json_writer_array trace_arr(join->thd, "pulled_out_tables"); /* Don't do table pull-out for nested joins (if we get nested joins here, it @@ -2296,7 +2299,8 @@ int pull_out_semijoin_tables(JOIN *join) pulled_a_table= TRUE; pulled_tables |= tbl->table->map; DBUG_PRINT("info", ("Table %s pulled out (reason: func dep)", - tbl->table->alias.c_ptr())); + tbl->table->alias.c_ptr_safe())); + trace_arr.add(tbl->table->alias.c_ptr_safe()); /* Pulling a table out of uncorrelated subquery in general makes makes it correlated. See the NOTE to this funtion. @@ -2456,7 +2460,7 @@ bool optimize_semijoin_nests(JOIN *join, table_map all_table_map) &subjoin_out_rows); sjm->materialization_cost.convert_from_cost(subjoin_read_time); - sjm->rows= subjoin_out_rows; + sjm->rows_with_duplicates= sjm->rows= subjoin_out_rows; // Don't use the following list because it has "stale" items. use // ref_pointer_array instead: @@ -2778,27 +2782,30 @@ void advance_sj_state(JOIN *join, table_map remaining_tables, uint idx, { POSITION *pos= join->positions + idx; const JOIN_TAB *new_join_tab= pos->table; - Semi_join_strategy_picker *pickers[]= - { - &pos->firstmatch_picker, - &pos->loosescan_picker, - &pos->sjmat_picker, - &pos->dups_weedout_picker, - NULL, - }; - - if (join->emb_sjm_nest) + if (join->emb_sjm_nest || //(1) + !join->select_lex->have_merged_subqueries) //(2) { /* - We're performing optimization inside SJ-Materialization nest: + (1): We're performing optimization inside SJ-Materialization nest: - there are no other semi-joins inside semi-join nests - attempts to build semi-join strategies here will confuse the optimizer, so bail out. + (2): Don't waste time on semi-join optimizations if we don't have any + semi-joins */ pos->sj_strategy= SJ_OPT_NONE; return; } + Semi_join_strategy_picker *pickers[]= + { + &pos->firstmatch_picker, + &pos->loosescan_picker, + &pos->sjmat_picker, + &pos->dups_weedout_picker, + NULL, + }; + Json_writer_array trace_steps(join->thd, "semijoin_strategy_choice"); /* Update join->cur_sj_inner_tables (Used by FirstMatch in this function and LooseScan detector in best_access_path) @@ -2897,6 +2904,7 @@ void advance_sj_state(JOIN *join, table_map remaining_tables, uint idx, *current_read_time= read_time; *current_record_count= rec_count; dups_producing_tables &= ~handled_fanout; + //TODO: update bitmap of semi-joins that were handled together with // others. if (is_multiple_semi_joins(join, join->positions, idx, @@ -2924,6 +2932,33 @@ void advance_sj_state(JOIN *join, table_map remaining_tables, uint idx, } } } + + if (unlikely(join->thd->trace_started() && pos->sj_strategy != SJ_OPT_NONE)) + { + Json_writer_object tr(join->thd); + const char *sname; + switch (pos->sj_strategy) { + case SJ_OPT_MATERIALIZE: + sname= "SJ-Materialize"; + break; + case SJ_OPT_MATERIALIZE_SCAN: + sname= "SJ-Materialize-Scan"; + break; + case SJ_OPT_FIRST_MATCH: + sname= "FirstMatch"; + break; + case SJ_OPT_DUPS_WEEDOUT: + sname= "DuplicateWeedout"; + break; + case SJ_OPT_LOOSE_SCAN: + sname= "LooseScan"; + break; + default: + DBUG_ASSERT(0); + sname="Invalid"; + } + tr.add("chosen_strategy", sname); + } } if ((emb_sj_nest= new_join_tab->emb_sj_nest)) @@ -3000,6 +3035,8 @@ bool Sj_materialization_picker::check_qep(JOIN *join, } else { + Json_writer_object trace(join->thd); + trace.add("strategy", "SJ-Materialization"); /* This is SJ-Materialization with lookups */ Cost_estimate prefix_cost; signed int first_tab= (int)idx - mat_info->tables; @@ -3032,6 +3069,11 @@ bool Sj_materialization_picker::check_qep(JOIN *join, *record_count= prefix_rec_count; *handled_fanout= new_join_tab->emb_sj_nest->sj_inner_tables; *strategy= SJ_OPT_MATERIALIZE; + if (unlikely(join->thd->trace_started())) + { + trace.add("records", *record_count); + trace.add("read_time", *read_time); + } return TRUE; } } @@ -3040,6 +3082,8 @@ bool Sj_materialization_picker::check_qep(JOIN *join, if (sjm_scan_need_tables && /* Have SJM-Scan prefix */ !(sjm_scan_need_tables & remaining_tables)) { + Json_writer_object trace(join->thd); + trace.add("strategy", "SJ-Materialization-Scan"); TABLE_LIST *mat_nest= join->positions[sjm_scan_last_inner].table->emb_sj_nest; SJ_MATERIALIZATION_INFO *mat_info= mat_nest->sj_mat_info; @@ -3082,12 +3126,20 @@ bool Sj_materialization_picker::check_qep(JOIN *join, disable_jbuf, prefix_rec_count, &curpos, &dummy); prefix_rec_count= COST_MULT(prefix_rec_count, curpos.records_read); prefix_cost= COST_ADD(prefix_cost, curpos.read_time); + prefix_cost= COST_ADD(prefix_cost, + prefix_rec_count / (double) TIME_FOR_COMPARE); + //TODO: take into account join condition selectivity here } *strategy= SJ_OPT_MATERIALIZE_SCAN; *read_time= prefix_cost; - *record_count= prefix_rec_count; + *record_count= prefix_rec_count / mat_info->rows_with_duplicates; *handled_fanout= mat_nest->sj_inner_tables; + if (unlikely(join->thd->trace_started())) + { + trace.add("records", *record_count); + trace.add("read_time", *read_time); + } return TRUE; } return FALSE; @@ -3151,6 +3203,8 @@ bool LooseScan_picker::check_qep(JOIN *join, !(remaining_tables & loosescan_need_tables) && (new_join_tab->table->map & loosescan_need_tables)) { + Json_writer_object trace(join->thd); + trace.add("strategy", "SJ-Materialization-Scan"); /* Ok we have LooseScan plan and also have all LooseScan sj-nest's inner tables and outer correlated tables into the prefix. @@ -3181,6 +3235,11 @@ bool LooseScan_picker::check_qep(JOIN *join, */ *strategy= SJ_OPT_LOOSE_SCAN; *handled_fanout= first->table->emb_sj_nest->sj_inner_tables; + if (unlikely(join->thd->trace_started())) + { + trace.add("records", *record_count); + trace.add("read_time", *read_time); + } return TRUE; } return FALSE; @@ -3260,6 +3319,8 @@ bool Firstmatch_picker::check_qep(JOIN *join, if (in_firstmatch_prefix() && !(firstmatch_need_tables & remaining_tables)) { + Json_writer_object trace(join->thd); + trace.add("strategy", "FirstMatch"); /* Got a complete FirstMatch range. Calculate correct costs and fanout */ @@ -3292,6 +3353,11 @@ bool Firstmatch_picker::check_qep(JOIN *join, *handled_fanout= firstmatch_need_tables; /* *record_count and *read_time were set by the above call */ *strategy= SJ_OPT_FIRST_MATCH; + if (unlikely(join->thd->trace_started())) + { + trace.add("records", *record_count); + trace.add("read_time", *read_time); + } return TRUE; } } @@ -3370,6 +3436,8 @@ bool Duplicate_weedout_picker::check_qep(JOIN *join, double sj_inner_fanout= 1.0; double sj_outer_fanout= 1.0; uint temptable_rec_size; + Json_writer_object trace(join->thd); + trace.add("strategy", "DuplicateWeedout"); if (first_tab == join->const_tables) { prefix_rec_count= 1.0; @@ -3430,6 +3498,11 @@ bool Duplicate_weedout_picker::check_qep(JOIN *join, *record_count= prefix_rec_count * sj_outer_fanout; *handled_fanout= dups_removed_fanout; *strategy= SJ_OPT_DUPS_WEEDOUT; + if (unlikely(join->thd->trace_started())) + { + trace.add("records", *record_count); + trace.add("read_time", *read_time); + } return TRUE; } return FALSE; @@ -3660,7 +3733,7 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join) join->best_positions[first].n_sj_tables= sjm->tables; join->best_positions[first].sj_strategy= SJ_OPT_MATERIALIZE; Json_writer_object semijoin_strategy(thd); - semijoin_strategy.add("semi_join_strategy","sj_materialize"); + semijoin_strategy.add("semi_join_strategy","SJ-Materialization"); Json_writer_array semijoin_plan(thd, "join_order"); for (uint i= first; i < first+ sjm->tables; i++) { @@ -3709,7 +3782,7 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join) POSITION dummy; join->cur_sj_inner_tables= 0; Json_writer_object semijoin_strategy(thd); - semijoin_strategy.add("semi_join_strategy","sj_materialize_scan"); + semijoin_strategy.add("semi_join_strategy","SJ-Materialization-Scan"); Json_writer_array semijoin_plan(thd, "join_order"); for (i= first + sjm->tables; i <= tablenr; i++) { @@ -3747,7 +3820,7 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join) */ join->cur_sj_inner_tables= 0; Json_writer_object semijoin_strategy(thd); - semijoin_strategy.add("semi_join_strategy","firstmatch"); + semijoin_strategy.add("semi_join_strategy","FirstMatch"); Json_writer_array semijoin_plan(thd, "join_order"); for (idx= first; idx <= tablenr; idx++) { @@ -3785,7 +3858,7 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join) */ join->cur_sj_inner_tables= 0; Json_writer_object semijoin_strategy(thd); - semijoin_strategy.add("semi_join_strategy","sj_materialize"); + semijoin_strategy.add("semi_join_strategy","LooseScan"); Json_writer_array semijoin_plan(thd, "join_order"); for (idx= first; idx <= tablenr; idx++) { diff --git a/sql/opt_subselect.h b/sql/opt_subselect.h index 65131f6bc89..6210fc972c8 100644 --- a/sql/opt_subselect.h +++ b/sql/opt_subselect.h @@ -96,15 +96,14 @@ public: Loose_scan_opt(): try_loosescan(FALSE), bound_sj_equalities(0), - quick_uses_applicable_index(FALSE) + quick_uses_applicable_index(0), + quick_max_loose_keypart(0), + best_loose_scan_key(0), + best_loose_scan_cost(0), + best_loose_scan_records(0), + best_loose_scan_start_key(NULL), + best_max_loose_keypart(0) { - /* Protected by quick_uses_applicable_index */ - LINT_INIT(quick_max_loose_keypart); - /* The following are protected by best_loose_scan_cost!= DBL_MAX */ - LINT_INIT(best_loose_scan_key); - LINT_INIT(best_loose_scan_records); - LINT_INIT(best_max_loose_keypart); - LINT_INIT(best_loose_scan_start_key); } void init(JOIN *join, JOIN_TAB *s, table_map remaining_tables) diff --git a/sql/partition_element.h b/sql/partition_element.h index a3eb6953be1..ff0d0d59fc4 100644 --- a/sql/partition_element.h +++ b/sql/partition_element.h @@ -98,7 +98,7 @@ enum stat_trx_field class partition_element :public Sql_alloc { public: - enum elem_type + enum elem_type_enum { CONVENTIONAL= 0, CURRENT, @@ -125,19 +125,7 @@ public: bool max_value; // MAXVALUE range uint32 id; bool empty; - - // TODO: subclass partition_element by partitioning type to avoid such semantic - // mixup - elem_type type() - { - return (elem_type)(int(signed_flag) << 1 | int(max_value)); - } - - void type(elem_type val) - { - max_value= (bool)(val & 1); - signed_flag= (bool)(val & 2); - } + elem_type_enum type; partition_element() : part_max_rows(0), part_min_rows(0), range_value(0), @@ -148,7 +136,8 @@ public: nodegroup_id(UNDEF_NODEGROUP), has_null_value(FALSE), signed_flag(FALSE), max_value(FALSE), id(UINT_MAX32), - empty(true) + empty(true), + type(CONVENTIONAL) {} partition_element(partition_element *part_elem) : part_max_rows(part_elem->part_max_rows), @@ -164,13 +153,13 @@ public: nodegroup_id(part_elem->nodegroup_id), has_null_value(FALSE), id(part_elem->id), - empty(part_elem->empty) + empty(part_elem->empty), + type(CONVENTIONAL) {} ~partition_element() {} part_column_list_val& get_col_val(uint idx) { - DBUG_ASSERT(type() == CONVENTIONAL || list_val_list.elements == 1); part_elem_value *ev= list_val_list.head(); DBUG_ASSERT(ev); DBUG_ASSERT(ev->col_val_array); diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 66216493de8..38e085b3be9 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -894,15 +894,16 @@ bool partition_info::vers_setup_expression(THD * thd, uint32 alter_add) DBUG_ASSERT(part_type == VERSIONING_PARTITION); DBUG_ASSERT(table->versioned(VERS_TIMESTAMP)); - DBUG_ASSERT(num_columns == 1); if (!alter_add) { Field *row_end= table->vers_end_field(); - part_field_list.push_back(row_end->field_name.str, thd->mem_root); - DBUG_ASSERT(part_field_list.elements == 1); // needed in handle_list_of_fields() row_end->flags|= GET_FIXED_FIELDS_FLAG; + Name_resolution_context *context= &thd->lex->current_select->context; + Item *row_end_item= new (thd->mem_root) Item_field(thd, context, row_end); + Item *row_end_ts= new (thd->mem_root) Item_func_unix_timestamp(thd, row_end_item); + set_part_expr(thd, row_end_ts, false); } if (alter_add) @@ -911,12 +912,12 @@ bool partition_info::vers_setup_expression(THD * thd, uint32 alter_add) partition_element *el; for(uint32 id= 0; ((el= it++)); id++) { - DBUG_ASSERT(el->type() != partition_element::CONVENTIONAL); + DBUG_ASSERT(el->type != partition_element::CONVENTIONAL); /* Newly added element is inserted before AS_OF_NOW. */ - if (el->id == UINT_MAX32 || el->type() == partition_element::CURRENT) + if (el->id == UINT_MAX32 || el->type == partition_element::CURRENT) { el->id= id; - if (el->type() == partition_element::CURRENT) + if (el->type == partition_element::CURRENT) break; } } @@ -1343,13 +1344,13 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, } if (part_type == VERSIONING_PARTITION) { - if (part_elem->type() == partition_element::HISTORY) + if (part_elem->type == partition_element::HISTORY) { hist_parts++; } else { - DBUG_ASSERT(part_elem->type() == partition_element::CURRENT); + DBUG_ASSERT(part_elem->type == partition_element::CURRENT); now_parts++; } } @@ -1473,15 +1474,8 @@ void partition_info::print_no_partition_found(TABLE *table_arg, myf errflag) FALSE Success */ -bool partition_info::set_part_expr(THD *thd, char *start_token, Item *item_ptr, - char *end_token, bool is_subpart) +bool partition_info::set_part_expr(THD *thd, Item *item_ptr, bool is_subpart) { - size_t expr_len= end_token - start_token; - char *func_string= (char*) thd->memdup(start_token, expr_len); - - if (unlikely(!func_string)) - return TRUE; - if (is_subpart) { list_of_subpart_fields= FALSE; @@ -2650,12 +2644,9 @@ part_column_list_val *partition_info::add_column_value(THD *thd) return NULL; } -bool partition_info::set_part_expr(THD *thd, char *start_token, Item *item_ptr, - char *end_token, bool is_subpart) +bool partition_info::set_part_expr(THD *thd, Item *item_ptr, bool is_subpart) { - (void)start_token; (void)item_ptr; - (void)end_token; (void)is_subpart; return FALSE; } @@ -2693,9 +2684,8 @@ bool check_partition_dirs(partition_info *part_info) bool partition_info::vers_init_info(THD * thd) { part_type= VERSIONING_PARTITION; - list_of_part_fields= TRUE; - column_list= TRUE; - num_columns= 1; + list_of_part_fields= true; + column_list= false; vers_info= new (thd->mem_root) Vers_part_info; if (unlikely(!vers_info)) return true; diff --git a/sql/partition_info.h b/sql/partition_info.h index a2320c34048..00ef815ce09 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -55,11 +55,11 @@ struct Vers_part_info : public Sql_alloc if (now_part) { DBUG_ASSERT(now_part->id != UINT_MAX32); - DBUG_ASSERT(now_part->type() == partition_element::CURRENT); + DBUG_ASSERT(now_part->type == partition_element::CURRENT); if (hist_part) { DBUG_ASSERT(hist_part->id != UINT_MAX32); - DBUG_ASSERT(hist_part->type() == partition_element::HISTORY); + DBUG_ASSERT(hist_part->type == partition_element::HISTORY); } return true; } @@ -366,8 +366,7 @@ public: void init_col_val(part_column_list_val *col_val, Item *item); int reorganize_into_single_field_col_val(THD *thd); part_column_list_val *add_column_value(THD *thd); - bool set_part_expr(THD *thd, char *start_token, Item *item_ptr, - char *end_token, bool is_subpart); + bool set_part_expr(THD *thd, Item *item_ptr, bool is_subpart); bool set_up_charset_field_preps(THD *thd); bool check_partition_field_length(); bool init_column_part(THD *thd); diff --git a/sql/rpl_utility_server.cc b/sql/rpl_utility_server.cc index de088be6434..e58c9cf018e 100644 --- a/sql/rpl_utility_server.cc +++ b/sql/rpl_utility_server.cc @@ -266,48 +266,55 @@ Type_handler_olddecimal::max_display_length_for_field(const Conv_source &src) } -void Type_handler::show_binlog_type(const Conv_source &src, String *str) const +void Type_handler::show_binlog_type(const Conv_source &src, const Field &, + String *str) const { str->set_ascii(name().ptr(), name().length()); } void Type_handler_var_string::show_binlog_type(const Conv_source &src, + const Field &dst, String *str) const { CHARSET_INFO *cs= str->charset(); + const char* fmt= dst.cmp_type() != STRING_RESULT || dst.has_charset() + ? "char(%u octets)" : "binary(%u)"; size_t length= cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(), - "varchar(%u)", - src.metadata() / src.mbmaxlen()); + fmt, src.metadata()); str->length(length); } void Type_handler_varchar::show_binlog_type(const Conv_source &src, + const Field &dst, String *str) const { CHARSET_INFO *cs= str->charset(); + const char* fmt= dst.cmp_type() != STRING_RESULT || dst.has_charset() + ? "varchar(%u octets)" : "varbinary(%u)"; size_t length= cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(), - "varchar(%u)", - src.metadata() / src.mbmaxlen()); + fmt, src.metadata()); str->length(length); } void Type_handler_varchar_compressed::show_binlog_type(const Conv_source &src, + const Field &dst, String *str) const { CHARSET_INFO *cs= str->charset(); + const char* fmt= dst.cmp_type() != STRING_RESULT || dst.has_charset() + ? "varchar(%u octets) compressed" : "varbinary(%u) compressed"; size_t length= cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(), - "varchar(%u) compressed", - src.metadata() / src.mbmaxlen()); + fmt, src.metadata()); str->length(length); } -void Type_handler_bit::show_binlog_type(const Conv_source &src, +void Type_handler_bit::show_binlog_type(const Conv_source &src, const Field &, String *str) const { CHARSET_INFO *cs= str->charset(); @@ -320,6 +327,7 @@ void Type_handler_bit::show_binlog_type(const Conv_source &src, void Type_handler_olddecimal::show_binlog_type(const Conv_source &src, + const Field &, String *str) const { CHARSET_INFO *cs= str->charset(); @@ -332,6 +340,7 @@ void Type_handler_olddecimal::show_binlog_type(const Conv_source &src, void Type_handler_newdecimal::show_binlog_type(const Conv_source &src, + const Field &, String *str) const { CHARSET_INFO *cs= str->charset(); @@ -344,6 +353,7 @@ void Type_handler_newdecimal::show_binlog_type(const Conv_source &src, void Type_handler_blob_compressed::show_binlog_type(const Conv_source &src, + const Field &, String *str) const { /* @@ -371,6 +381,7 @@ void Type_handler_blob_compressed::show_binlog_type(const Conv_source &src, void Type_handler_string::show_binlog_type(const Conv_source &src, + const Field &dst, String *str) const { /* @@ -379,9 +390,11 @@ void Type_handler_string::show_binlog_type(const Conv_source &src, CHARSET_INFO *cs= str->charset(); uint bytes= (((src.metadata() >> 4) & 0x300) ^ 0x300) + (src.metadata() & 0x00ff); + const char* fmt= dst.cmp_type() != STRING_RESULT || dst.has_charset() + ? "char(%u octets)" : "binary(%u)"; size_t length= cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(), - "char(%d)", bytes / src.mbmaxlen()); + fmt, bytes); str->length(length); } @@ -743,13 +756,14 @@ Field_null::rpl_conv_type_from(const Conv_source &source, /** */ -void show_sql_type(const Conv_source &src, String *str) +static void show_sql_type(const Conv_source &src, const Field &dst, + String *str) { DBUG_ENTER("show_sql_type"); DBUG_ASSERT(src.type_handler() != NULL); DBUG_PRINT("enter", ("type: %s, metadata: 0x%x", src.type_handler()->name().ptr(), src.metadata())); - src.type_handler()->show_binlog_type(src, str); + src.type_handler()->show_binlog_type(src, dst, str); DBUG_VOID_RETURN; } @@ -979,8 +993,8 @@ table_def::compatible_with(THD *thd, rpl_group_info *rgi, StringBuffer<MAX_FIELD_WIDTH> target_type(&my_charset_latin1); THD *thd= table->in_use; - show_sql_type(source, &source_type); - field->sql_type(target_type); + show_sql_type(source, *field, &source_type); + field->sql_rpl_type(&target_type); DBUG_ASSERT(source_type.length() > 0); DBUG_ASSERT(target_type.length() > 0); rli->report(ERROR_LEVEL, ER_SLAVE_CONVERSION_FAILED, rgi->gtid_info(), diff --git a/sql/select_handler.cc b/sql/select_handler.cc index f020d2f6b80..b364cb12341 100644 --- a/sql/select_handler.cc +++ b/sql/select_handler.cc @@ -45,6 +45,8 @@ Pushdown_select::Pushdown_select(SELECT_LEX *sel, select_handler *h) Pushdown_select::~Pushdown_select() { + if (handler->table) + free_tmp_table(handler->thd, handler->table); delete handler; select->select_h= NULL; } diff --git a/sql/service_wsrep.cc b/sql/service_wsrep.cc index c47ba9d9d37..b365b393e0b 100644 --- a/sql/service_wsrep.cc +++ b/sql/service_wsrep.cc @@ -146,11 +146,10 @@ extern "C" void wsrep_handle_SR_rollback(THD *bf_thd, victim_thd->wsrep_trx_id(), victim_thd->wsrep_sr().fragments_certified(), wsrep_thd_transaction_state_str(victim_thd)); - if (bf_thd && bf_thd != victim_thd) - { - victim_thd->store_globals(); - } - else + + /* Note: do not store/reset globals before wsrep_bf_abort() call + to avoid losing BF thd context. */ + if (!(bf_thd && bf_thd != victim_thd)) { DEBUG_SYNC(victim_thd, "wsrep_before_SR_rollback"); } @@ -162,21 +161,24 @@ extern "C" void wsrep_handle_SR_rollback(THD *bf_thd, { wsrep_thd_self_abort(victim_thd); } - if (bf_thd && bf_thd != victim_thd) + if (bf_thd) { - bf_thd->store_globals(); + wsrep_store_threadvars(bf_thd); } } extern "C" my_bool wsrep_thd_bf_abort(const THD *bf_thd, THD *victim_thd, my_bool signal) { + /* Note: do not store/reset globals before wsrep_bf_abort() call + to avoid losing BF thd context. */ if (WSREP(victim_thd) && !victim_thd->wsrep_trx().active()) { WSREP_DEBUG("BF abort for non active transaction"); wsrep_start_transaction(victim_thd, victim_thd->wsrep_next_trx_id()); } my_bool ret= wsrep_bf_abort(bf_thd, victim_thd); + wsrep_store_threadvars((THD*)bf_thd); /* Send awake signal if victim was BF aborted or does not have wsrep on. Note that this should never interrupt RSU diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 0f68c726747..4cb8750ccf8 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -6461,7 +6461,7 @@ ER_MESSAGE_AND_STATEMENT eng "%s Statement: %s" ER_SLAVE_CONVERSION_FAILED - eng "Column %d of table '%-.192s.%-.192s' cannot be converted from type '%-.32s' to type '%-.32s'" + eng "Column %d of table '%-.192s.%-.192s' cannot be converted from type '%-.50s' to type '%-.50s'" ER_SLAVE_CANT_CREATE_CONVERSION eng "Can't create conversion table for table '%-.192s.%-.192s'" ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 7ac0dcad596..562c70be914 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2663,9 +2663,16 @@ Locked_tables_list::reopen_tables(THD *thd, bool need_reopen) { if (!table_list->table || !table_list->table->needs_reopen()) continue; - /* no need to remove the table from the TDC here, thus (TABLE*)1 */ - close_all_tables_for_name(thd, table_list->table->s, - HA_EXTRA_NOT_USED, (TABLE*)1); + for (TABLE **prev= &thd->open_tables; *prev; prev= &(*prev)->next) + { + if (*prev == table_list->table) + { + thd->locked_tables_list.unlink_from_list(thd, table_list, false); + mysql_lock_remove(thd, thd->lock, *prev); + close_thread_table(thd, prev); + break; + } + } DBUG_ASSERT(table_list->table == NULL); } else diff --git a/sql/sql_class.h b/sql/sql_class.h index 54d6541efbf..a8f3de4cb1a 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -6162,7 +6162,10 @@ public: uint tables; /* Number of tables in the sj-nest */ - /* Expected #rows in the materialized table */ + /* Number of rows in the materialized table, before the de-duplication */ + double rows_with_duplicates; + + /* Expected #rows in the materialized table, after de-duplication */ double rows; /* diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 37c4c27c08b..0add71b7b11 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -1188,6 +1188,16 @@ void end_connection(THD *thd) { NET *net= &thd->net; +#ifdef WITH_WSREP + if (thd->wsrep_cs().state() == wsrep::client_state::s_exec) + { + /* Error happened after the thread acquired ownership to wsrep + client state, but before command was processed. Clean up the + state before wsrep_close(). */ + wsrep_after_command_ignore_result(thd); + } + wsrep_close(thd); +#endif /* WITH_WSREP */ if (thd->user_connect) { /* @@ -1330,6 +1340,7 @@ bool thd_prepare_connection(THD *thd) prepare_new_connection_state(thd); #ifdef WITH_WSREP thd->wsrep_client_thread= true; + wsrep_open(thd); #endif /* WITH_WSREP */ return FALSE; } @@ -1403,9 +1414,6 @@ void do_handle_one_connection(CONNECT *connect, bool put_in_cache) create_user= FALSE; goto end_thread; } -#ifdef WITH_WSREP - wsrep_open(thd); -#endif /* WITH_WSREP */ while (thd_is_connection_alive(thd)) { @@ -1416,10 +1424,6 @@ void do_handle_one_connection(CONNECT *connect, bool put_in_cache) } end_connection(thd); -#ifdef WITH_WSREP - wsrep_close(thd); -#endif /* WITH_WSREP */ - end_thread: close_connection(thd); diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc index 2b46a385ba1..d3b85638898 100644 --- a/sql/sql_explain.cc +++ b/sql/sql_explain.cc @@ -460,6 +460,8 @@ uint Explain_union::make_union_table_name(char *buf) break; default: DBUG_ASSERT(0); + type.str= NULL; + type.length= 0; } memcpy(buf, type.str, (len= (uint)type.length)); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index e8e320e8439..dcffc0662dd 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2076,7 +2076,7 @@ before_trg_err: /****************************************************************************** - Check that all fields with arn't null_fields are used + Check that there aren't any null_fields ******************************************************************************/ diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index e77f17394b6..2e163a9d464 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -2998,6 +2998,7 @@ void st_select_lex_unit::print(String *str, enum_query_type query_type) { default: DBUG_ASSERT(0); + /* fall through */ case UNION_TYPE: str->append(STRING_WITH_LEN(" union ")); break; @@ -8770,11 +8771,9 @@ bool LEX::part_values_current(THD *thd) create_last_non_select_table->table_name.str); return true; } - elem->type(partition_element::CURRENT); + elem->type= partition_element::CURRENT; DBUG_ASSERT(part_info->vers_info); part_info->vers_info->now_part= elem; - if (unlikely(part_info->init_column_part(thd))) - return true; return false; } @@ -8804,9 +8803,7 @@ bool LEX::part_values_history(THD *thd) create_last_non_select_table->table_name.str); return true; } - elem->type(partition_element::HISTORY); - if (unlikely(part_info->init_column_part(thd))) - return true; + elem->type= partition_element::HISTORY; return false; } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 813f3fb3d57..9b1cb836867 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3629,6 +3629,7 @@ mysql_execute_command(THD *thd) case GET_NO_ARG: case GET_DISABLED: DBUG_ASSERT(0); + /* fall through */ case 0: case GET_FLAGSET: case GET_ENUM: @@ -8350,9 +8351,9 @@ TABLE_LIST *st_select_lex::nest_last_join(THD *thd) DBUG_ENTER("nest_last_join"); TABLE_LIST *head= join_list->head(); - if (head->nested_join && head->nested_join->nest_type & REBALANCED_NEST) + if (head->nested_join && (head->nested_join->nest_type & REBALANCED_NEST)) { - join_list->empty(); + head= join_list->pop(); DBUG_RETURN(head); } @@ -8436,13 +8437,13 @@ void st_select_lex::add_joined_table(TABLE_LIST *table) context and right-associative in another context. In this query - SELECT * FROM t1 JOIN t2 LEFT JOIN t3 ON t2.a=t3.a (Q1) + SELECT * FROM t1 JOIN t2 LEFT JOIN t3 ON t2.a=t3.a (Q1) JOIN is left-associative and the query Q1 is interpreted as - SELECT * FROM (t1 JOIN t2) LEFT JOIN t3 ON t2.a=t3.a. + SELECT * FROM (t1 JOIN t2) LEFT JOIN t3 ON t2.a=t3.a. While in this query - SELECT * FROM t1 JOIN t2 LEFT JOIN t3 ON t2.a=t3.a ON t1.b=t2.b (Q2) + SELECT * FROM t1 JOIN t2 LEFT JOIN t3 ON t2.a=t3.a ON t1.b=t2.b (Q2) JOIN is right-associative and the query Q2 is interpreted as - SELECT * FROM t1 JOIN (t2 LEFT JOIN t3 ON t2.a=t3.a) ON t1.b=t2.b + SELECT * FROM t1 JOIN (t2 LEFT JOIN t3 ON t2.a=t3.a) ON t1.b=t2.b JOIN is right-associative if it is used with ON clause or with USING clause. Otherwise it is left-associative. @@ -8488,9 +8489,9 @@ void st_select_lex::add_joined_table(TABLE_LIST *table) J LJ - ON / \ / \ - t1 LJ - ON (TQ3*) => J t2 - / \ / \ - t3 t2 t1 t3 + t1 LJ - ON (TQ3*) => t3 J + / \ / \ + t3 t2 t1 t2 With several left associative JOINs SELECT * FROM t1 JOIN t2 JOIN t3 LEFT JOIN t4 ON t3.a=t4.a (Q4) @@ -8498,15 +8499,15 @@ void st_select_lex::add_joined_table(TABLE_LIST *table) J1 LJ - ON / \ / \ - t1 LJ - ON J2 t4 + t1 J2 J2 t4 / \ => / \ - J2 t4 J1 t3 - / \ / \ - t2 t3 t1 t2 + t2 LJ - ON J1 t3 + / \ / \ + t3 t4 t1 t2 - Here's another example: - SELECT * - FROM t1 JOIN t2 LEFT JOIN t3 JOIN t4 ON t3.a=t4.a ON t2.b=t3.b (Q5) + Here's another example: + SELECT * + FROM t1 JOIN t2 LEFT JOIN t3 JOIN t4 ON t3.a=t4.a ON t2.b=t3.b (Q5) J LJ - ON / \ / \ @@ -8516,15 +8517,58 @@ void st_select_lex::add_joined_table(TABLE_LIST *table) / \ t3 t4 - If the transformed nested join node node is a natural join node like in - the following query - SELECT * FROM t1 JOIN t2 LEFT JOIN t3 USING(a) (Q6) - the transformation additionally has to take care about setting proper - references in the field natural_join for both operands of the natural - join operation. - The function also has to change the name resolution context for ON - expressions used in the transformed join expression to take into - account the tables of the left_op node. + If the transformed nested join node node is a natural join node like in + the following query + SELECT * FROM t1 JOIN t2 LEFT JOIN t3 USING(a) (Q6) + the transformation additionally has to take care about setting proper + references in the field natural_join for both operands of the natural + join operation. + + The queries that combine comma syntax for join operation with + JOIN expression require a special care. Consider the query + SELECT * FROM t1, t2 JOIN t3 LEFT JOIN t4 ON t3.a=t4.a (Q7) + This query is equivalent to the query + SELECT * FROM (t1, t2) JOIN t3 LEFT JOIN t4 ON t3.a=t4.a + The latter is transformed in the same way as query Q1 + + J LJ - ON + / \ / \ + (t1,t2) LJ - ON => J t4 + / \ / \ + t3 t4 (t1,t2) t3 + + A transformation similar to the transformation for Q3 is done for + the following query with RIGHT JOIN + SELECT * FROM t1, t2 JOIN t3 RIGHT JOIN t4 ON t3.a=t4.a (Q8) + + J LJ - ON + / \ / \ + t3 LJ - ON => t4 J + / \ / \ + t4 (t1,t2) (t1,t2) t3 + + The function also has to change the name resolution context for ON + expressions used in the transformed join expression to take into + account the tables of the left_op node. + + TODO: + A more elegant solution would be to implement the transformation that + eliminates nests for cross join operations. For Q7 it would work like this: + + J LJ - ON + / \ / \ + (t1,t2) LJ - ON => (t1,t2,t3) t4 + / \ + t3 t4 + + For Q8 with RIGHT JOIN the transformation would work similarly: + + J LJ - ON + / \ / \ + t3 LJ - ON => t4 (t1,t2,t3) + / \ + t4 (t1,t2) + */ bool st_select_lex::add_cross_joined_table(TABLE_LIST *left_op, @@ -8549,11 +8593,9 @@ bool st_select_lex::add_cross_joined_table(TABLE_LIST *left_op, } TABLE_LIST *tbl; - List<TABLE_LIST> *jl= &right_op->nested_join->join_list; + List<TABLE_LIST> *right_op_jl= right_op->join_list; TABLE_LIST *cj_nest; - add_joined_table(right_op); - /* Create the node NJ for a new nested join for the future inclusion of left_op in it. Initially the nest is empty. @@ -8568,6 +8610,8 @@ bool st_select_lex::add_cross_joined_table(TABLE_LIST *left_op, List<TABLE_LIST> *cjl= &cj_nest->nested_join->join_list; cjl->empty(); + List<TABLE_LIST> *jl= &right_op->nested_join->join_list; + DBUG_ASSERT(jl->elements == 2); /* Look for the left most node tbl of the right_op tree */ for ( ; ; ) { @@ -8640,6 +8684,8 @@ bool st_select_lex::add_cross_joined_table(TABLE_LIST *left_op, create a new top level nested join node. */ right_op->nested_join->nest_type|= REBALANCED_NEST; + if (unlikely(right_op_jl->push_front(right_op))) + DBUG_RETURN(true); DBUG_RETURN(false); } diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index e24a8962dcd..a0fd64c72ad 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -1559,7 +1559,7 @@ static bool check_vers_constants(THD *thd, partition_info *part_info) return 0; part_info->range_int_array= - (longlong*) thd->alloc(hist_parts * sizeof(longlong)); + (longlong*) thd->alloc(part_info->num_parts * sizeof(longlong)); MYSQL_TIME ltime; List_iterator<partition_element> it(part_info->partitions); @@ -1578,6 +1578,9 @@ static bool check_vers_constants(THD *thd, partition_info *part_info) if (vers_info->hist_part->range_value <= thd->query_start()) vers_info->hist_part= el; } + DBUG_ASSERT(el == vers_info->now_part); + el->max_value= true; + part_info->range_int_array[el->id]= el->range_value= LONGLONG_MAX; return 0; err: my_error(ER_DATA_OUT_OF_RANGE, MYF(0), "TIMESTAMP", "INTERVAL"); @@ -1971,7 +1974,6 @@ bool fix_partition_func(THD *thd, TABLE *table, bool is_create_table_ind) } } DBUG_ASSERT(part_info->part_type != NOT_A_PARTITION); - DBUG_ASSERT(part_info->part_type != VERSIONING_PARTITION || part_info->column_list); /* Partition is defined. We need to verify that partitioning function is correct. @@ -2004,15 +2006,15 @@ bool fix_partition_func(THD *thd, TABLE *table, bool is_create_table_ind) { if (part_info->column_list) { - if (part_info->part_type == VERSIONING_PARTITION && - part_info->vers_setup_expression(thd)) - goto end; List_iterator<const char> it(part_info->part_field_list); if (unlikely(handle_list_of_fields(thd, it, table, part_info, FALSE))) goto end; } else { + if (part_info->part_type == VERSIONING_PARTITION && + part_info->vers_setup_expression(thd)) + goto end; if (unlikely(fix_fields_part_func(thd, part_info->part_expr, table, FALSE, is_create_table_ind))) goto end; @@ -2028,7 +2030,8 @@ bool fix_partition_func(THD *thd, TABLE *table, bool is_create_table_ind) goto end; } if (unlikely(!part_info->column_list && - part_info->part_expr->result_type() != INT_RESULT)) + part_info->part_expr->result_type() != INT_RESULT && + part_info->part_expr->result_type() != DECIMAL_RESULT)) { part_info->report_part_expr_error(FALSE); goto end; @@ -2537,7 +2540,7 @@ static int add_partition_values(String *str, partition_info *part_info, } else if (part_info->part_type == VERSIONING_PARTITION) { - switch (p_elem->type()) + switch (p_elem->type) { case partition_element::CURRENT: err+= str->append(STRING_WITH_LEN(" CURRENT")); @@ -5315,7 +5318,7 @@ that are reorganised. partition_element *el; while ((el= it++)) { - if (el->type() == partition_element::CURRENT) + if (el->type == partition_element::CURRENT) { it.remove(); now_part= el; @@ -5411,7 +5414,7 @@ that are reorganised. { if (tab_part_info->part_type == VERSIONING_PARTITION) { - if (part_elem->type() == partition_element::CURRENT) + if (part_elem->type == partition_element::CURRENT) { my_error(ER_VERS_WRONG_PARTS, MYF(0), table->s->table_name.str); goto err; @@ -7665,6 +7668,10 @@ static void set_up_range_analysis_info(partition_info *part_info) partitioning */ switch (part_info->part_type) { + case VERSIONING_PARTITION: + if (!part_info->vers_info->interval.is_set()) + break; + /* Fall through */ case RANGE_PARTITION: case LIST_PARTITION: if (!part_info->column_list) @@ -8101,7 +8108,8 @@ static int get_part_iter_for_interval_via_mapping(partition_info *part_info, part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE; part_iter->ret_default_part= part_iter->ret_default_part_orig= FALSE; - if (part_info->part_type == RANGE_PARTITION) + if (part_info->part_type == RANGE_PARTITION || + part_info->part_type == VERSIONING_PARTITION) { if (part_info->part_charset_field_array) get_endpoint= get_partition_id_range_for_endpoint_charset; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 46c68d7cd4d..1361930b65d 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -924,19 +924,6 @@ Item* SELECT_LEX::period_setup_conds(THD *thd, TABLE_LIST *tables, Item *where) DBUG_RETURN(result); } -/** - Setup System Versioning conditions - - Add WHERE condition according to FOR SYSTEM_TIME clause. - - If the table is partitioned by SYSTEM_TIME and there is no FOR SYSTEM_TIME - clause, then select now-partition instead of modifying WHERE condition. - - @retval - -1 on error - @retval - 0 on success -*/ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables) { DBUG_ENTER("SELECT_LEX::vers_setup_conds"); @@ -994,13 +981,12 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables) vers_select_conds_t &vers_conditions= table->vers_conditions; #ifdef WITH_PARTITION_STORAGE_ENGINE - Vers_part_info *vers_info; - if (table->table->part_info && (vers_info= table->table->part_info->vers_info)) - { - if (table->partition_names) + /* + if the history is stored in partitions, then partitions + themselves are not versioned + */ + if (table->partition_names && table->table->part_info->vers_info) { - /* If the history is stored in partitions, then partitions - themselves are not versioned. */ if (vers_conditions.is_set()) { my_error(ER_VERS_QUERY_IN_PARTITION, MYF(0), table->alias.str); @@ -1009,19 +995,6 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables) else vers_conditions.init(SYSTEM_TIME_ALL); } - else if (!vers_conditions.is_set() && - /* We cannot optimize REPLACE .. SELECT because it may need - to call vers_set_hist_part() to update history. */ - thd->lex->sql_command != SQLCOM_REPLACE_SELECT) - { - table->partition_names= newx List<String>; - String *s= newx String(vers_info->now_part->partition_name, - system_charset_info); - table->partition_names->push_back(s); - table->table->file->change_partitions_to_open(table->partition_names); - vers_conditions.init(SYSTEM_TIME_ALL); - } - } #endif if (outer_table && !vers_conditions.is_set()) @@ -1076,7 +1049,6 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables) DBUG_RETURN(0); } -#undef newx /***************************************************************************** Check fields, find best join, do the select and output fields. @@ -8831,6 +8803,7 @@ void JOIN::get_prefix_cost_and_fanout(uint n_tables, record_count= COST_MULT(record_count, best_positions[i].records_read); read_time= COST_ADD(read_time, best_positions[i].read_time); } + /* TODO: Take into account condition selectivities here */ } *read_time_arg= read_time;// + record_count / TIME_FOR_COMPARE; *record_count_arg= record_count; @@ -9071,6 +9044,7 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s, KEYUSE *keyuse= pos->key; KEYUSE *prev_ref_keyuse= keyuse; uint key= keyuse->key; + bool used_range_selectivity= false; /* Check if we have a prefix of key=const that matches a quick select. @@ -9096,6 +9070,7 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s, keyparts++; } sel /= (double)table->quick_rows[key] / (double) table->stat_records(); + used_range_selectivity= true; } } @@ -9131,13 +9106,14 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s, if (keyparts > keyuse->keypart) { /* Ok this is the keyuse that will be used for ref access */ - uint fldno; - if (is_hash_join_key_no(key)) - fldno= keyuse->keypart; - else - fldno= table->key_info[key].key_part[keyparts-1].fieldnr - 1; - if (keyuse->val->const_item()) + if (!used_range_selectivity && keyuse->val->const_item()) { + uint fldno; + if (is_hash_join_key_no(key)) + fldno= keyuse->keypart; + else + fldno= table->key_info[key].key_part[keyparts-1].fieldnr - 1; + if (table->field[fldno]->cond_selectivity > 0) { sel /= table->field[fldno]->cond_selectivity; @@ -16718,10 +16694,20 @@ void optimize_wo_join_buffering(JOIN *join, uint first_tab, uint last_tab, reopt_remaining_tables &= ~rs->table->map; rec_count= COST_MULT(rec_count, pos.records_read); cost= COST_ADD(cost, pos.read_time); - - + cost= COST_ADD(cost, rec_count / (double) TIME_FOR_COMPARE); + //TODO: take into account join condition selectivity here + double pushdown_cond_selectivity= 1.0; + table_map real_table_bit= rs->table->map; + if (join->thd->variables.optimizer_use_condition_selectivity > 1) + { + pushdown_cond_selectivity= table_cond_selectivity(join, i, rs, + reopt_remaining_tables & + ~real_table_bit); + } + (*outer_rec_count) *= pushdown_cond_selectivity; if (!rs->emb_sj_nest) *outer_rec_count= COST_MULT(*outer_rec_count, pos.records_read); + } join->cur_sj_inner_tables= save_cur_sj_inner_tables; @@ -28676,7 +28662,7 @@ select_handler *SELECT_LEX::find_select_handler(THD *thd) return 0; if (master_unit()->outer_select()) return 0; - for (TABLE_LIST *tbl= join->tables_list; tbl; tbl= tbl->next_local) + for (TABLE_LIST *tbl= join->tables_list; tbl; tbl= tbl->next_global) { if (!tbl->table) continue; diff --git a/sql/sql_select.h b/sql/sql_select.h index 0c226e54706..2fafdd74ad9 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -882,7 +882,7 @@ public: void set_empty() { sjm_scan_need_tables= 0; - LINT_INIT_STRUCT(sjm_scan_last_inner); + sjm_scan_last_inner= 0; is_used= FALSE; } void set_from_prev(struct st_position *prev); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 34c0e0abc6c..cc977ddca4b 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2158,6 +2158,10 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN(" STORED")); else packet->append(STRING_WITH_LEN(" VIRTUAL")); + if (field->invisible == INVISIBLE_USER) + { + packet->append(STRING_WITH_LEN(" INVISIBLE")); + } } else { @@ -2478,6 +2482,7 @@ static const LEX_CSTRING *view_algorithm(TABLE_LIST *table) return &merge; default: DBUG_ASSERT(0); // never should happen + /* fall through */ case VIEW_ALGORITHM_UNDEFINED: return &undefined; } diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index a91d3a03ed1..438a9248a31 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -1820,16 +1820,13 @@ public: bool is_partial_fields_present; Index_prefix_calc(THD *thd, TABLE *table, KEY *key_info) - : index_table(table), index_info(key_info) + : index_table(table), index_info(key_info), prefixes(0), empty(true), + calc_state(NULL), is_single_comp_pk(false), is_partial_fields_present(false) { uint i; Prefix_calc_state *state; uint key_parts= table->actual_n_key_parts(key_info); - empty= TRUE; - prefixes= 0; - LINT_INIT_STRUCT(calc_state); - is_partial_fields_present= is_single_comp_pk= FALSE; uint pk= table->s->primary_key; if ((uint) (table->key_info - key_info) == pk && table->key_info[pk].user_defined_key_parts == 1) @@ -2133,6 +2130,7 @@ int alloc_statistics_for_table(THD* thd, TABLE *table) if ((histogram= (uchar *) alloc_root(&table->mem_root, hist_size * columns))) bzero(histogram, hist_size * columns); + } if (!table_stats || !column_stats || !index_stats || !idx_avg_frequency || diff --git a/sql/sql_string.cc b/sql/sql_string.cc index a4050c579d0..483eb4fcbec 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -1204,6 +1204,8 @@ size_t convert_to_printable_required_length(uint len) bool String::append_semi_hex(const char *s, uint len, CHARSET_INFO *cs) { + if (!len) + return false; size_t dst_len= convert_to_printable_required_length(len); if (reserve(dst_len)) return true; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 9bb1d98152b..0785276ecd8 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -64,7 +64,7 @@ const char *primary_key_name="PRIMARY"; static int check_if_keyname_exists(const char *name,KEY *start, KEY *end); static char *make_unique_key_name(THD *, const char *, KEY *, KEY *); -static void make_unique_constraint_name(THD *, LEX_CSTRING *, const char *, +static bool make_unique_constraint_name(THD *, LEX_CSTRING *, const char *, List<Virtual_column_info> *, uint *); static const char *make_unique_invisible_field_name(THD *, const char *, List<Create_field> *); @@ -76,6 +76,9 @@ static int copy_data_between_tables(THD *, TABLE *,TABLE *, static int mysql_prepare_create_table(THD *, HA_CREATE_INFO *, Alter_info *, uint *, handler *, KEY **, uint *, int); static uint blob_length_by_type(enum_field_types type); +static bool fix_constraints_names(THD *thd, List<Virtual_column_info> + *check_constraint_list, + const HA_CREATE_INFO *create_info); /** @brief Helper function for explain_filename @@ -4326,20 +4329,13 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, /* Check table level constraints */ create_info->check_constraint_list= &alter_info->check_constraint_list; { - uint nr= 1; List_iterator_fast<Virtual_column_info> c_it(alter_info->check_constraint_list); Virtual_column_info *check; while ((check= c_it++)) { - if (!check->name.length) - { - const char *own_name_base= create_info->period_info.constr == check - ? create_info->period_info.name.str : NULL; + if (!check->name.length || check->automatic_name) + continue; - make_unique_constraint_name(thd, &check->name, own_name_base, - &alter_info->check_constraint_list, - &nr); - } { /* Check that there's no repeating constraint names. */ List_iterator_fast<Virtual_column_info> @@ -4884,6 +4880,10 @@ int create_table_impl(THD *thd, const LEX_CSTRING &orig_db, DBUG_PRINT("enter", ("db: '%s' table: '%s' tmp: %d path: %s", db.str, table_name.str, internal_tmp_table, path)); + if (fix_constraints_names(thd, &alter_info->check_constraint_list, + create_info)) + DBUG_RETURN(1); + if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE) { if (create_info->data_file_name) @@ -5382,7 +5382,7 @@ make_unique_key_name(THD *thd, const char *field_name,KEY *start,KEY *end) Make an unique name for constraints without a name */ -static void make_unique_constraint_name(THD *thd, LEX_CSTRING *name, +static bool make_unique_constraint_name(THD *thd, LEX_CSTRING *name, const char *own_name_base, List<Virtual_column_info> *vcol, uint *nr) @@ -5410,9 +5410,10 @@ static void make_unique_constraint_name(THD *thd, LEX_CSTRING *name, { name->length= (size_t) (real_end - buff); name->str= thd->strmake(buff, name->length); - return; + return (name->str == NULL); } } + return FALSE; } /** @@ -6035,10 +6036,11 @@ static bool is_candidate_key(KEY *key) from the list if existing found. RETURN VALUES - NONE + TRUE error + FALSE OK */ -static void +static bool handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info, Table_period_info *period_info) { @@ -6484,6 +6486,7 @@ remove_key: Virtual_column_info *check; TABLE_SHARE *share= table->s; uint c; + while ((check=it++)) { if (!(check->flags & Alter_info::CHECK_CONSTRAINT_IF_NOT_EXISTS) && @@ -6531,7 +6534,48 @@ remove_key: *period_info= {}; } - DBUG_VOID_RETURN; + DBUG_RETURN(false); +} + + +static bool fix_constraints_names(THD *thd, List<Virtual_column_info> + *check_constraint_list, + const HA_CREATE_INFO *create_info) +{ + List_iterator<Virtual_column_info> it((*check_constraint_list)); + Virtual_column_info *check; + uint nr= 1; + DBUG_ENTER("fix_constraints_names"); + if (!check_constraint_list) + DBUG_RETURN(FALSE); + // Prevent accessing freed memory during generating unique names + while ((check=it++)) + { + if (check->automatic_name) + { + check->name.str= NULL; + check->name.length= 0; + } + } + it.rewind(); + // Generate unique names if needed + while ((check=it++)) + { + if (!check->name.length) + { + check->automatic_name= TRUE; + + const char *own_name_base= create_info->period_info.constr == check + ? create_info->period_info.name.str : NULL; + + if (make_unique_constraint_name(thd, &check->name, + own_name_base, + check_constraint_list, + &nr)) + DBUG_RETURN(TRUE); + } + } + DBUG_RETURN(FALSE); } @@ -7959,7 +8003,6 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, Create_field *def; Field **f_ptr,*field; MY_BITMAP *dropped_fields= NULL; // if it's NULL - no dropped fields - bool save_reopen= table->m_needs_reopen; bool drop_period= false; DBUG_ENTER("mysql_prepare_alter_table"); @@ -8707,9 +8750,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, alter_info->create_list.swap(new_create_list); alter_info->key_list.swap(new_key_list); alter_info->check_constraint_list.swap(new_constraint_list); - DBUG_RETURN(rc); err: - table->m_needs_reopen= save_reopen; DBUG_RETURN(rc); } @@ -9675,7 +9716,11 @@ do_continue:; } } - handle_if_exists_options(thd, table, alter_info, &create_info->period_info); + if (handle_if_exists_options(thd, table, alter_info, + &create_info->period_info) || + fix_constraints_names(thd, &alter_info->check_constraint_list, + create_info)) + DBUG_RETURN(true); /* Look if we have to do anything at all. @@ -11153,10 +11198,9 @@ 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->first_select_lex(); TABLE_LIST *first_table= select_lex->table_list.first; - DBUG_ASSERT(first_table == all_tables && first_table != 0); + DBUG_ASSERT(first_table == lex->query_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; diff --git a/sql/sql_time.cc b/sql/sql_time.cc index c64995fa3d6..b128a7f7291 100644 --- a/sql/sql_time.cc +++ b/sql/sql_time.cc @@ -914,7 +914,7 @@ void make_truncated_value_warning(THD *thd, #define GET_PART(X, N) X % N ## LL; X/= N ## LL bool date_add_interval(THD *thd, MYSQL_TIME *ltime, interval_type int_type, - const INTERVAL &interval) + const INTERVAL &interval, bool push_warn) { long period, sign; @@ -1027,6 +1027,7 @@ bool date_add_interval(THD *thd, MYSQL_TIME *ltime, interval_type int_type, return 0; // Ok invalid_date: + if (push_warn) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_DATETIME_FUNCTION_OVERFLOW, diff --git a/sql/sql_time.h b/sql/sql_time.h index 25980d6417c..fe9697adf67 100644 --- a/sql/sql_time.h +++ b/sql/sql_time.h @@ -92,7 +92,7 @@ bool my_TIME_to_str(const MYSQL_TIME *ltime, String *str, uint dec); /* MYSQL_TIME operations */ bool date_add_interval(THD *thd, MYSQL_TIME *ltime, interval_type int_type, - const INTERVAL &interval); + const INTERVAL &interval, bool push_warn= true); bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, int l_sign, ulonglong *seconds_out, ulong *microseconds_out); int append_interval(String *str, interval_type int_type, diff --git a/sql/sql_type.cc b/sql/sql_type.cc index 905676ee604..2ac1516082a 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -1029,10 +1029,21 @@ bool Temporal::datetime_add_nanoseconds_or_invalidate(THD *thd, int *warn, ulong INTERVAL interval; memset(&interval, 0, sizeof(interval)); interval.hour= 1; - /* date_add_interval cannot handle bad dates */ - if (check_date(TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE, warn) || - date_add_interval(thd, this, INTERVAL_HOUR, interval)) + /* + date_add_interval cannot handle bad dates with zero YYYY or MM. + Note, check_date(NO_ZERO_XX) does not check YYYY against zero, + so let's additionally check it. + */ + if (year == 0 || + check_date(TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE, warn) || + date_add_interval(thd, this, INTERVAL_HOUR, interval, false/*no warn*/)) { + char buf[MAX_DATE_STRING_REP_LENGTH]; + my_date_to_str(this, buf); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_VALUE_FOR_TYPE, + ER_THD(thd, ER_WRONG_VALUE_FOR_TYPE), + "date", buf, "round(datetime)"); make_from_out_of_range(warn); return true; } @@ -6073,6 +6084,30 @@ bool Type_handler_temporal_result:: } +bool Type_handler_time_common:: + Item_func_round_fix_length_and_dec(Item_func_round *item) const +{ + item->fix_arg_time(); + return false; +} + + +bool Type_handler_datetime_common:: + Item_func_round_fix_length_and_dec(Item_func_round *item) const +{ + item->fix_arg_datetime(); + return false; +} + + +bool Type_handler_timestamp_common:: + Item_func_round_fix_length_and_dec(Item_func_round *item) const +{ + item->fix_arg_datetime(); + return false; +} + + bool Type_handler_string_result:: Item_func_round_fix_length_and_dec(Item_func_round *item) const { diff --git a/sql/sql_type.h b/sql/sql_type.h index 62ba43acc81..64b5a9d252b 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -2524,7 +2524,7 @@ public: Datetime to_datetime(THD *thd) const { if (is_zero_datetime()) - return Datetime(); + return Datetime::zero(); return Timestamp::to_datetime(thd); } bool is_zero_datetime() const { return m_is_zero_datetime; } @@ -2572,7 +2572,7 @@ public: Datetime to_datetime(THD *thd) const { return is_zero_datetime() ? - Datetime() : + Datetime::zero() : Datetime(thd, Timestamp(*this).tv()); } bool is_zero_datetime() const @@ -3344,6 +3344,16 @@ public: { return MYSQL_TIMESTAMP_ERROR; } + /* + Return true if the native format is fully implemented for a data type: + - Field_xxx::val_native() + - Item_xxx::val_native() for all classes supporting this data type + - Type_handler_xxx::cmp_native() + */ + virtual bool is_val_native_ready() const + { + return false; + } virtual bool is_timestamp_type() const { return false; @@ -3495,7 +3505,8 @@ public: TABLE *table, uint metadata, const Field *target) const= 0; - virtual void show_binlog_type(const Conv_source &src, String *str) const; + virtual void show_binlog_type(const Conv_source &src, const Field &dst, + String *str) const; virtual uint32 max_display_length_for_field(const Conv_source &src) const= 0; /* Performs the final data type validation for a UNION element, @@ -4746,13 +4757,14 @@ public: class Type_handler_general_purpose_int: public Type_handler_int_result { public: - bool type_can_have_auto_increment_attribute() const { return true; } + bool type_can_have_auto_increment_attribute() const override { return true; } virtual const Type_limits_int *type_limits_int() const= 0; - uint32 max_display_length(const Item *item) const + uint32 max_display_length(const Item *item) const override { return type_limits_int()->char_length(); } - bool Vers_history_point_resolve_unit(THD *thd, Vers_history_point *p) const; + bool Vers_history_point_resolve_unit(THD *thd, Vers_history_point *p) + const override; }; @@ -4762,68 +4774,71 @@ protected: uint Item_decimal_scale_with_seconds(const Item *item) const; uint Item_divisor_precision_increment_with_seconds(const Item *) const; public: - Item_result result_type() const { return STRING_RESULT; } - Item_result cmp_type() const { return TIME_RESULT; } + Item_result result_type() const override { return STRING_RESULT; } + Item_result cmp_type() const override { return TIME_RESULT; } virtual ~Type_handler_temporal_result() {} void make_sort_key(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field, - Sort_param *param) const; + Sort_param *param) const override; void sortlength(THD *thd, const Type_std_attributes *item, - SORT_FIELD_ATTR *attr) const; + SORT_FIELD_ATTR *attr) const override; bool Item_const_eq(const Item_const *a, const Item_const *b, - bool binary_cmp) const; + bool binary_cmp) const override; bool Item_param_set_from_value(THD *thd, Item_param *param, const Type_all_attributes *attr, - const st_value *value) const; - uint32 max_display_length(const Item *item) const; + const st_value *value) const override; + uint32 max_display_length(const Item *item) const override; bool can_change_cond_ref_to_const(Item_bool_func2 *target, Item *target_expr, Item *target_value, Item_bool_func2 *source, - Item *source_expr, Item *source_const) const; + Item *source_expr, Item *source_const) + const override; bool subquery_type_allows_materialization(const Item *inner, - const Item *outer) const; + const Item *outer) const override; bool Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func, - Item **items, uint nitems) const; - bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const; - bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const; - bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const; - bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const; - bool Item_val_bool(Item *item) const; + Item **items, uint nitems) + const override; + bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *) const override; + bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const override; + bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const override; + bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *)const override; + bool Item_val_bool(Item *item) const override; void Item_get_date(THD *thd, Item *item, Temporal::Warn *warn, - MYSQL_TIME *ltime, date_mode_t fuzzydate) const; - longlong Item_val_int_signed_typecast(Item *item) const; - longlong Item_val_int_unsigned_typecast(Item *item) const; - String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const; + MYSQL_TIME *ltime, date_mode_t fuzzydate) const override; + longlong Item_val_int_signed_typecast(Item *item) const override; + longlong Item_val_int_unsigned_typecast(Item *item) const override; + String *Item_func_hex_val_str_ascii(Item_func_hex *, String *)const override; String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *, - String *) const; + String *) const override; double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *) - const; + const override; longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *) - const; + const override; my_decimal *Item_func_hybrid_field_type_val_decimal( Item_func_hybrid_field_type *, - my_decimal *) const; + my_decimal *) const override; void Item_func_hybrid_field_type_get_date(THD *, Item_func_hybrid_field_type *, Temporal::Warn *, MYSQL_TIME *, - date_mode_t fuzzydate) const; + date_mode_t) const override; bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*, - MYSQL_TIME *, date_mode_t fuzzydate) const; - bool Item_func_between_fix_length_and_dec(Item_func_between *func) const; - bool Item_func_in_fix_comparator_compatible_types(THD *thd, - Item_func_in *) const; - bool Item_func_round_fix_length_and_dec(Item_func_round *) const; - bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const; - bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const; - bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const; - bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const; - bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const; - bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const; - bool Item_func_div_fix_length_and_dec(Item_func_div *) const; - bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const; - bool Vers_history_point_resolve_unit(THD *thd, Vers_history_point *p) const; + MYSQL_TIME *, date_mode_t) const override; + bool Item_func_between_fix_length_and_dec(Item_func_between *)const override; + bool Item_func_in_fix_comparator_compatible_types(THD *, Item_func_in *) + const override; + bool Item_func_round_fix_length_and_dec(Item_func_round *) const override; + bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *)const override; + bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const override; + bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const override; + bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const override; + bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const override; + bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const override; + bool Item_func_div_fix_length_and_dec(Item_func_div *) const override; + bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const override; + bool Vers_history_point_resolve_unit(THD *thd, Vers_history_point *p) + const override; }; @@ -4981,8 +4996,9 @@ public: class Type_handler_general_purpose_string: public Type_handler_string_result { public: - bool is_general_purpose_string_type() const { return true; } - bool Vers_history_point_resolve_unit(THD *thd, Vers_history_point *p) const; + bool is_general_purpose_string_type() const override { return true; } + bool Vers_history_point_resolve_unit(THD *thd, Vers_history_point *p) + const override; }; @@ -5407,7 +5423,8 @@ public: { return print_item_value_csstr(thd, item, str); } - void show_binlog_type(const Conv_source &src, String *str) const override; + void show_binlog_type(const Conv_source &src, const Field &, String *str) + const override; Field *make_conversion_table_field(MEM_ROOT *root, TABLE *table, uint metadata, const Field *target) const override; @@ -5652,6 +5669,7 @@ public: MYSQL_TIME *, date_mode_t fuzzydate) const override; longlong Item_func_between_val_int(Item_func_between *func) const override; + bool Item_func_round_fix_length_and_dec(Item_func_round *) const override; Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const override; bool set_comparator_func(Arg_comparator *cmp) const override; @@ -5935,6 +5953,7 @@ public: longlong Item_func_min_max_val_int(Item_func_min_max *) const override; my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, my_decimal *) const override; + bool Item_func_round_fix_length_and_dec(Item_func_round *) const override; bool Item_hybrid_func_fix_attributes(THD *thd, const char *name, Type_handler_hybrid_field_type *, @@ -6037,6 +6056,10 @@ public: { return MYSQL_TIMESTAMP_DATETIME; } + bool is_val_native_ready() const override + { + return true; + } bool is_timestamp_type() const override { return true; @@ -6052,6 +6075,7 @@ public: const override; int cmp_native(const Native &a, const Native &b) const override; longlong Item_func_between_val_int(Item_func_between *func) const override; + bool Item_func_round_fix_length_and_dec(Item_func_round *) const override; cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const override; in_vector *make_in_vector(THD *thd, const Item_func_in *f, uint nargs) const override; @@ -6178,7 +6202,8 @@ public: uint32 calc_pack_length(uint32 length) const override { return length; } const Type_handler *type_handler_for_tmp_table(const Item *item) const override; const Type_handler *type_handler_for_union(const Item *item) const override; - void show_binlog_type(const Conv_source &src, String *str) const override; + void show_binlog_type(const Conv_source &src, const Field &, String *str) + const override; Field *make_conversion_table_field(MEM_ROOT *root, TABLE *table, uint metadata, const Field *target) const override; @@ -6211,7 +6236,8 @@ public: enum_field_types field_type() const override { return MYSQL_TYPE_NEWDECIMAL; } uint32 max_display_length_for_field(const Conv_source &src) const override; uint32 calc_pack_length(uint32 length) const override; - void show_binlog_type(const Conv_source &src, String *str) const override; + void show_binlog_type(const Conv_source &src, const Field &, String *str) + const override; Field *make_conversion_table_field(MEM_ROOT *root, TABLE *table, uint metadata, const Field *target) const override; @@ -6326,7 +6352,8 @@ public: { return varstring_type_handler(item); } - void show_binlog_type(const Conv_source &src, String *str) const override; + void show_binlog_type(const Conv_source &src, const Field &dst, String *str) + const override; Field *make_conversion_table_field(MEM_ROOT *root, TABLE *table, uint metadata, const Field *target) const override; @@ -6369,7 +6396,8 @@ public: return varstring_type_handler(item); } uint32 max_display_length_for_field(const Conv_source &src) const override; - void show_binlog_type(const Conv_source &src, String *str) const override; + void show_binlog_type(const Conv_source &src, const Field &dst, String *str) + const override; void Column_definition_implicit_upgrade(Column_definition *c) const override; bool Column_definition_fix_attributes(Column_definition *c) const override; bool Column_definition_prepare_stage2(Column_definition *c, @@ -6408,7 +6436,8 @@ public: return varstring_type_handler(item); } bool is_param_long_data_type() const override { return true; } - void show_binlog_type(const Conv_source &src, String *str) const override; + void show_binlog_type(const Conv_source &src, const Field &dst, String *str) + const override; Field *make_conversion_table_field(MEM_ROOT *root, TABLE *table, uint metadata, const Field *target) const override; @@ -6458,7 +6487,8 @@ public: return MYSQL_TYPE_VARCHAR_COMPRESSED; } uint32 max_display_length_for_field(const Conv_source &src) const override; - void show_binlog_type(const Conv_source &src, String *str) const override; + void show_binlog_type(const Conv_source &src, const Field &dst, String *str) + const override; Field *make_conversion_table_field(MEM_ROOT *root, TABLE *table, uint metadata, const Field *target) const override; @@ -6626,7 +6656,8 @@ public: return MYSQL_TYPE_BLOB_COMPRESSED; } uint32 max_display_length_for_field(const Conv_source &src) const override; - void show_binlog_type(const Conv_source &src, String *str) const override; + void show_binlog_type(const Conv_source &src, const Field &, String *str) + const override; Field *make_conversion_table_field(MEM_ROOT *root, TABLE *table, uint metadata, const Field *target) const override; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index a09af1237e1..49ca047e89b 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -5478,10 +5478,10 @@ part_column_list: part_func: - '(' remember_name part_func_expr remember_end ')' + '(' part_func_expr ')' { partition_info *part_info= Lex->part_info; - if (unlikely(part_info->set_part_expr(thd, $2 + 1, $3, $4, FALSE))) + if (unlikely(part_info->set_part_expr(thd, $2, FALSE))) MYSQL_YYABORT; part_info->num_columns= 1; part_info->column_list= FALSE; @@ -5489,9 +5489,9 @@ part_func: ; sub_part_func: - '(' remember_name part_func_expr remember_end ')' + '(' part_func_expr ')' { - if (unlikely(Lex->part_info->set_part_expr(thd, $2 + 1, $3, $4, TRUE))) + if (unlikely(Lex->part_info->set_part_expr(thd, $2, TRUE))) MYSQL_YYABORT; } ; diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index f938bcfd9d5..cff3a5be47c 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -5477,10 +5477,10 @@ part_column_list: part_func: - '(' remember_name part_func_expr remember_end ')' + '(' part_func_expr ')' { partition_info *part_info= Lex->part_info; - if (unlikely(part_info->set_part_expr(thd, $2 + 1, $3, $4, FALSE))) + if (unlikely(part_info->set_part_expr(thd, $2, FALSE))) MYSQL_YYABORT; part_info->num_columns= 1; part_info->column_list= FALSE; @@ -5488,9 +5488,9 @@ part_func: ; sub_part_func: - '(' remember_name part_func_expr remember_end ')' + '(' part_func_expr ')' { - if (unlikely(Lex->part_info->set_part_expr(thd, $2 + 1, $3, $4, TRUE))) + if (unlikely(Lex->part_info->set_part_expr(thd, $2, TRUE))) MYSQL_YYABORT; } ; diff --git a/sql/table.cc b/sql/table.cc index 3b3a1fa6fb0..61bc015269c 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -801,7 +801,8 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end, { if (strpos + (new_frm_ver >= 1 ? 9 : 7) >= frm_image_end) return 1; - *rec_per_key++=0; + if (!(keyinfo->algorithm == HA_KEY_ALG_LONG_HASH)) + *rec_per_key++=0; key_part->fieldnr= (uint16) (uint2korr(strpos) & FIELD_NR_MASK); key_part->offset= (uint) uint2korr(strpos+2)-1; key_part->key_type= (uint) uint2korr(strpos+5); @@ -829,6 +830,7 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end, { keyinfo->key_length= HA_HASH_KEY_LENGTH_WITHOUT_NULL; key_part++; // reserved for the hash value + *rec_per_key++=0; } /* @@ -9523,8 +9525,7 @@ bool vers_select_conds_t::eq(const vers_select_conds_t &conds) const case SYSTEM_TIME_ALL: return true; case SYSTEM_TIME_BEFORE: - DBUG_ASSERT(0); - return false; + break; case SYSTEM_TIME_AS_OF: return start.eq(conds.start); case SYSTEM_TIME_FROM_TO: diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc index ec889851b78..6ed80c8de76 100644 --- a/sql/threadpool_common.cc +++ b/sql/threadpool_common.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2012 Monty Program Ab +/* Copyright (C) 2012, 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 @@ -25,6 +25,10 @@ #include <threadpool.h> #include <my_counter.h> +#ifdef WITH_WSREP +#include "wsrep_trans_observer.h" +#endif /* WITH_WSREP */ + /* Threadpool parameters */ uint threadpool_min_threads; @@ -139,6 +143,11 @@ static inline void set_thd_idle(THD *thd) */ static void thread_attach(THD* thd) { +#ifdef WITH_WSREP + /* Wait until possible background rollback has finished before + attaching the thd. */ + wsrep_wait_rollback_complete_and_acquire_ownership(thd); +#endif /* WITH_WSREP */ pthread_setspecific(THR_KEY_mysys,thd->mysys_var); thd->thread_stack=(char*)&thd; thd->store_globals(); diff --git a/sql/wsrep_client_service.cc b/sql/wsrep_client_service.cc index 0fa10c1c9ea..8d58f62bd03 100644 --- a/sql/wsrep_client_service.cc +++ b/sql/wsrep_client_service.cc @@ -29,9 +29,9 @@ #include "slave.h" /* opt_log_slave_updates */ #include "transaction.h" /* trans_commit()... */ #include "log.h" /* stmt_has_updated_trans_table() */ -//#include "debug_sync.h" #include "mysql/service_debug_sync.h" #include "mysql/psi/mysql_thread.h" /* mysql_mutex_assert_owner() */ + namespace { @@ -56,16 +56,12 @@ Wsrep_client_service::Wsrep_client_service(THD* thd, void Wsrep_client_service::store_globals() { - DBUG_ENTER("Wsrep_client_service::store_globals"); - m_thd->store_globals(); - DBUG_VOID_RETURN; + wsrep_store_threadvars(m_thd); } void Wsrep_client_service::reset_globals() { - DBUG_ENTER("Wsrep_client_service::reset_globals"); - m_thd->reset_globals(); - DBUG_VOID_RETURN; + wsrep_reset_threadvars(m_thd); } bool Wsrep_client_service::interrupted( diff --git a/sql/wsrep_high_priority_service.cc b/sql/wsrep_high_priority_service.cc index 581ecfc8d34..02ab3b50388 100644 --- a/sql/wsrep_high_priority_service.cc +++ b/sql/wsrep_high_priority_service.cc @@ -398,20 +398,13 @@ int Wsrep_high_priority_service::apply_toi(const wsrep::ws_meta& ws_meta, void Wsrep_high_priority_service::store_globals() { - DBUG_ENTER("Wsrep_high_priority_service::store_globals"); - /* In addition to calling THD::store_globals(), call - wsrep::client_state::store_globals() to gain ownership of - the client state */ - m_thd->store_globals(); - m_thd->wsrep_cs().store_globals(); - DBUG_VOID_RETURN; + wsrep_store_threadvars(m_thd); + m_thd->wsrep_cs().acquire_ownership(); } void Wsrep_high_priority_service::reset_globals() { - DBUG_ENTER("Wsrep_high_priority_service::reset_globals"); - m_thd->reset_globals(); - DBUG_VOID_RETURN; + wsrep_reset_threadvars(m_thd); } void Wsrep_high_priority_service::switch_execution_context(wsrep::high_priority_service& orig_high_priority_service) @@ -590,11 +583,14 @@ Wsrep_replayer_service::Wsrep_replayer_service(THD* replayer_thd, THD* orig_thd) thd_proc_info(orig_thd, "wsrep replaying trx"); /* - Swith execution context to replayer_thd and prepare it for + Switch execution context to replayer_thd and prepare it for replay execution. */ - orig_thd->reset_globals(); - replayer_thd->store_globals(); + /* Copy thd vars from orig_thd before reset, otherwise reset + for orig thd clears thread local storage before copy. */ + wsrep_assign_from_threadvars(replayer_thd); + wsrep_reset_threadvars(orig_thd); + wsrep_store_threadvars(replayer_thd); wsrep_open(replayer_thd); wsrep_before_command(replayer_thd); replayer_thd->wsrep_cs().clone_transaction_for_replay(orig_thd->wsrep_trx()); @@ -611,8 +607,8 @@ Wsrep_replayer_service::~Wsrep_replayer_service() wsrep_after_apply(replayer_thd); wsrep_after_command_ignore_result(replayer_thd); wsrep_close(replayer_thd); - replayer_thd->reset_globals(); - orig_thd->store_globals(); + wsrep_reset_threadvars(replayer_thd); + wsrep_store_threadvars(orig_thd); DBUG_ASSERT(!orig_thd->get_stmt_da()->is_sent()); DBUG_ASSERT(!orig_thd->get_stmt_da()->is_set()); diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index ad4203490f2..22b274bef1b 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -2251,6 +2251,7 @@ static void wsrep_close_thread(THD *thd) { thd->set_killed(KILL_CONNECTION); MYSQL_CALLBACK(thread_scheduler, post_kill_notification, (thd)); + mysql_mutex_lock(&thd->LOCK_thd_kill); if (thd->mysys_var) { thd->mysys_var->abort=1; @@ -2263,6 +2264,7 @@ static void wsrep_close_thread(THD *thd) } mysql_mutex_unlock(&thd->mysys_var->mutex); } + mysql_mutex_unlock(&thd->LOCK_thd_kill); } static my_bool have_committing_connections(THD *thd, void *) @@ -2659,7 +2661,8 @@ void* start_wsrep_THD(void *arg) /* now that we've called my_thread_init(), it is safe to call DBUG_* */ thd->thread_stack= (char*) &thd; - if (thd->store_globals()) + wsrep_assign_from_threadvars(thd); + if (wsrep_store_threadvars(thd)) { close_connection(thd, ER_OUT_OF_RESOURCES); statistic_increment(aborted_connects,&LOCK_status); @@ -2697,19 +2700,17 @@ void* start_wsrep_THD(void *arg) WSREP_DEBUG("wsrep system thread %llu, %p starting", thd->thread_id, thd); - thd_args->fun()(thd, thd_args->args()); + thd_args->fun()(thd, static_cast<void *>(thd_args)); WSREP_DEBUG("wsrep system thread: %llu, %p closing", thd->thread_id, thd); /* Wsrep may reset globals during thread context switches, store globals before cleanup. */ - thd->store_globals(); + wsrep_store_threadvars(thd); close_connection(thd, 0); - delete thd_args; - mysql_mutex_lock(&LOCK_wsrep_slave_threads); DBUG_ASSERT(wsrep_running_threads > 0); wsrep_running_threads--; @@ -2728,6 +2729,7 @@ void* start_wsrep_THD(void *arg) break; } + delete thd_args; WSREP_DEBUG("wsrep running threads now: %lu", wsrep_running_threads); mysql_cond_broadcast(&COND_wsrep_slave_threads); mysql_mutex_unlock(&LOCK_wsrep_slave_threads); diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h index 42808aa66c0..497e5f7a086 100644 --- a/sql/wsrep_mysqld.h +++ b/sql/wsrep_mysqld.h @@ -411,18 +411,17 @@ typedef void (*wsrep_thd_processor_fun)(THD*, void *); class Wsrep_thd_args { public: - Wsrep_thd_args(wsrep_thd_processor_fun fun, void* args, - wsrep_thread_type thread_type) + Wsrep_thd_args(wsrep_thd_processor_fun fun, + wsrep_thread_type thread_type, + pthread_t thread_id) : fun_ (fun), - args_ (args), - thread_type_ (thread_type) + thread_type_ (thread_type), + thread_id_ (thread_id) { } wsrep_thd_processor_fun fun() { return fun_; } - - void* args() { return args_; } - + pthread_t* thread_id() {return &thread_id_; } enum wsrep_thread_type thread_type() {return thread_type_;} private: @@ -431,8 +430,8 @@ class Wsrep_thd_args Wsrep_thd_args& operator=(const Wsrep_thd_args&); wsrep_thd_processor_fun fun_; - void* args_; enum wsrep_thread_type thread_type_; + pthread_t thread_id_; }; void* start_wsrep_THD(void*); diff --git a/sql/wsrep_schema.cc b/sql/wsrep_schema.cc index ee0fc8f12cc..c7ea378d4fb 100644 --- a/sql/wsrep_schema.cc +++ b/sql/wsrep_schema.cc @@ -29,6 +29,7 @@ #include "wsrep_binlog.h" #include "wsrep_high_priority_service.h" #include "wsrep_storage_service.h" +#include "wsrep_thd.h" #include <string> #include <sstream> @@ -145,13 +146,13 @@ public: : m_orig_thd(orig_thd) , m_cur_thd(cur_thd) { - m_orig_thd->reset_globals(); - m_cur_thd->store_globals(); + wsrep_reset_threadvars(m_orig_thd); + wsrep_store_threadvars(m_cur_thd); } ~thd_context_switch() { - m_cur_thd->reset_globals(); - m_orig_thd->store_globals(); + wsrep_reset_threadvars(m_cur_thd); + wsrep_store_threadvars(m_orig_thd); } private: THD *m_orig_thd; @@ -474,7 +475,9 @@ static int scan(TABLE* table, uint field, char* strbuf, uint strbuf_len) { String str; (void)table->field[field]->val_str(&str); - strncpy(strbuf, str.ptr(), std::min(str.length(), strbuf_len)); + LEX_CSTRING tmp= str.lex_cstring(); + uint len = tmp.length; + strncpy(strbuf, tmp.str, std::min(len, strbuf_len)); strbuf[strbuf_len - 1]= '\0'; return 0; } @@ -593,7 +596,8 @@ static void wsrep_init_thd_for_schema(THD *thd) thd->variables.option_bits |= OPTION_LOG_OFF; /* Read committed isolation to avoid gap locking */ thd->variables.tx_isolation= ISO_READ_COMMITTED; - thd->store_globals(); + wsrep_assign_from_threadvars(thd); + wsrep_store_threadvars(thd); } int Wsrep_schema::init() @@ -1121,6 +1125,7 @@ int Wsrep_schema::replay_transaction(THD* orig_thd, THD thd(next_thread_id(), true); thd.thread_stack= (orig_thd ? orig_thd->thread_stack : (char*) &thd); + wsrep_assign_from_threadvars(&thd); Wsrep_schema_impl::wsrep_off wsrep_off(&thd); Wsrep_schema_impl::binlog_off binlog_off(&thd); @@ -1226,6 +1231,7 @@ int Wsrep_schema::recover_sr_transactions(THD *orig_thd) THD storage_thd(next_thread_id(), true); storage_thd.thread_stack= (orig_thd ? orig_thd->thread_stack : (char*) &storage_thd); + wsrep_assign_from_threadvars(&storage_thd); TABLE* frag_table= 0; TABLE* cluster_table= 0; Wsrep_storage_service storage_service(&storage_thd); @@ -1331,12 +1337,7 @@ int Wsrep_schema::recover_sr_transactions(THD *orig_thd) transaction_id))) { DBUG_ASSERT(wsrep::starts_transaction(flags)); - THD* thd= new THD(next_thread_id(), true); - thd->thread_stack= (char*)&storage_thd; - - thd->real_id= pthread_self(); - - applier= new Wsrep_applier_service(thd); + applier = wsrep_create_streaming_applier(&storage_thd, "recovery"); server_state.start_streaming_applier(server_id, transaction_id, applier); applier->start_transaction(wsrep::ws_handle(transaction_id, 0), @@ -1366,6 +1367,7 @@ int Wsrep_schema::recover_sr_transactions(THD *orig_thd) Wsrep_schema_impl::end_scan(frag_table); Wsrep_schema_impl::finish_stmt(&storage_thd); trans_commit(&storage_thd); + storage_thd.set_mysys_var(0); out: DBUG_RETURN(ret); } diff --git a/sql/wsrep_server_service.cc b/sql/wsrep_server_service.cc index 42856862db3..bfb85e3d0ab 100644 --- a/sql/wsrep_server_service.cc +++ b/sql/wsrep_server_service.cc @@ -26,6 +26,7 @@ #include "wsrep_mysqld.h" #include "wsrep_schema.h" #include "wsrep_utils.h" +#include "wsrep_thd.h" #include "log.h" /* sql_print_xxx() */ #include "sql_class.h" /* system variables */ @@ -50,6 +51,10 @@ wsrep::storage_service* Wsrep_server_service::storage_service( init_service_thd(thd, cs.m_thd->thread_stack); WSREP_DEBUG("Created storage service with thread id %llu", thd->thread_id); + /* Use variables from the current thd attached to client_service. + This is because we need to be able to BF abort storage access + operations. */ + wsrep_assign_from_threadvars(thd); return new Wsrep_storage_service(thd); } @@ -62,6 +67,7 @@ wsrep::storage_service* Wsrep_server_service::storage_service( init_service_thd(thd, hps.m_thd->thread_stack); WSREP_DEBUG("Created high priority storage service with thread id %llu", thd->thread_id); + wsrep_assign_from_threadvars(thd); return new Wsrep_storage_service(thd); } @@ -71,21 +77,48 @@ void Wsrep_server_service::release_storage_service( Wsrep_storage_service* ss= static_cast<Wsrep_storage_service*>(storage_service); THD* thd= ss->m_thd; + wsrep_reset_threadvars(thd); delete ss; delete thd; } +Wsrep_applier_service* +wsrep_create_streaming_applier(THD *orig_thd, const char *ctx) +{ + /* Reset variables to allow creating new variables in thread local + storage for new THD if needed. Note that reset must be done for + current_thd, as orig_thd may not be in effect. This may be the case when + streaming transaction is BF aborted and streaming applier + is created from BF aborter context. */ + Wsrep_threadvars saved_threadvars(wsrep_save_threadvars()); + wsrep_reset_threadvars(saved_threadvars.cur_thd); + THD *thd= 0; + Wsrep_applier_service *ret= 0; + if (!wsrep_create_threadvars() && + (thd= new THD(next_thread_id(), true))) + { + init_service_thd(thd, orig_thd->thread_stack); + wsrep_assign_from_threadvars(thd); + WSREP_DEBUG("Created streaming applier service in %s context with " + "thread id %llu", ctx, thd->thread_id); + if (!(ret= new (std::nothrow) Wsrep_applier_service(thd))) + { + delete thd; + } + } + /* Restore original thread local storage state before returning. */ + wsrep_restore_threadvars(saved_threadvars); + wsrep_store_threadvars(saved_threadvars.cur_thd); + return ret; +} + wsrep::high_priority_service* Wsrep_server_service::streaming_applier_service( wsrep::client_service& orig_client_service) { Wsrep_client_service& orig_cs= static_cast<Wsrep_client_service&>(orig_client_service); - THD* thd= new THD(next_thread_id(), true); - init_service_thd(thd, orig_cs.m_thd->thread_stack); - WSREP_DEBUG("Created streaming applier service in local context with " - "thread id %llu", thd->thread_id); - return new Wsrep_applier_service(thd); + return wsrep_create_streaming_applier(orig_cs.m_thd, "local"); } wsrep::high_priority_service* @@ -94,11 +127,7 @@ Wsrep_server_service::streaming_applier_service( { Wsrep_high_priority_service& orig_hps(static_cast<Wsrep_high_priority_service&>(orig_high_priority_service)); - THD* thd= new THD(next_thread_id(), true); - init_service_thd(thd, orig_hps.m_thd->thread_stack); - WSREP_DEBUG("Created streaming applier service in high priority " - "context with thread id %llu", thd->thread_id); - return new Wsrep_applier_service(thd); + return wsrep_create_streaming_applier(orig_hps.m_thd, "high priority"); } void Wsrep_server_service::release_high_priority_service(wsrep::high_priority_service* high_priority_service) @@ -107,7 +136,9 @@ void Wsrep_server_service::release_high_priority_service(wsrep::high_priority_se static_cast<Wsrep_high_priority_service*>(high_priority_service); THD* thd= hps->m_thd; delete hps; + wsrep_store_threadvars(thd); delete thd; + wsrep_delete_threadvars(); } void Wsrep_server_service::background_rollback(wsrep::client_state& client_state) diff --git a/sql/wsrep_server_service.h b/sql/wsrep_server_service.h index b8f1f009cde..6336fe2c473 100644 --- a/sql/wsrep_server_service.h +++ b/sql/wsrep_server_service.h @@ -77,5 +77,14 @@ private: Wsrep_server_state& m_server_state; }; +/** + Helper method to create new streaming applier. + + @param orig_thd Original thd context to copy operation context from. + @param ctx Context string for debug logging. + */ +class Wsrep_applier_service; +Wsrep_applier_service* +wsrep_create_streaming_applier(THD *orig_thd, const char *ctx); #endif /* WSREP_SERVER_SERVICE */ diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc index 85d5aca342d..74a8b9dff05 100644 --- a/sql/wsrep_sst.cc +++ b/sql/wsrep_sst.cc @@ -16,6 +16,7 @@ #include "mariadb.h" #include "wsrep_sst.h" #include <inttypes.h> +#include <ctype.h> #include <mysqld.h> #include <m_ctype.h> #include <strfunc.h> @@ -27,6 +28,8 @@ #include "wsrep_priv.h" #include "wsrep_utils.h" #include "wsrep_xid.h" +#include "wsrep_thd.h" + #include <cstdio> #include <cstdlib> @@ -237,7 +240,7 @@ void wsrep_sst_received (THD* thd, wsrep thread pool. Restore original thd context before returning. */ if (thd) { - thd->store_globals(); + wsrep_store_threadvars(thd); } else { my_pthread_setspecific_ptr(THR_THD, NULL); @@ -307,7 +310,31 @@ static char* my_fgets (char* buf, size_t buf_len, FILE* stream) } /* - Generate opt_binlog_opt_val for sst_donate_other(), sst_prepare_other(). + Generate "name 'value'" string. +*/ +static char* generate_name_value(const char* name, const char* value) +{ + size_t name_len= strlen(name); + size_t value_len= strlen(value); + char* buf= + (char*) my_malloc((name_len + value_len + 5) * sizeof(char), MYF(0)); + if (buf) + { + char* ref= buf; + *ref++ = ' '; + memcpy(ref, name, name_len * sizeof(char)); + ref += name_len; + *ref++ = ' '; + *ref++ = '\''; + memcpy(ref, value, value_len * sizeof(char)); + ref += value_len; + *ref++ = '\''; + *ref = 0; + } + return buf; +} +/* + Generate binlog option string for sst_donate_other(), sst_prepare_other(). Returns zero on success, negative error code otherwise. @@ -323,7 +350,9 @@ static int generate_binlog_opt_val(char** ret) { assert(opt_bin_logname); *ret= strcmp(opt_bin_logname, "0") ? - my_strdup(opt_bin_logname, MYF(0)) : my_strdup("", MYF(0)); + generate_name_value(WSREP_SST_OPT_BINLOG, + opt_bin_logname) : + my_strdup("", MYF(0)); } else { @@ -340,7 +369,9 @@ static int generate_binlog_index_opt_val(char** ret) if (opt_binlog_index_name) { *ret= strcmp(opt_binlog_index_name, "0") ? - my_strdup(opt_binlog_index_name, MYF(0)) : my_strdup("", MYF(0)); + generate_name_value(WSREP_SST_OPT_BINLOG_INDEX, + opt_binlog_index_name) : + my_strdup("", MYF(0)); } else { @@ -509,7 +540,8 @@ err: thd->system_thread= SYSTEM_THREAD_GENERIC; thd->real_id= pthread_self(); - thd->store_globals(); + wsrep_assign_from_threadvars(thd); + wsrep_store_threadvars(thd); /* */ thd->variables.wsrep_on = 0; @@ -576,23 +608,320 @@ static int sst_append_data_dir(wsp::env& env, const char* data_dir) return -env.error(); } +static size_t estimate_cmd_len (bool* extra_args) +{ + /* + The length of the area reserved for the control parameters + of the SST script (excluding the copying of the original + mysqld arguments): + */ + size_t cmd_len= 4096; + bool extra= false; + /* + If mysqld was started with arguments, add them all: + */ + if (orig_argc > 1) + { + for (int i = 1; i < orig_argc; i++) + { + const char* arg= orig_argv[i]; + size_t n= strlen(arg); + if (n == 0) continue; + cmd_len += n; + bool quotation= false; + char c; + while ((c = *arg++) != 0) + { + /* A whitespace or a single quote requires double quotation marks: */ + if (isspace(c) || c == '\'') + { + quotation= true; + } + /* + If the equals symbol is encountered, then we need to separately + process the right side: + */ + else if (c == '=') + { + /* Perhaps we need to quote the left part of the argument: */ + if (quotation) + { + cmd_len += 2; + /* + Reset the quotation flag, since now the status for + the right side of the expression will be saved here: + */ + quotation= false; + } + while ((c = *arg++) != 0) + { + /* + A whitespace or a single quote requires double + quotation marks: + */ + if (isspace(c) || c == '\'') + { + quotation= true; + } + /* + Double quotation mark or backslash symbol requires backslash + prefixing: + */ +#ifdef __WIN__ + else if (c == '"' || c == '\\') +#else + /* + The dollar symbol is used to substitute a variable, therefore + it also requires escaping: + */ + else if (c == '"' || c == '\\' || c == '$') +#endif + { + cmd_len++; + } + } + break; + } + /* + Double quotation mark or backslash symbol requires backslash + prefixing: + */ +#ifdef __WIN__ + else if (c == '"' || c == '\\') +#else + /* + The dollar symbol is used to substitute a variable, therefore + it also requires escaping: + */ + else if (c == '"' || c == '\\' || c == '$') +#endif + { + cmd_len++; + } + } + /* Perhaps we need to quote the entire argument or its right part: */ + if (quotation) + { + cmd_len += 2; + } + } + extra = true; + cmd_len += strlen(WSREP_SST_OPT_MYSQLD); + /* + Add the separating spaces between arguments, + and one additional space before "--mysqld-args": + */ + cmd_len += orig_argc; + } + *extra_args= extra; + return cmd_len; +} + +static void copy_orig_argv (char* cmd_str) +{ + /* + If mysqld was started with arguments, copy them all: + */ + if (orig_argc > 1) + { + size_t n = strlen(WSREP_SST_OPT_MYSQLD); + *cmd_str++ = ' '; + memcpy(cmd_str, WSREP_SST_OPT_MYSQLD, n * sizeof(char)); + cmd_str += n; + for (int i = 1; i < orig_argc; i++) + { + char* arg= orig_argv[i]; + n = strlen(arg); + if (n == 0) continue; + *cmd_str++ = ' '; + bool quotation= false; + bool plain= true; + char *arg_scan= arg; + char c; + while ((c = *arg_scan++) != 0) + { + /* A whitespace or a single quote requires double quotation marks: */ + if (isspace(c) || c == '\'') + { + quotation= true; + } + /* + If the equals symbol is encountered, then we need to separately + process the right side: + */ + else if (c == '=') + { + /* Calculate length of the Left part of the argument: */ + size_t m = (size_t) (arg_scan - arg) - 1; + if (m) + { + /* Perhaps we need to quote the left part of the argument: */ + if (quotation) + { + *cmd_str++ = '"'; + } + /* + If there were special characters inside, then we can use + the fast memcpy function: + */ + if (plain) + { + memcpy(cmd_str, arg, m * sizeof(char)); + cmd_str += m; + /* Left part of the argument has already been processed: */ + n -= m; + arg += m; + } + /* Otherwise we need to prefix individual characters: */ + else + { + n -= m; + while (m) + { + c = *arg++; +#ifdef __WIN__ + if (c == '"' || c == '\\') +#else + if (c == '"' || c == '\\' || c == '$') +#endif + { + *cmd_str++ = '\\'; + } + *cmd_str++ = c; + m--; + } + /* + Reset the plain string flag, since now the status for + the right side of the expression will be saved here: + */ + plain= true; + } + /* Perhaps we need to quote the left part of the argument: */ + if (quotation) + { + *cmd_str++ = '"'; + /* + Reset the quotation flag, since now the status for + the right side of the expression will be saved here: + */ + quotation= false; + } + } + /* Copy equals symbol: */ + *cmd_str++ = '='; + arg++; + n--; + /* Let's deal with the left side of the expression: */ + while ((c = *arg_scan++) != 0) + { + /* + A whitespace or a single quote requires double + quotation marks: + */ + if (isspace(c) || c == '\'') + { + quotation= true; + } + /* + Double quotation mark or backslash symbol requires backslash + prefixing: + */ +#ifdef __WIN__ + else if (c == '"' || c == '\\') +#else + /* + The dollar symbol is used to substitute a variable, therefore + it also requires escaping: + */ + else if (c == '"' || c == '\\' || c == '$') +#endif + { + plain= false; + } + } + break; + } + /* + Double quotation mark or backslash symbol requires backslash + prefixing: + */ +#ifdef __WIN__ + else if (c == '"' || c == '\\') +#else + /* + The dollar symbol is used to substitute a variable, therefore + it also requires escaping: + */ + else if (c == '"' || c == '\\' || c == '$') +#endif + { + plain= false; + } + } + if (n) + { + /* Perhaps we need to quote the entire argument or its right part: */ + if (quotation) + { + *cmd_str++ = '"'; + } + /* + If there were no special characters inside, then we can use + the fast memcpy function: + */ + if (plain) + { + memcpy(cmd_str, arg, n * sizeof(char)); + cmd_str += n; + } + /* Otherwise we need to prefix individual characters: */ + else + { + while ((c = *arg++) != 0) + { +#ifdef __WIN__ + if (c == '"' || c == '\\') +#else + if (c == '"' || c == '\\' || c == '$') +#endif + { + *cmd_str++ = '\\'; + } + *cmd_str++ = c; + } + } + /* Perhaps we need to quote the entire argument or its right part: */ + if (quotation) + { + *cmd_str++ = '"'; + } + } + } + /* + Add a terminating null character (not counted in the length, + since we've overwritten the original null character which + was previously added by snprintf: + */ + *cmd_str = 0; + } +} + static ssize_t sst_prepare_other (const char* method, const char* sst_auth, const char* addr_in, const char** addr_out) { - int const cmd_len= 4096; + bool extra_args; + size_t const cmd_len= estimate_cmd_len(&extra_args); wsp::string cmd_str(cmd_len); if (!cmd_str()) { - WSREP_ERROR("sst_prepare_other(): could not allocate cmd buffer of %d bytes", + WSREP_ERROR("sst_prepare_other(): could not allocate cmd buffer of %zd bytes", cmd_len); return -ENOMEM; } - const char* binlog_opt= ""; - const char* binlog_index_opt= ""; char* binlog_opt_val= NULL; char* binlog_index_opt_val= NULL; @@ -610,9 +939,6 @@ static ssize_t sst_prepare_other (const char* method, ret); } - if (strlen(binlog_opt_val)) binlog_opt= WSREP_SST_OPT_BINLOG; - if (strlen(binlog_index_opt_val)) binlog_index_opt= WSREP_SST_OPT_BINLOG_INDEX; - make_wsrep_defaults_file(); ret= snprintf (cmd_str(), cmd_len, @@ -620,23 +946,26 @@ static ssize_t sst_prepare_other (const char* method, WSREP_SST_OPT_ROLE " 'joiner' " WSREP_SST_OPT_ADDR " '%s' " WSREP_SST_OPT_DATA " '%s' " - " %s " + "%s" WSREP_SST_OPT_PARENT " '%d'" - " %s '%s'" - " %s '%s'", + "%s" + "%s", method, addr_in, mysql_real_data_home, wsrep_defaults_file, - (int)getpid(), binlog_opt, binlog_opt_val, - binlog_index_opt, binlog_index_opt_val); + (int)getpid(), + binlog_opt_val, binlog_index_opt_val); my_free(binlog_opt_val); my_free(binlog_index_opt_val); - if (ret < 0 || ret >= cmd_len) + if (ret < 0 || size_t(ret) >= cmd_len) { WSREP_ERROR("sst_prepare_other(): snprintf() failed: %d", ret); return (ret < 0 ? ret : -EMSGSIZE); } + if (extra_args) + copy_orig_argv(cmd_str() + ret); + wsp::env env(NULL); if (env.error()) { @@ -890,13 +1219,14 @@ static int sst_donate_mysqldump (const char* addr, } memcpy(host, address.get_address(), address.get_address_len()); int port= address.get_port(); - int const cmd_len= 4096; - wsp::string cmd_str(cmd_len); + bool extra_args; + size_t const cmd_len= estimate_cmd_len(&extra_args); + wsp::string cmd_str(cmd_len); if (!cmd_str()) { WSREP_ERROR("sst_donate_mysqldump(): " - "could not allocate cmd buffer of %d bytes", cmd_len); + "could not allocate cmd buffer of %zd bytes", cmd_len); return -ENOMEM; } @@ -917,7 +1247,7 @@ static int sst_donate_mysqldump (const char* addr, WSREP_SST_OPT_PORT " '%u' " WSREP_SST_OPT_LPORT " '%u' " WSREP_SST_OPT_SOCKET " '%s' " - " %s " + "%s" WSREP_SST_OPT_GTID " '%s:%lld' " WSREP_SST_OPT_GTID_DOMAIN_ID " '%d'" "%s", @@ -927,12 +1257,15 @@ static int sst_donate_mysqldump (const char* addr, wsrep_gtid_domain_id, bypass ? " " WSREP_SST_OPT_BYPASS : ""); - if (ret < 0 || ret >= cmd_len) + if (ret < 0 || size_t(ret) >= cmd_len) { WSREP_ERROR("sst_donate_mysqldump(): snprintf() failed: %d", ret); return (ret < 0 ? ret : -EMSGSIZE); } + if (extra_args) + copy_orig_argv(cmd_str() + ret); + WSREP_DEBUG("Running: '%s'", cmd_str()); ret= sst_run_shell (cmd_str(), env, 3); @@ -1283,18 +1616,17 @@ static int sst_donate_other (const char* method, bool bypass, char** env) // carries auth info { - int const cmd_len= 4096; - wsp::string cmd_str(cmd_len); + bool extra_args; + size_t const cmd_len= estimate_cmd_len(&extra_args); + wsp::string cmd_str(cmd_len); if (!cmd_str()) { WSREP_ERROR("sst_donate_other(): " - "could not allocate cmd buffer of %d bytes", cmd_len); + "could not allocate cmd buffer of %zd bytes", cmd_len); return -ENOMEM; } - const char* binlog_opt= ""; - const char* binlog_index_opt= ""; char* binlog_opt_val= NULL; char* binlog_index_opt_val= NULL; @@ -1311,9 +1643,6 @@ static int sst_donate_other (const char* method, ret); } - if (strlen(binlog_opt_val)) binlog_opt= WSREP_SST_OPT_BINLOG; - if (strlen(binlog_index_opt_val)) binlog_index_opt= WSREP_SST_OPT_BINLOG_INDEX; - make_wsrep_defaults_file(); std::ostringstream uuid_oss; @@ -1324,27 +1653,30 @@ static int sst_donate_other (const char* method, WSREP_SST_OPT_ADDR " '%s' " WSREP_SST_OPT_SOCKET " '%s' " WSREP_SST_OPT_DATA " '%s' " - " %s " - " %s '%s' " - " %s '%s' " + "%s" WSREP_SST_OPT_GTID " '%s:%lld' " WSREP_SST_OPT_GTID_DOMAIN_ID " '%d'" + "%s" + "%s" "%s", method, addr, mysqld_unix_port, mysql_real_data_home, wsrep_defaults_file, - binlog_opt, binlog_opt_val, - binlog_index_opt, binlog_index_opt_val, uuid_oss.str().c_str(), gtid.seqno().get(), wsrep_gtid_domain_id, + binlog_opt_val, binlog_index_opt_val, bypass ? " " WSREP_SST_OPT_BYPASS : ""); + my_free(binlog_opt_val); my_free(binlog_index_opt_val); - if (ret < 0 || ret >= cmd_len) + if (ret < 0 || size_t(ret) >= cmd_len) { WSREP_ERROR("sst_donate_other(): snprintf() failed: %d", ret); return (ret < 0 ? ret : -EMSGSIZE); } + if (extra_args) + copy_orig_argv(cmd_str() + ret); + if (!bypass && wsrep_sst_donor_rejects_queries) sst_reject_queries(FALSE); pthread_t tmp; diff --git a/sql/wsrep_sst.h b/sql/wsrep_sst.h index eb218647bc0..2389db4abe7 100644 --- a/sql/wsrep_sst.h +++ b/sql/wsrep_sst.h @@ -32,6 +32,7 @@ #define WSREP_SST_OPT_PARENT "--parent" #define WSREP_SST_OPT_BINLOG "--binlog" #define WSREP_SST_OPT_BINLOG_INDEX "--binlog-index" +#define WSREP_SST_OPT_MYSQLD "--mysqld-args" // mysqldump-specific options #define WSREP_SST_OPT_USER "--user" diff --git a/sql/wsrep_storage_service.cc b/sql/wsrep_storage_service.cc index e164114b733..6dfe3eee448 100644 --- a/sql/wsrep_storage_service.cc +++ b/sql/wsrep_storage_service.cc @@ -196,18 +196,10 @@ int Wsrep_storage_service::rollback(const wsrep::ws_handle& ws_handle, void Wsrep_storage_service::store_globals() { - DBUG_ENTER("Wsrep_storage_service::store_globals"); - DBUG_PRINT("info", ("Wsrep_storage_service::store_globals(%llu, %p)", - m_thd->thread_id, m_thd)); - m_thd->store_globals(); - DBUG_VOID_RETURN; + wsrep_store_threadvars(m_thd); } void Wsrep_storage_service::reset_globals() { - DBUG_ENTER("Wsrep_storage_service::reset_globals"); - DBUG_PRINT("info", ("Wsrep_storage_service::reset_globals(%llu, %p)", - m_thd->thread_id, m_thd)); - m_thd->reset_globals(); - DBUG_VOID_RETURN; + wsrep_reset_threadvars(m_thd); } diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc index 5907d495ee9..50f0376f674 100644 --- a/sql/wsrep_thd.cc +++ b/sql/wsrep_thd.cc @@ -31,8 +31,9 @@ #include "rpl_rli.h" #include "rpl_mi.h" +extern "C" pthread_key(struct st_my_thread_var*, THR_KEY_mysys); + static Wsrep_thd_queue* wsrep_rollback_queue= 0; -static Wsrep_thd_queue* wsrep_post_rollback_queue= 0; static Atomic_counter<uint64_t> wsrep_bf_aborts_counter; @@ -86,7 +87,7 @@ static void wsrep_replication_process(THD *thd, static bool create_wsrep_THD(Wsrep_thd_args* args) { ulong old_wsrep_running_threads= wsrep_running_threads; - pthread_t unused; + #ifdef HAVE_PSI_THREAD_INTERFACE PSI_thread_key key; @@ -100,10 +101,11 @@ static bool create_wsrep_THD(Wsrep_thd_args* args) break; default: assert(0); + key= 0; break; } #endif - bool res= mysql_thread_create(key, &unused, &connection_attrib, + bool res= mysql_thread_create(key, args->thread_id(), &connection_attrib, start_wsrep_THD, (void*)args); /* if starting a thread on server startup, wait until the this thread's THD @@ -123,9 +125,9 @@ void wsrep_create_appliers(long threads) /* Dont' start slave threads if wsrep-provider or wsrep-cluster-address is not set. */ - if (!WSREP_PROVIDER_EXISTS) + if (!WSREP_PROVIDER_EXISTS) { - return; + return; } if (!wsrep_cluster_address || wsrep_cluster_address[0]== 0) @@ -135,11 +137,12 @@ void wsrep_create_appliers(long threads) } long wsrep_threads=0; - + while (wsrep_threads++ < threads) { - Wsrep_thd_args* args(new Wsrep_thd_args(wsrep_replication_process, 0, - WSREP_APPLIER_THREAD)); + Wsrep_thd_args* args(new Wsrep_thd_args(wsrep_replication_process, + WSREP_APPLIER_THREAD, + pthread_self())); if (create_wsrep_THD(args)) { WSREP_WARN("Can't create thread to manage wsrep replication"); @@ -147,6 +150,122 @@ void wsrep_create_appliers(long threads) } } +static void wsrep_rollback_streaming_aborted_by_toi(THD *thd) +{ + WSREP_INFO("wsrep_rollback_streaming_aborted_by_toi"); + /* Set thd->event_scheduler.data temporarily to NULL to avoid + callbacks to threadpool wait_begin() during rollback. */ + auto saved_esd= thd->event_scheduler.data; + thd->event_scheduler.data= 0; + if (thd->wsrep_cs().mode() == wsrep::client_state::m_high_priority) + { + DBUG_ASSERT(!saved_esd); + DBUG_ASSERT(thd->wsrep_applier_service); + thd->wsrep_applier_service->rollback(wsrep::ws_handle(), + wsrep::ws_meta()); + thd->wsrep_applier_service->after_apply(); + /* Will free THD */ + Wsrep_server_state::instance().server_service(). + release_high_priority_service(thd->wsrep_applier_service); + } + else + { + mysql_mutex_lock(&thd->LOCK_thd_data); + /* prepare THD for rollback processing */ + thd->reset_for_next_command(true); + thd->lex->sql_command= SQLCOM_ROLLBACK; + mysql_mutex_unlock(&thd->LOCK_thd_data); + /* Perform a client rollback, restore globals and signal + the victim only when all the resources have been + released */ + thd->wsrep_cs().client_service().bf_rollback(); + wsrep_reset_threadvars(thd); + /* Assign saved event_scheduler.data back before letting + client to continue. */ + thd->event_scheduler.data= saved_esd; + thd->wsrep_cs().sync_rollback_complete(); + } +} + +static void wsrep_rollback_high_priority(THD *thd) +{ + WSREP_INFO("rollbacker aborting SR thd: (%lld %llu)", + thd->thread_id, (long long)thd->real_id); + DBUG_ASSERT(thd->wsrep_cs().mode() == Wsrep_client_state::m_high_priority); + /* Must be streaming and must have been removed from the + server state streaming appliers map. */ + DBUG_ASSERT(thd->wsrep_trx().is_streaming()); + DBUG_ASSERT(!Wsrep_server_state::instance().find_streaming_applier( + thd->wsrep_trx().server_id(), + thd->wsrep_trx().id())); + DBUG_ASSERT(thd->wsrep_applier_service); + + /* Fragment removal should happen before rollback to make + the transaction non-observable in SR table after the rollback + completes. For correctness the order does not matter here, + but currently it is mandated by checks in some MTR tests. */ + wsrep::transaction_id transaction_id(thd->wsrep_trx().id()); + Wsrep_storage_service* storage_service= + static_cast<Wsrep_storage_service*>( + Wsrep_server_state::instance().server_service().storage_service( + *thd->wsrep_applier_service)); + storage_service->store_globals(); + storage_service->adopt_transaction(thd->wsrep_trx()); + storage_service->remove_fragments(); + storage_service->commit(wsrep::ws_handle(transaction_id, 0), + wsrep::ws_meta()); + Wsrep_server_state::instance().server_service().release_storage_service(storage_service); + wsrep_store_threadvars(thd); + thd->wsrep_applier_service->rollback(wsrep::ws_handle(), + wsrep::ws_meta()); + thd->wsrep_applier_service->after_apply(); + /* Will free THD */ + Wsrep_server_state::instance().server_service() + .release_high_priority_service(thd->wsrep_applier_service); +} + +static void wsrep_rollback_local(THD *thd) +{ + WSREP_INFO("Wsrep_rollback_local"); + if (thd->wsrep_trx().is_streaming()) + { + wsrep::transaction_id transaction_id(thd->wsrep_trx().id()); + Wsrep_storage_service* storage_service= + static_cast<Wsrep_storage_service*>( + Wsrep_server_state::instance().server_service(). + storage_service(thd->wsrep_cs().client_service())); + + storage_service->store_globals(); + storage_service->adopt_transaction(thd->wsrep_trx()); + storage_service->remove_fragments(); + storage_service->commit(wsrep::ws_handle(transaction_id, 0), + wsrep::ws_meta()); + Wsrep_server_state::instance().server_service(). + release_storage_service(storage_service); + wsrep_store_threadvars(thd); + } + /* Set thd->event_scheduler.data temporarily to NULL to avoid + callbacks to threadpool wait_begin() during rollback. */ + auto saved_esd= thd->event_scheduler.data; + thd->event_scheduler.data= 0; + mysql_mutex_lock(&thd->LOCK_thd_data); + /* prepare THD for rollback processing */ + thd->reset_for_next_command(); + thd->lex->sql_command= SQLCOM_ROLLBACK; + mysql_mutex_unlock(&thd->LOCK_thd_data); + /* Perform a client rollback, restore globals and signal + the victim only when all the resources have been + released */ + thd->wsrep_cs().client_service().bf_rollback(); + wsrep_reset_threadvars(thd); + /* Assign saved event_scheduler.data back before letting + client to continue. */ + thd->event_scheduler.data= saved_esd; + thd->wsrep_cs().sync_rollback_complete(); + WSREP_DEBUG("rollbacker aborted thd: (%llu %llu)", + thd->thread_id, (long long)thd->real_id); +} + static void wsrep_rollback_process(THD *rollbacker, void *arg __attribute__((unused))) { @@ -168,119 +287,36 @@ static void wsrep_rollback_process(THD *rollbacker, WSREP_DEBUG("rollbacker thd already aborted: %llu state: %d", (long long)thd->real_id, tx.state()); - mysql_mutex_unlock(&thd->LOCK_thd_data); continue; } mysql_mutex_unlock(&thd->LOCK_thd_data); + wsrep_reset_threadvars(rollbacker); + wsrep_store_threadvars(thd); + thd->wsrep_cs().acquire_ownership(); + thd_proc_info(rollbacker, "wsrep aborter active"); - wsrep::transaction_id transaction_id(thd->wsrep_trx().id()); + /* Rollback methods below may free thd pointer. Do not try + to access it after method returns. */ if (thd->wsrep_trx().is_streaming() && thd->wsrep_trx().bf_aborted_in_total_order()) { - thd->store_globals(); - thd->wsrep_cs().store_globals(); - if (thd->wsrep_cs().mode() == wsrep::client_state::m_high_priority) - { - DBUG_ASSERT(thd->wsrep_applier_service); - thd->wsrep_applier_service->rollback(wsrep::ws_handle(), - wsrep::ws_meta()); - thd->wsrep_applier_service->after_apply(); - /* Will free THD */ - Wsrep_server_state::instance().server_service(). - release_high_priority_service(thd->wsrep_applier_service); - } - else - { - mysql_mutex_lock(&thd->LOCK_thd_data); - /* prepare THD for rollback processing */ - thd->reset_for_next_command(true); - thd->lex->sql_command= SQLCOM_ROLLBACK; - mysql_mutex_unlock(&thd->LOCK_thd_data); - /* Perform a client rollback, restore globals and signal - the victim only when all the resources have been - released */ - thd->wsrep_cs().client_service().bf_rollback(); - thd->reset_globals(); - thd->wsrep_cs().sync_rollback_complete(); - } + wsrep_rollback_streaming_aborted_by_toi(thd); } else if (wsrep_thd_is_applying(thd)) { - WSREP_DEBUG("rollbacker aborting SR thd: (%lld %llu)", - thd->thread_id, (long long)thd->real_id); - DBUG_ASSERT(thd->wsrep_cs().mode() == Wsrep_client_state::m_high_priority); - /* Must be streaming and must have been removed from the - server state streaming appliers map. */ - DBUG_ASSERT(thd->wsrep_trx().is_streaming()); - DBUG_ASSERT(!Wsrep_server_state::instance().find_streaming_applier( - thd->wsrep_trx().server_id(), - thd->wsrep_trx().id())); - DBUG_ASSERT(thd->wsrep_applier_service); - - /* Fragment removal should happen before rollback to make - the transaction non-observable in SR table after the rollback - completes. For correctness the order does not matter here, - but currently it is mandated by checks in some MTR tests. */ - Wsrep_storage_service* storage_service= - static_cast<Wsrep_storage_service*>( - Wsrep_server_state::instance().server_service().storage_service( - *thd->wsrep_applier_service)); - storage_service->store_globals(); - storage_service->adopt_transaction(thd->wsrep_trx()); - storage_service->remove_fragments(); - storage_service->commit(wsrep::ws_handle(transaction_id, 0), - wsrep::ws_meta()); - Wsrep_server_state::instance().server_service().release_storage_service(storage_service); - thd->store_globals(); - thd->wsrep_cs().store_globals(); - thd->wsrep_applier_service->rollback(wsrep::ws_handle(), - wsrep::ws_meta()); - thd->wsrep_applier_service->after_apply(); - /* Will free THD */ - Wsrep_server_state::instance().server_service() - .release_high_priority_service(thd->wsrep_applier_service); - + wsrep_rollback_high_priority(thd); } else { - if (thd->wsrep_trx().is_streaming()) - { - Wsrep_storage_service* storage_service= - static_cast<Wsrep_storage_service*>( - Wsrep_server_state::instance().server_service(). - storage_service(thd->wsrep_cs().client_service())); - - storage_service->store_globals(); - storage_service->adopt_transaction(thd->wsrep_trx()); - storage_service->remove_fragments(); - storage_service->commit(wsrep::ws_handle(transaction_id, 0), - wsrep::ws_meta()); - Wsrep_server_state::instance().server_service(). - release_storage_service(storage_service); - } - thd->store_globals(); - thd->wsrep_cs().store_globals(); - mysql_mutex_lock(&thd->LOCK_thd_data); - /* prepare THD for rollback processing */ - thd->reset_for_next_command(); - thd->lex->sql_command= SQLCOM_ROLLBACK; - mysql_mutex_unlock(&thd->LOCK_thd_data); - /* Perform a client rollback, restore globals and signal - the victim only when all the resources have been - released */ - thd->wsrep_cs().client_service().bf_rollback(); - thd->reset_globals(); - thd->wsrep_cs().sync_rollback_complete(); - WSREP_DEBUG("rollbacker aborted thd: (%llu %llu)", - thd->thread_id, (long long)thd->real_id); + wsrep_rollback_local(thd); } - + wsrep_store_threadvars(rollbacker); thd_proc_info(rollbacker, "wsrep aborter idle"); } - + delete wsrep_rollback_queue; wsrep_rollback_queue= NULL; @@ -291,55 +327,17 @@ static void wsrep_rollback_process(THD *rollbacker, DBUG_VOID_RETURN; } -static void wsrep_post_rollback_process(THD *post_rollbacker, - void *arg __attribute__((unused))) -{ - DBUG_ENTER("wsrep_post_rollback_process"); - THD* thd= NULL; - - WSREP_INFO("Starting post rollbacker thread %llu", post_rollbacker->thread_id); - DBUG_ASSERT(!wsrep_post_rollback_queue); - wsrep_post_rollback_queue= new Wsrep_thd_queue(post_rollbacker); - - while ((thd= wsrep_post_rollback_queue->pop_front()) != NULL) - { - thd->store_globals(); - wsrep::client_state& cs(thd->wsrep_cs()); - mysql_mutex_lock(&thd->LOCK_thd_data); - DBUG_ASSERT(thd->wsrep_trx().state() == wsrep::transaction::s_aborting); - WSREP_DEBUG("post rollbacker calling post rollback for thd %llu, conf %s", - thd->thread_id, wsrep_thd_transaction_state_str(thd)); - - cs.after_rollback(); - DBUG_ASSERT(thd->wsrep_trx().state() == wsrep::transaction::s_aborted); - mysql_mutex_unlock(&thd->LOCK_thd_data); - } - - delete wsrep_post_rollback_queue; - wsrep_post_rollback_queue= NULL; - - DBUG_ASSERT(post_rollbacker->killed != NOT_KILLED); - DBUG_PRINT("wsrep",("wsrep post rollbacker thread exiting")); - WSREP_INFO("post rollbacker thread exiting %llu", post_rollbacker->thread_id); - DBUG_VOID_RETURN; -} - void wsrep_create_rollbacker() { if (wsrep_cluster_address && wsrep_cluster_address[0] != 0) { - Wsrep_thd_args* args= new Wsrep_thd_args(wsrep_rollback_process, 0, - WSREP_ROLLBACKER_THREAD); + Wsrep_thd_args* args(new Wsrep_thd_args(wsrep_rollback_process, + WSREP_ROLLBACKER_THREAD, + pthread_self())); /* create rollbacker */ if (create_wsrep_THD(args)) WSREP_WARN("Can't create thread to manage wsrep rollback"); - - /* create post_rollbacker */ - args= new Wsrep_thd_args(wsrep_post_rollback_process, 0, - WSREP_ROLLBACKER_THREAD); - if (create_wsrep_THD(args)) - WSREP_WARN("Can't create thread to manage wsrep post rollback"); } } @@ -433,3 +431,84 @@ void wsrep_thd_auto_increment_variables(THD* thd, *offset= thd->variables.auto_increment_offset; *increment= thd->variables.auto_increment_increment; } + +int wsrep_create_threadvars() +{ + int ret= 0; + if (thread_handling == SCHEDULER_TYPES_COUNT) + { + /* Caller should have called wsrep_reset_threadvars() before this + method. */ + DBUG_ASSERT(!pthread_getspecific(THR_KEY_mysys)); + pthread_setspecific(THR_KEY_mysys, 0); + ret= my_thread_init(); + } + return ret; +} + +void wsrep_delete_threadvars() +{ + if (thread_handling == SCHEDULER_TYPES_COUNT) + { + /* The caller should have called wsrep_store_threadvars() before + this method. */ + DBUG_ASSERT(pthread_getspecific(THR_KEY_mysys)); + /* Reset psi state to avoid deallocating applier thread + psi_thread. */ + PSI_thread *psi_thread= PSI_CALL_get_thread(); +#ifdef HAVE_PSI_INTERFACE + if (PSI_server) + { + PSI_server->set_thread(0); + } +#endif /* HAVE_PSI_INTERFACE */ + my_thread_end(); + PSI_CALL_set_thread(psi_thread); + pthread_setspecific(THR_KEY_mysys, 0); + } +} + +void wsrep_assign_from_threadvars(THD *thd) +{ + if (thread_handling == SCHEDULER_TYPES_COUNT) + { + st_my_thread_var *mysys_var= (st_my_thread_var *)pthread_getspecific(THR_KEY_mysys); + DBUG_ASSERT(mysys_var); + thd->set_mysys_var(mysys_var); + } +} + +Wsrep_threadvars wsrep_save_threadvars() +{ + return Wsrep_threadvars{ + current_thd, + (st_my_thread_var*) pthread_getspecific(THR_KEY_mysys) + }; +} + +void wsrep_restore_threadvars(const Wsrep_threadvars& globals) +{ + set_current_thd(globals.cur_thd); + pthread_setspecific(THR_KEY_mysys, globals.mysys_var); +} + +int wsrep_store_threadvars(THD *thd) +{ + if (thread_handling == SCHEDULER_TYPES_COUNT) + { + pthread_setspecific(THR_KEY_mysys, thd->mysys_var); + } + return thd->store_globals(); +} + +void wsrep_reset_threadvars(THD *thd) +{ + if (thread_handling == SCHEDULER_TYPES_COUNT) + { + pthread_setspecific(THR_KEY_mysys, 0); + } + else + { + thd->reset_globals(); + } +} diff --git a/sql/wsrep_thd.h b/sql/wsrep_thd.h index 2eceb3223a8..872570cd028 100644 --- a/sql/wsrep_thd.h +++ b/sql/wsrep_thd.h @@ -82,13 +82,8 @@ private: mysql_cond_t COND_wsrep_thd_queue; }; -void wsrep_prepare_bf_thd(THD*, struct wsrep_thd_shadow*); -void wsrep_return_from_bf_mode(THD*, struct wsrep_thd_shadow*); - int wsrep_show_bf_aborts (THD *thd, SHOW_VAR *var, char *buff, enum enum_var_type scope); -void wsrep_client_rollback(THD *thd, bool rollbacker = false); -void wsrep_replay_transaction(THD *thd); void wsrep_create_appliers(long threads); void wsrep_create_rollbacker(); @@ -96,8 +91,83 @@ bool wsrep_bf_abort(const THD*, THD*); int wsrep_abort_thd(void *bf_thd_ptr, void *victim_thd_ptr, my_bool signal); extern void wsrep_thd_set_PA_safe(void *thd_ptr, my_bool safe); -THD* wsrep_start_SR_THD(char *thread_stack); -void wsrep_end_SR_THD(THD* thd); + +/* + Helper methods to deal with thread local storage. + The purpose of these methods is to hide the details of thread + local storage handling when operating with wsrep storage access + and streaming applier THDs + + With one-thread-per-connection thread handling thread specific + variables are allocated when the thread is started and deallocated + before thread exits (my_thread_init(), my_thread_end()). However, + with pool-of-threads thread handling new thread specific variables + are allocated for each THD separately (see threadpool_add_connection()), + and the variables in thread local storage are assigned from + currently active thread (see thread_attach()). This must be taken into + account when storing/resetting thread local storage and when creating + streaming applier THDs. +*/ + +/** + Create new variables for thread local storage. With + one-thread-per-connection thread handling this is a no op, + with pool-of-threads new variables are created via my_thread_init(). + It is assumed that the caller has called wsrep_reset_threadvars() to clear + the thread local storage before this call. + + @return Zero in case of success, non-zero otherwise. +*/ +int wsrep_create_threadvars(); + +/** + Delete variables which were created by wsrep_create_threadvars(). + The caller must store variables into thread local storage before + this call via wsrep_store_threadvars(). +*/ +void wsrep_delete_threadvars(); + +/** + Assign variables from current thread local storage into THD. + This should be called for THDs whose lifetime is limited to single + thread execution or which may share the operation context with some + parent THD (e.g. storage access) and thus don't require separately + allocated globals. + + With one-thread-per-connection thread handling this is a no-op, + with pool-of-threads the variables which are currently stored into + thread local storage are assigned to THD. +*/ +void wsrep_assign_from_threadvars(THD *); + +/** + Helper struct to save variables from thread local storage. + */ +struct Wsrep_threadvars +{ + THD* cur_thd; + st_my_thread_var* mysys_var; +}; + +/** + Save variables from thread local storage into Wsrep_threadvars struct. + */ +Wsrep_threadvars wsrep_save_threadvars(); + +/** + Restore variables into thread local storage from Wsrep_threadvars struct. +*/ +void wsrep_restore_threadvars(const Wsrep_threadvars&); + +/** + Store variables into thread local storage. +*/ +int wsrep_store_threadvars(THD *); + +/** + Reset thread local storage. +*/ +void wsrep_reset_threadvars(THD *); /** Helper functions to override error status diff --git a/sql/wsrep_trans_observer.h b/sql/wsrep_trans_observer.h index 118525bb908..b8ce7eb42d0 100644 --- a/sql/wsrep_trans_observer.h +++ b/sql/wsrep_trans_observer.h @@ -422,6 +422,17 @@ static inline void wsrep_close(THD* thd) DBUG_VOID_RETURN; } +static inline void +wsrep_wait_rollback_complete_and_acquire_ownership(THD *thd) +{ + DBUG_ENTER("wsrep_wait_rollback_complete_and_acquire_ownership"); + if (thd->wsrep_cs().state() != wsrep::client_state::s_none) + { + thd->wsrep_cs().wait_rollback_complete_and_acquire_ownership(); + } + DBUG_VOID_RETURN; +} + static inline int wsrep_before_command(THD* thd) { return (thd->wsrep_cs().state() != wsrep::client_state::s_none ? diff --git a/sql/wsrep_utils.cc b/sql/wsrep_utils.cc index 52949a95e5d..49ea78a3872 100644 --- a/sql/wsrep_utils.cc +++ b/sql/wsrep_utils.cc @@ -25,6 +25,7 @@ #include "wsrep_api.h" #include "wsrep_utils.h" #include "wsrep_mysqld.h" +#include "wsrep_thd.h" #include <sql_class.h> @@ -421,7 +422,8 @@ thd::thd (my_bool won) : init(), ptr(new THD(0)) if (ptr) { ptr->thread_stack= (char*) &ptr; - ptr->store_globals(); + wsrep_assign_from_threadvars(ptr); + wsrep_store_threadvars(ptr); ptr->variables.option_bits&= ~OPTION_BIN_LOG; // disable binlog ptr->variables.wsrep_on= won; ptr->security_ctx->master_access= ~(ulong)0; |