summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/create_options.cc5
-rw-r--r--sql/field.cc45
-rw-r--r--sql/field.h8
-rw-r--r--sql/ha_partition.cc4
-rw-r--r--sql/handler.cc48
-rw-r--r--sql/item.cc32
-rw-r--r--sql/item.h6
-rw-r--r--sql/item_cmpfunc.h3
-rw-r--r--sql/item_func.cc66
-rw-r--r--sql/item_func.h19
-rw-r--r--sql/item_sum.cc72
-rw-r--r--sql/item_sum.h1
-rw-r--r--sql/mysqld.cc6
-rw-r--r--sql/opt_subselect.cc111
-rw-r--r--sql/opt_subselect.h15
-rw-r--r--sql/partition_element.h23
-rw-r--r--sql/partition_info.cc36
-rw-r--r--sql/partition_info.h7
-rw-r--r--sql/rpl_utility_server.cc40
-rw-r--r--sql/select_handler.cc2
-rw-r--r--sql/service_wsrep.cc16
-rw-r--r--sql/share/errmsg-utf8.txt2
-rw-r--r--sql/sql_base.cc13
-rw-r--r--sql/sql_class.h5
-rw-r--r--sql/sql_connect.cc18
-rw-r--r--sql/sql_explain.cc2
-rw-r--r--sql/sql_insert.cc2
-rw-r--r--sql/sql_lex.cc9
-rw-r--r--sql/sql_parse.cc102
-rw-r--r--sql/sql_partition.cc28
-rw-r--r--sql/sql_select.cc70
-rw-r--r--sql/sql_select.h2
-rw-r--r--sql/sql_show.cc5
-rw-r--r--sql/sql_statistics.cc8
-rw-r--r--sql/sql_string.cc2
-rw-r--r--sql/sql_table.cc86
-rw-r--r--sql/sql_time.cc3
-rw-r--r--sql/sql_time.h2
-rw-r--r--sql/sql_type.cc41
-rw-r--r--sql/sql_type.h139
-rw-r--r--sql/sql_yacc.yy8
-rw-r--r--sql/sql_yacc_ora.yy8
-rw-r--r--sql/table.cc7
-rw-r--r--sql/threadpool_common.cc11
-rw-r--r--sql/wsrep_client_service.cc10
-rw-r--r--sql/wsrep_high_priority_service.cc26
-rw-r--r--sql/wsrep_mysqld.cc12
-rw-r--r--sql/wsrep_mysqld.h15
-rw-r--r--sql/wsrep_schema.cc26
-rw-r--r--sql/wsrep_server_service.cc51
-rw-r--r--sql/wsrep_server_service.h9
-rw-r--r--sql/wsrep_sst.cc406
-rw-r--r--sql/wsrep_sst.h1
-rw-r--r--sql/wsrep_storage_service.cc12
-rw-r--r--sql/wsrep_thd.cc365
-rw-r--r--sql/wsrep_thd.h84
-rw-r--r--sql/wsrep_trans_observer.h11
-rw-r--r--sql/wsrep_utils.cc4
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;