From 04ce29354b6053f0334ad1b8d5acaa0974b10fd8 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 24 Aug 2020 09:17:47 +0400 Subject: [PATCH 01/15] MDEV-23551 Performance degratation in temporal literals in 10.4 Problem: Queries like this showed performance degratation in 10.4 over 10.3: SELECT temporal_literal FROM t1; SELECT temporal_literal + 1 FROM t1; SELECT COUNT(*) FROM t1 WHERE temporal_column = temporal_literal; SELECT COUNT(*) FROM t1 WHERE temporal_column = string_literal; Fix: Replacing the universal member "MYSQL_TIME cached_time" in Item_temporal_literal to data type specific containers: - Date in Item_date_literal - Time in Item_time_literal - Datetime in Item_datetime_literal This restores the performance, and make it even better in some cases. See benchmark results in MDEV. Also, this change makes futher separations of Date, Time, Datetime from each other, which will make it possible not to derive them from a too heavy (40 bytes) MYSQL_TIME, and replace them to smaller data type specific containers. --- sql/field.cc | 21 +++---- sql/item.cc | 27 ++++----- sql/item.h | 147 +++++++++++++++++++++++++++++++++------------- sql/sql_select.cc | 9 ++- sql/sql_show.cc | 8 +-- sql/sql_type.cc | 14 +++-- sql/sql_type.h | 60 +++++++++++++++++++ sql/table.cc | 3 +- 8 files changed, 207 insertions(+), 82 deletions(-) diff --git a/sql/field.cc b/sql/field.cc index bac5dd95b5a..4a82eae6a0e 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -5818,9 +5818,7 @@ Item *Field_temporal::get_equal_const_item_datetime(THD *thd, See comments about truncation in the same place in Field_time::get_equal_const_item(). */ - return new (thd->mem_root) Item_datetime_literal(thd, - dt.get_mysql_time(), - decimals()); + return new (thd->mem_root) Item_datetime_literal(thd, &dt, decimals()); } break; case ANY_SUBST: @@ -5832,7 +5830,7 @@ Item *Field_temporal::get_equal_const_item_datetime(THD *thd, if (!dt.is_valid_datetime()) return NULL; return new (thd->mem_root) - Item_datetime_literal_for_invalid_dates(thd, dt.get_mysql_time(), + Item_datetime_literal_for_invalid_dates(thd, &dt, dt.get_mysql_time()-> second_part ? TIME_SECOND_PART_DIGITS : 0); @@ -6183,7 +6181,7 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx, (assuming CURRENT_DATE is '2015-08-30' */ - return new (thd->mem_root) Item_time_literal(thd, tm.get_mysql_time(), + return new (thd->mem_root) Item_time_literal(thd, &tm, tm.get_mysql_time()-> second_part ? TIME_SECOND_PART_DIGITS : @@ -6212,8 +6210,7 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx, decimals()); if (!tm.is_valid_time()) return NULL; - return new (thd->mem_root) Item_time_literal(thd, tm.get_mysql_time(), - decimals()); + return new (thd->mem_root) Item_time_literal(thd, &tm, decimals()); } break; } @@ -6772,12 +6769,12 @@ Item *Field_newdate::get_equal_const_item(THD *thd, const Context &ctx, */ if (!dt.hhmmssff_is_zero()) return new (thd->mem_root) - Item_datetime_literal_for_invalid_dates(thd, dt.get_mysql_time(), + Item_datetime_literal_for_invalid_dates(thd, &dt, dt.get_mysql_time()-> second_part ? TIME_SECOND_PART_DIGITS : 0); - return new (thd->mem_root) - Item_date_literal_for_invalid_dates(thd, Date(&dt).get_mysql_time()); + Date d(&dt); + return new (thd->mem_root) Item_date_literal_for_invalid_dates(thd, &d); } break; case IDENTITY_SUBST: @@ -6792,8 +6789,8 @@ Item *Field_newdate::get_equal_const_item(THD *thd, const Context &ctx, Datetime dt(thd, const_item, Datetime::Options(TIME_CONV_NONE, thd)); if (!dt.is_valid_datetime()) return NULL; - return new (thd->mem_root) - Item_date_literal(thd, Date(&dt).get_mysql_time()); + Date d(&dt); + return new (thd->mem_root) Item_date_literal(thd, &d); } break; } diff --git a/sql/item.cc b/sql/item.cc index 22a8cb169b3..d8074dbb013 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -6998,7 +6998,7 @@ void Item_date_literal::print(String *str, enum_query_type query_type) { str->append("DATE'"); char buf[MAX_DATE_STRING_REP_LENGTH]; - my_date_to_str(&cached_time, buf); + my_date_to_str(cached_time.get_mysql_time(), buf); str->append(buf); str->append('\''); } @@ -7013,7 +7013,7 @@ Item *Item_date_literal::clone_item(THD *thd) bool Item_date_literal::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { fuzzydate |= sql_mode_for_dates(thd); - *ltime= cached_time; + cached_time.copy_to_mysql_time(ltime); return (null_value= check_date_with_warn(thd, ltime, fuzzydate, MYSQL_TIMESTAMP_ERROR)); } @@ -7023,7 +7023,7 @@ void Item_datetime_literal::print(String *str, enum_query_type query_type) { str->append("TIMESTAMP'"); char buf[MAX_DATE_STRING_REP_LENGTH]; - my_datetime_to_str(&cached_time, buf, decimals); + my_datetime_to_str(cached_time.get_mysql_time(), buf, decimals); str->append(buf); str->append('\''); } @@ -7038,7 +7038,7 @@ Item *Item_datetime_literal::clone_item(THD *thd) bool Item_datetime_literal::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { fuzzydate |= sql_mode_for_dates(thd); - *ltime= cached_time; + cached_time.copy_to_mysql_time(ltime); return (null_value= check_date_with_warn(thd, ltime, fuzzydate, MYSQL_TIMESTAMP_ERROR)); } @@ -7048,7 +7048,7 @@ void Item_time_literal::print(String *str, enum_query_type query_type) { str->append("TIME'"); char buf[MAX_DATE_STRING_REP_LENGTH]; - my_time_to_str(&cached_time, buf, decimals); + my_time_to_str(cached_time.get_mysql_time(), buf, decimals); str->append(buf); str->append('\''); } @@ -7062,7 +7062,7 @@ Item *Item_time_literal::clone_item(THD *thd) bool Item_time_literal::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - *ltime= cached_time; + cached_time.copy_to_mysql_time(ltime); if (fuzzydate & TIME_TIME_ONLY) return (null_value= false); return (null_value= check_date_with_warn(thd, ltime, fuzzydate, @@ -9922,23 +9922,20 @@ Item *Item_cache_temporal::convert_to_basic_const_item(THD *thd) Item *Item_cache_datetime::make_literal(THD *thd) { - MYSQL_TIME ltime; - unpack_time(val_datetime_packed(thd), <ime, MYSQL_TIMESTAMP_DATETIME); - return new (thd->mem_root) Item_datetime_literal(thd, <ime, decimals); + Datetime dt(thd, this, TIME_CONV_NONE | TIME_FRAC_NONE); + return new (thd->mem_root) Item_datetime_literal(thd, &dt, decimals); } Item *Item_cache_date::make_literal(THD *thd) { - MYSQL_TIME ltime; - unpack_time(val_datetime_packed(thd), <ime, MYSQL_TIMESTAMP_DATE); - return new (thd->mem_root) Item_date_literal(thd, <ime); + Date d(thd, this, TIME_CONV_NONE | TIME_FRAC_NONE); + return new (thd->mem_root) Item_date_literal(thd, &d); } Item *Item_cache_time::make_literal(THD *thd) { - MYSQL_TIME ltime; - unpack_time(val_time_packed(thd), <ime, MYSQL_TIMESTAMP_TIME); - return new (thd->mem_root) Item_time_literal(thd, <ime, decimals); + Time t(thd, this); + return new (thd->mem_root) Item_time_literal(thd, &t, decimals); } diff --git a/sql/item.h b/sql/item.h index 95ca06ac211..bff60d60506 100644 --- a/sql/item.h +++ b/sql/item.h @@ -4820,29 +4820,20 @@ public: class Item_temporal_literal :public Item_literal { -protected: - MYSQL_TIME cached_time; public: - /** - Constructor for Item_date_literal. - @param ltime DATE value. - */ - Item_temporal_literal(THD *thd, const MYSQL_TIME *ltime) + Item_temporal_literal(THD *thd) :Item_literal(thd) { collation.set(&my_charset_numeric, DERIVATION_NUMERIC, MY_REPERTOIRE_ASCII); decimals= 0; - cached_time= *ltime; } - Item_temporal_literal(THD *thd, const MYSQL_TIME *ltime, uint dec_arg): + Item_temporal_literal(THD *thd, uint dec_arg): Item_literal(thd) { collation.set(&my_charset_numeric, DERIVATION_NUMERIC, MY_REPERTOIRE_ASCII); decimals= dec_arg; - cached_time= *ltime; } - const MYSQL_TIME *const_ptr_mysql_time() const { return &cached_time; } int save_in_field(Field *field, bool no_conversions) { return save_date_in_field(field, no_conversions); } }; @@ -4853,27 +4844,62 @@ public: */ class Item_date_literal: public Item_temporal_literal { -public: - Item_date_literal(THD *thd, const MYSQL_TIME *ltime) - :Item_temporal_literal(thd, ltime) +protected: + Date cached_time; + bool update_null() { + return maybe_null && + (null_value= cached_time.check_date_with_warn(current_thd)); + } +public: + Item_date_literal(THD *thd, const Date *ltime) + :Item_temporal_literal(thd), + cached_time(*ltime) + { + DBUG_ASSERT(cached_time.is_valid_date()); max_length= MAX_DATE_WIDTH; /* If date has zero month or day, it can return NULL in case of NO_ZERO_DATE or NO_ZERO_IN_DATE. - We can't just check the current sql_mode here in constructor, + If date is `February 30`, it can return NULL in case if + no ALLOW_INVALID_DATES is set. + We can't set null_value using the current sql_mode here in constructor, because sql_mode can change in case of prepared statements between PREPARE and EXECUTE. + Here we only set maybe_null to true if the value has such anomalies. + Later (during execution time), if maybe_null is true, then the value + will be checked per row, according to the execution time sql_mode. + The check_date() below call should cover all cases mentioned. */ - maybe_null= !ltime->month || !ltime->day; + maybe_null= cached_time.check_date(TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE); } const Type_handler *type_handler() const { return &type_handler_newdate; } void print(String *str, enum_query_type query_type); + const MYSQL_TIME *const_ptr_mysql_time() const + { + return cached_time.get_mysql_time(); + } Item *clone_item(THD *thd); - longlong val_int() { return Date(this).to_longlong(); } - double val_real() { return Date(this).to_double(); } - String *val_str(String *to) { return Date(this).to_string(to); } - my_decimal *val_decimal(my_decimal *to) { return Date(this).to_decimal(to); } + longlong val_int() + { + return update_null() ? 0 : cached_time.to_longlong(); + } + double val_real() + { + return update_null() ? 0 : cached_time.to_double(); + } + String *val_str(String *to) + { + return update_null() ? 0 : cached_time.to_string(to); + } + my_decimal *val_decimal(my_decimal *to) + { + return update_null() ? 0 : cached_time.to_decimal(to); + } + longlong val_datetime_packed(THD *thd) + { + return update_null() ? 0 : cached_time.valid_date_to_packed(); + } bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); Item *get_copy(THD *thd) { return get_item_copy(thd, this); } @@ -4885,19 +4911,31 @@ public: */ class Item_time_literal: public Item_temporal_literal { +protected: + Time cached_time; public: - Item_time_literal(THD *thd, const MYSQL_TIME *ltime, uint dec_arg): - Item_temporal_literal(thd, ltime, dec_arg) + Item_time_literal(THD *thd, const Time *ltime, uint dec_arg): + Item_temporal_literal(thd, dec_arg), + cached_time(*ltime) { + DBUG_ASSERT(cached_time.is_valid_time()); max_length= MIN_TIME_WIDTH + (decimals ? decimals + 1 : 0); } const Type_handler *type_handler() const { return &type_handler_time2; } void print(String *str, enum_query_type query_type); + const MYSQL_TIME *const_ptr_mysql_time() const + { + return cached_time.get_mysql_time(); + } Item *clone_item(THD *thd); - longlong val_int() { return Time(this).to_longlong(); } - double val_real() { return Time(this).to_double(); } - String *val_str(String *to) { return Time(this).to_string(to, decimals); } - my_decimal *val_decimal(my_decimal *to) { return Time(this).to_decimal(to); } + longlong val_int() { return cached_time.to_longlong(); } + double val_real() { return cached_time.to_double(); } + String *val_str(String *to) { return cached_time.to_string(to, decimals); } + my_decimal *val_decimal(my_decimal *to) { return cached_time.to_decimal(to); } + longlong val_time_packed(THD *thd) + { + return cached_time.valid_time_to_packed(); + } bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); bool val_native(THD *thd, Native *to) { @@ -4913,26 +4951,49 @@ public: */ class Item_datetime_literal: public Item_temporal_literal { -public: - Item_datetime_literal(THD *thd, const MYSQL_TIME *ltime, uint dec_arg): - Item_temporal_literal(thd, ltime, dec_arg) +protected: + Datetime cached_time; + bool update_null() { + return maybe_null && + (null_value= cached_time.check_date_with_warn(current_thd)); + } +public: + Item_datetime_literal(THD *thd, const Datetime *ltime, uint dec_arg): + Item_temporal_literal(thd, dec_arg), + cached_time(*ltime) + { + DBUG_ASSERT(cached_time.is_valid_datetime()); max_length= MAX_DATETIME_WIDTH + (decimals ? decimals + 1 : 0); // See the comment on maybe_null in Item_date_literal - maybe_null= !ltime->month || !ltime->day; + maybe_null= cached_time.check_date(TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE); } const Type_handler *type_handler() const { return &type_handler_datetime2; } void print(String *str, enum_query_type query_type); + const MYSQL_TIME *const_ptr_mysql_time() const + { + return cached_time.get_mysql_time(); + } Item *clone_item(THD *thd); - longlong val_int() { return Datetime(this).to_longlong(); } - double val_real() { return Datetime(this).to_double(); } + longlong val_int() + { + return update_null() ? 0 : cached_time.to_longlong(); + } + double val_real() + { + return update_null() ? 0 : cached_time.to_double(); + } String *val_str(String *to) { - return Datetime(this).to_string(to, decimals); + return update_null() ? NULL : cached_time.to_string(to, decimals); } my_decimal *val_decimal(my_decimal *to) { - return Datetime(this).to_decimal(to); + return update_null() ? NULL : cached_time.to_decimal(to); + } + longlong val_datetime_packed(THD *thd) + { + return update_null() ? 0 : cached_time.valid_datetime_to_packed(); } bool get_date(THD *thd, MYSQL_TIME *res, date_mode_t fuzzydate); Item *get_copy(THD *thd) @@ -4969,11 +5030,14 @@ class Item_date_literal_for_invalid_dates: public Item_date_literal in sql_mode=TRADITIONAL. */ public: - Item_date_literal_for_invalid_dates(THD *thd, const MYSQL_TIME *ltime) - :Item_date_literal(thd, ltime) { } + Item_date_literal_for_invalid_dates(THD *thd, const Date *ltime) + :Item_date_literal(thd, ltime) + { + maybe_null= false; + } bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - *ltime= cached_time; + cached_time.copy_to_mysql_time(ltime); return (null_value= false); } }; @@ -4987,11 +5051,14 @@ class Item_datetime_literal_for_invalid_dates: public Item_datetime_literal { public: Item_datetime_literal_for_invalid_dates(THD *thd, - const MYSQL_TIME *ltime, uint dec_arg) - :Item_datetime_literal(thd, ltime, dec_arg) { } + const Datetime *ltime, uint dec_arg) + :Item_datetime_literal(thd, ltime, dec_arg) + { + maybe_null= false; + } bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - *ltime= cached_time; + cached_time.copy_to_mysql_time(ltime); return (null_value= false); } }; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 19e36632f0c..7091ffc2c58 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -722,8 +722,9 @@ bool vers_select_conds_t::init_from_sysvar(THD *thd) if (type != SYSTEM_TIME_UNSPECIFIED && type != SYSTEM_TIME_ALL) { DBUG_ASSERT(type == SYSTEM_TIME_AS_OF); + Datetime dt(&in.ltime); start.item= new (thd->mem_root) - Item_datetime_literal(thd, &in.ltime, TIME_SECOND_PART_DIGITS); + Item_datetime_literal(thd, &dt, TIME_SECOND_PART_DIGITS); if (!start.item) return true; } @@ -787,15 +788,17 @@ Item* period_get_condition(THD *thd, TABLE_LIST *table, SELECT_LEX *select, { case SYSTEM_TIME_UNSPECIFIED: case SYSTEM_TIME_HISTORY: + { thd->variables.time_zone->gmt_sec_to_TIME(&max_time, TIMESTAMP_MAX_VALUE); max_time.second_part= TIME_MAX_SECOND_PART; - curr= newx Item_datetime_literal(thd, &max_time, TIME_SECOND_PART_DIGITS); + Datetime dt(&max_time); + curr= newx Item_datetime_literal(thd, &dt, TIME_SECOND_PART_DIGITS); if (conds->type == SYSTEM_TIME_UNSPECIFIED) cond1= newx Item_func_eq(thd, conds->field_end, curr); else cond1= newx Item_func_lt(thd, conds->field_end, curr); break; - break; + } case SYSTEM_TIME_AS_OF: cond1= newx Item_func_le(thd, conds->field_start, conds->start.item); cond2= newx Item_func_gt(thd, conds->field_end, conds->start.item); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 2f8de331d30..3ffb5338053 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -10082,11 +10082,6 @@ int finalize_schema_table(st_plugin_int *plugin) DBUG_RETURN(0); } -/* - This is used to create a timestamp field -*/ - -MYSQL_TIME zero_time={ 0,0,0,0,0,0,0,0, MYSQL_TIMESTAMP_TIME }; /** Output trigger information (SHOW CREATE TRIGGER) to the client. @@ -10171,8 +10166,9 @@ static bool show_create_trigger_impl(THD *thd, Trigger *trigger) MY_CS_NAME_SIZE), mem_root); + static const Datetime zero_datetime(Datetime::zero()); Item_datetime_literal *tmp= (new (mem_root) - Item_datetime_literal(thd, &zero_time, 2)); + Item_datetime_literal(thd, &zero_datetime, 2)); tmp->set_name(thd, STRING_WITH_LEN("Created"), system_charset_info); fields.push_back(tmp, mem_root); diff --git a/sql/sql_type.cc b/sql/sql_type.cc index 85052a1c1bc..0c26e947789 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -8391,7 +8391,10 @@ Type_handler_date_common::create_literal_item(THD *thd, if (tmp.is_valid_temporal() && tmp.get_mysql_time()->time_type == MYSQL_TIMESTAMP_DATE && !have_important_literal_warnings(&st)) - item= new (thd->mem_root) Item_date_literal(thd, tmp.get_mysql_time()); + { + Date d(&tmp); + item= new (thd->mem_root) Item_date_literal(thd, &d); + } literal_warn(thd, item, str, length, cs, &st, "DATE", send_error); return item; } @@ -8410,8 +8413,10 @@ Type_handler_temporal_with_date::create_literal_item(THD *thd, if (tmp.is_valid_temporal() && tmp.get_mysql_time()->time_type == MYSQL_TIMESTAMP_DATETIME && !have_important_literal_warnings(&st)) - item= new (thd->mem_root) Item_datetime_literal(thd, tmp.get_mysql_time(), - st.precision); + { + Datetime dt(&tmp); + item= new (thd->mem_root) Item_datetime_literal(thd, &dt, st.precision); + } literal_warn(thd, item, str, length, cs, &st, "DATETIME", send_error); return item; } @@ -8430,8 +8435,7 @@ Type_handler_time_common::create_literal_item(THD *thd, Time tmp(thd, &st, str, length, cs, opt); if (tmp.is_valid_time() && !have_important_literal_warnings(&st)) - item= new (thd->mem_root) Item_time_literal(thd, tmp.get_mysql_time(), - st.precision); + item= new (thd->mem_root) Item_time_literal(thd, &tmp, st.precision); literal_warn(thd, item, str, length, cs, &st, "TIME", send_error); return item; } diff --git a/sql/sql_type.h b/sql/sql_type.h index 8726208b788..2064a55dc14 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -1722,6 +1722,11 @@ public: { return is_valid_time() ? Temporal::to_packed() : 0; } + longlong valid_time_to_packed() const + { + DBUG_ASSERT(is_valid_time_slow()); + return Temporal::to_packed(); + } long fraction_remainder(uint dec) const { DBUG_ASSERT(is_valid_time()); @@ -1896,6 +1901,11 @@ public: { return ::check_date_with_warn(thd, this, flags, MYSQL_TIMESTAMP_ERROR); } + bool check_date_with_warn(THD *thd) + { + return ::check_date_with_warn(thd, this, Temporal::sql_mode_for_dates(thd), + MYSQL_TIMESTAMP_ERROR); + } static date_conv_mode_t comparison_flags_for_get_date() { return TIME_INVALID_DATES | TIME_FUZZY_DATES; } }; @@ -1964,11 +1974,37 @@ public: datetime_to_date(this); DBUG_ASSERT(is_valid_date_slow()); } + explicit Date(const Temporal_hybrid *from) + { + *(static_cast(this))= *from; + DBUG_ASSERT(is_valid_date_slow()); + } bool is_valid_date() const { DBUG_ASSERT(is_valid_value_slow()); return time_type == MYSQL_TIMESTAMP_DATE; } + bool check_date(date_conv_mode_t flags, int *warnings) const + { + DBUG_ASSERT(is_valid_date_slow()); + return ::check_date(this, (year || month || day), + ulonglong(flags & TIME_MODE_FOR_XXX_TO_DATE), + warnings); + } + bool check_date(THD *thd, int *warnings) const + { + return check_date(Temporal::sql_mode_for_dates(thd), warnings); + } + bool check_date(date_conv_mode_t flags) const + { + int dummy; /* unused */ + return check_date(flags, &dummy); + } + bool check_date(THD *thd) const + { + int dummy; + return check_date(Temporal::sql_mode_for_dates(thd), &dummy); + } const MYSQL_TIME *get_mysql_time() const { DBUG_ASSERT(is_valid_date_slow()); @@ -2011,6 +2047,11 @@ public: return Temporal_with_date::yearweek(week_behaviour); } + longlong valid_date_to_packed() const + { + DBUG_ASSERT(is_valid_date_slow()); + return Temporal::to_packed(); + } longlong to_longlong() const { return is_valid_date() ? (longlong) TIME_to_ulonglong_date(this) : 0LL; @@ -2197,6 +2238,16 @@ public: { round(thd, dec, time_round_mode_t(fuzzydate), warn); } + explicit Datetime(const Temporal_hybrid *from) + { + *(static_cast(this))= *from; + DBUG_ASSERT(is_valid_datetime_slow()); + } + explicit Datetime(const MYSQL_TIME *from) + { + *(static_cast(this))= *from; + DBUG_ASSERT(is_valid_datetime_slow()); + } bool is_valid_datetime() const { @@ -2219,6 +2270,10 @@ public: int dummy; /* unused */ return check_date(flags, &dummy); } + bool check_date(THD *thd) const + { + return check_date(Temporal::sql_mode_for_dates(thd)); + } bool hhmmssff_is_zero() const { DBUG_ASSERT(is_valid_datetime_slow()); @@ -2327,6 +2382,11 @@ public: { return is_valid_datetime() ? Temporal::to_packed() : 0; } + longlong valid_datetime_to_packed() const + { + DBUG_ASSERT(is_valid_datetime_slow()); + return Temporal::to_packed(); + } long fraction_remainder(uint dec) const { DBUG_ASSERT(is_valid_datetime()); diff --git a/sql/table.cc b/sql/table.cc index ca0af28a79d..6fa2ef51f89 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -9365,7 +9365,8 @@ bool TR_table::query(MYSQL_TIME &commit_time, bool backwards) SELECT_LEX &slex= *(thd->lex->first_select_lex()); Name_resolution_context_backup backup(slex.context, *this); Item *field= newx Item_field(thd, &slex.context, (*this)[FLD_COMMIT_TS]); - Item *value= newx Item_datetime_literal(thd, &commit_time, 6); + Datetime dt(&commit_time); + Item *value= newx Item_datetime_literal(thd, &dt, 6); COND *conds; if (backwards) conds= newx Item_func_ge(thd, field, value); From 056766c042e975a91b016246357e9e6929b60b1a Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 24 Aug 2020 14:27:32 +0400 Subject: [PATCH 02/15] The patch for MDEV-23551 did not compile on some compilers. Fixing. --- sql/sql_type.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/sql/sql_type.h b/sql/sql_type.h index 2064a55dc14..c1b13dfa811 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -1056,6 +1056,13 @@ public: } // End of constuctors + bool copy_valid_value_to_mysql_time(MYSQL_TIME *ltime) const + { + DBUG_ASSERT(is_valid_temporal()); + *ltime= *this; + return false; + } + longlong to_longlong() const { if (!is_valid_temporal()) @@ -1976,7 +1983,7 @@ public: } explicit Date(const Temporal_hybrid *from) { - *(static_cast(this))= *from; + from->copy_valid_value_to_mysql_time(this); DBUG_ASSERT(is_valid_date_slow()); } bool is_valid_date() const @@ -2240,7 +2247,7 @@ public: } explicit Datetime(const Temporal_hybrid *from) { - *(static_cast(this))= *from; + from->copy_valid_value_to_mysql_time(this); DBUG_ASSERT(is_valid_datetime_slow()); } explicit Datetime(const MYSQL_TIME *from) From 329301d2e647db099e1554663578f0352125d1c9 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 24 Aug 2020 22:55:39 +0400 Subject: [PATCH 03/15] MDEV-23562 Assertion `time_type == MYSQL_TIMESTAMP_DATETIME' failed upon SELECT from versioned table The code in vers_select_conds_t::init_from_sysvar() assumed that the value of the system_versioning_asof is DATETIME. But it also could be DATE after a query like this: SET system_versioning_asof = DATE(NOW()); Fixing Sys_var_vers_asof::update() to convert the value of the assignment source to DATETIME if it returned DATE. Now vers_select_conds_t::init_from_sysvar() always gets a DATETIME value. --- mysql-test/suite/versioning/r/sysvars.result | 9 +++++++++ mysql-test/suite/versioning/t/sysvars.test | 10 ++++++++++ sql/sys_vars.ic | 6 +++++- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/mysql-test/suite/versioning/r/sysvars.result b/mysql-test/suite/versioning/r/sysvars.result index ac0a1237902..66513741631 100644 --- a/mysql-test/suite/versioning/r/sysvars.result +++ b/mysql-test/suite/versioning/r/sysvars.result @@ -186,4 +186,13 @@ SELECT @@global.system_versioning_asof; @@global.system_versioning_asof 2002-01-01 00:00:00.000000 SET @@global.system_versioning_asof= DEFAULT; +# +# MDEV-23562 Assertion `time_type == MYSQL_TIMESTAMP_DATETIME' failed upon SELECT from versioned table +# +CREATE TABLE t1 (a INT) WITH SYSTEM VERSIONING; +SET system_versioning_asof= DATE(NOW()); +SELECT * FROM t1; +a +DROP TABLE t1; +SET system_versioning_asof= DEFAULT; # End of 10.4 tests diff --git a/mysql-test/suite/versioning/t/sysvars.test b/mysql-test/suite/versioning/t/sysvars.test index 7c5e818ec81..a1026418e98 100644 --- a/mysql-test/suite/versioning/t/sysvars.test +++ b/mysql-test/suite/versioning/t/sysvars.test @@ -138,4 +138,14 @@ SET @@global.system_versioning_asof= timestamp'2001-12-31 23:59:59.9999999'; SELECT @@global.system_versioning_asof; SET @@global.system_versioning_asof= DEFAULT; +--echo # +--echo # MDEV-23562 Assertion `time_type == MYSQL_TIMESTAMP_DATETIME' failed upon SELECT from versioned table +--echo # + +CREATE TABLE t1 (a INT) WITH SYSTEM VERSIONING; +SET system_versioning_asof= DATE(NOW()); +SELECT * FROM t1; +DROP TABLE t1; +SET system_versioning_asof= DEFAULT; + --echo # End of 10.4 tests diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic index 8e8ec4f00bc..94bfb3bb1bb 100644 --- a/sql/sys_vars.ic +++ b/sql/sys_vars.ic @@ -2669,7 +2669,11 @@ private: Datetime::Options opt(TIME_CONV_NONE | TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE, thd); - res= var->value->get_date(thd, &out.ltime, opt); + /* + var->value is allowed to return DATETIME and DATE + Make sure to convert DATE to DATETIME. + */ + res= Datetime(thd, var->value, opt).copy_to_mysql_time(&out.ltime); } else // set DEFAULT from global var { From 2000d05c2ed85563e35c4548d9518249ddf403df Mon Sep 17 00:00:00 2001 From: Kentoku SHIBA Date: Sun, 23 Aug 2020 12:30:14 +0900 Subject: [PATCH 04/15] MDEV-22246 Result rows duplicated by spider engine fix the following type mrr scan (select 0,`id`,`node` from `auto_test_remote`.`tbl_a` where (`id` <> 0) order by `id`)union all(select 1,`id`,`node` from `auto_test_remote`.`tbl_a` where (`id` <> 0) order by `id`) order by `id` --- storage/spider/ha_spider.cc | 89 ++++++++++++++++++ storage/spider/ha_spider.h | 2 + .../bugfix/include/mdev_22246_deinit.inc | 14 +++ .../spider/bugfix/include/mdev_22246_init.inc | 48 ++++++++++ .../spider/bugfix/r/mdev_22246.result | 79 ++++++++++++++++ .../mysql-test/spider/bugfix/t/mdev_22246.cnf | 4 + .../spider/bugfix/t/mdev_22246.test | 92 +++++++++++++++++++ storage/spider/spd_db_conn.cc | 6 ++ storage/spider/spd_db_include.h | 1 + 9 files changed, 335 insertions(+) create mode 100644 storage/spider/mysql-test/spider/bugfix/include/mdev_22246_deinit.inc create mode 100644 storage/spider/mysql-test/spider/bugfix/include/mdev_22246_init.inc create mode 100644 storage/spider/mysql-test/spider/bugfix/r/mdev_22246.result create mode 100644 storage/spider/mysql-test/spider/bugfix/t/mdev_22246.cnf create mode 100644 storage/spider/mysql-test/spider/bugfix/t/mdev_22246.test diff --git a/storage/spider/ha_spider.cc b/storage/spider/ha_spider.cc index d2d0ddf376f..1f4439161b2 100644 --- a/storage/spider/ha_spider.cc +++ b/storage/spider/ha_spider.cc @@ -1832,6 +1832,7 @@ int ha_spider::reset() multi_range_keys = NULL; } #endif + multi_range_num = 0; ft_handler = NULL; ft_current = NULL; ft_count = 0; @@ -4354,6 +4355,64 @@ int ha_spider::read_range_next() DBUG_RETURN(check_ha_range_eof()); } +void ha_spider::reset_no_where_cond() +{ + uint roop_count; + DBUG_ENTER("ha_spider::reset_no_where_cond"); +#if defined(HS_HAS_SQLCOM) && defined(HAVE_HANDLERSOCKET) + if (sql_kinds & (SPIDER_SQL_KIND_SQL | SPIDER_SQL_KIND_HANDLER)) + { +#endif + for (roop_count = 0; roop_count < share->use_sql_dbton_count; roop_count++) + { + dbton_handler[share->use_sql_dbton_ids[roop_count]]->no_where_cond = + FALSE; + } +#if defined(HS_HAS_SQLCOM) && defined(HAVE_HANDLERSOCKET) + } + if (sql_kinds & SPIDER_SQL_KIND_HS) + { + for (roop_count = 0; roop_count < share->use_hs_dbton_count; roop_count++) + { + dbton_handler[share->use_hs_dbton_ids[roop_count]]->no_where_cond = + FALSE; + } + } +#endif + DBUG_VOID_RETURN; +} + +bool ha_spider::check_no_where_cond() +{ + uint roop_count; + DBUG_ENTER("ha_spider::check_no_where_cond"); +#if defined(HS_HAS_SQLCOM) && defined(HAVE_HANDLERSOCKET) + if (sql_kinds & (SPIDER_SQL_KIND_SQL | SPIDER_SQL_KIND_HANDLER)) + { +#endif + for (roop_count = 0; roop_count < share->use_sql_dbton_count; roop_count++) + { + if (dbton_handler[share->use_sql_dbton_ids[roop_count]]->no_where_cond) + { + DBUG_RETURN(TRUE); + } + } +#if defined(HS_HAS_SQLCOM) && defined(HAVE_HANDLERSOCKET) + } + if (sql_kinds & SPIDER_SQL_KIND_HS) + { + for (roop_count = 0; roop_count < share->use_hs_dbton_count; roop_count++) + { + if (dbton_handler[share->use_hs_dbton_ids[roop_count]]->no_where_cond) + { + DBUG_RETURN(TRUE); + } + } + } +#endif + DBUG_RETURN(FALSE); +} + #ifdef HA_MRR_USE_DEFAULT_IMPL #if defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 100000 ha_rows ha_spider::multi_range_read_info_const( @@ -4497,6 +4556,7 @@ int ha_spider::multi_range_read_init( DBUG_PRINT("info",("spider n_ranges=%u", n_ranges)); multi_range_num = n_ranges; mrr_have_range = FALSE; + reset_no_where_cond(); DBUG_RETURN( handler::multi_range_read_init( seq, @@ -4969,6 +5029,10 @@ int ha_spider::read_multi_range_first_internal( result_list.current = result_list.current->prev; } } + if (check_no_where_cond()) + { + DBUG_RETURN(check_error_mode_eof(0)); + } set_where_to_pos_sql(SPIDER_SQL_TYPE_SELECT_SQL); set_where_to_pos_sql(SPIDER_SQL_TYPE_HANDLER); } @@ -5444,6 +5508,15 @@ int ha_spider::read_multi_range_first_internal( DBUG_PRINT("info",("spider range_res8=%d", range_res)); } #endif + if (check_no_where_cond()) + { +#ifdef HA_MRR_USE_DEFAULT_IMPL + range_res = 1; +#else + multi_range_curr = multi_range_end; +#endif + break; + } } #ifdef HA_MRR_USE_DEFAULT_IMPL while (!range_res); @@ -5856,6 +5929,10 @@ int ha_spider::read_multi_range_first_internal( } else DBUG_RETURN(error_num); } + if (check_no_where_cond()) + { + DBUG_RETURN(check_error_mode_eof(0)); + } multi_range_cnt = 0; if ((error_num = reset_sql_sql( SPIDER_SQL_TYPE_SELECT_SQL | SPIDER_SQL_TYPE_HANDLER))) @@ -6077,6 +6154,10 @@ int ha_spider::read_multi_range_next( #ifdef HA_MRR_USE_DEFAULT_IMPL DBUG_PRINT("info",("spider range_res2=%d", range_res)); #endif + if (check_no_where_cond()) + { + DBUG_RETURN(check_error_mode_eof(0)); + } set_where_to_pos_sql(SPIDER_SQL_TYPE_SELECT_SQL); set_where_to_pos_sql(SPIDER_SQL_TYPE_HANDLER); result_list.limit_num = @@ -6497,6 +6578,10 @@ int ha_spider::read_multi_range_next( #endif ) DBUG_RETURN(error_num); + if (check_no_where_cond()) + { + DBUG_RETURN(check_error_mode_eof(0)); + } spider_db_free_one_result_for_start_next(this); spider_first_split_read_param(this); #ifndef WITHOUT_SPIDER_BG_SEARCH @@ -7303,6 +7388,10 @@ int ha_spider::read_multi_range_next( } else DBUG_RETURN(error_num); } + if (check_no_where_cond()) + { + DBUG_RETURN(check_error_mode_eof(0)); + } multi_range_cnt = 0; if ((error_num = reset_sql_sql( SPIDER_SQL_TYPE_SELECT_SQL | SPIDER_SQL_TYPE_HANDLER))) diff --git a/storage/spider/ha_spider.h b/storage/spider/ha_spider.h index edd7b8c881f..cb0a2abcc06 100644 --- a/storage/spider/ha_spider.h +++ b/storage/spider/ha_spider.h @@ -355,6 +355,8 @@ public: bool sorted ); int read_range_next(); + void reset_no_where_cond(); + bool check_no_where_cond(); #ifdef HA_MRR_USE_DEFAULT_IMPL #if defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 100000 ha_rows multi_range_read_info_const( diff --git a/storage/spider/mysql-test/spider/bugfix/include/mdev_22246_deinit.inc b/storage/spider/mysql-test/spider/bugfix/include/mdev_22246_deinit.inc new file mode 100644 index 00000000000..9d255152dd8 --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/include/mdev_22246_deinit.inc @@ -0,0 +1,14 @@ +--let $MASTER_1_COMMENT_2_1= $MASTER_1_COMMENT_2_1_BACKUP +--let $CHILD2_1_DROP_TABLES= $CHILD2_1_DROP_TABLES_BACKUP +--let $CHILD2_1_CREATE_TABLES= $CHILD2_1_CREATE_TABLES_BACKUP +--let $CHILD2_1_SELECT_TABLES= $CHILD2_1_SELECT_TABLES_BACKUP +--let $CHILD2_2_DROP_TABLES= $CHILD2_2_DROP_TABLES_BACKUP +--let $CHILD2_2_CREATE_TABLES= $CHILD2_2_CREATE_TABLES_BACKUP +--let $CHILD2_2_SELECT_TABLES= $CHILD2_2_SELECT_TABLES_BACKUP +--disable_warnings +--disable_query_log +--disable_result_log +--source ../t/test_deinit.inc +--enable_result_log +--enable_query_log +--enable_warnings diff --git a/storage/spider/mysql-test/spider/bugfix/include/mdev_22246_init.inc b/storage/spider/mysql-test/spider/bugfix/include/mdev_22246_init.inc new file mode 100644 index 00000000000..48226ba2811 --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/include/mdev_22246_init.inc @@ -0,0 +1,48 @@ +--disable_warnings +--disable_query_log +--disable_result_log +--source ../t/test_init.inc +if (!$HAVE_PARTITION) +{ + --source group_by_order_by_limit_deinit.inc + --enable_result_log + --enable_query_log + --enable_warnings + skip Test requires partitioning; +} +--enable_result_log +--enable_query_log +--enable_warnings +--let $MASTER_1_COMMENT_2_1_BACKUP= $MASTER_1_COMMENT_2_1 +let $MASTER_1_COMMENT_2_1= + COMMENT='table "tbl_a"' + PARTITION BY HASH(id) ( + PARTITION pt1 COMMENT='srv "s_2_1"', + PARTITION pt2 COMMENT='srv "s_2_2"' + ); +--let $CHILD2_1_DROP_TABLES_BACKUP= $CHILD2_1_DROP_TABLES +let $CHILD2_1_DROP_TABLES= + DROP TABLE IF EXISTS tbl_a; +--let $CHILD2_1_CREATE_TABLES_BACKUP= $CHILD2_1_CREATE_TABLES +let $CHILD2_1_CREATE_TABLES= + CREATE TABLE tbl_a ( + id bigint NOT NULL, + node text, + PRIMARY KEY (id) + ) $CHILD2_1_ENGINE $CHILD2_1_CHARSET; +--let $CHILD2_1_SELECT_TABLES_BACKUP= $CHILD2_1_SELECT_TABLES +let $CHILD2_1_SELECT_TABLES= + SELECT * FROM tbl_a ORDER BY id; +--let $CHILD2_2_DROP_TABLES_BACKUP= $CHILD2_2_DROP_TABLES +let $CHILD2_2_DROP_TABLES= + DROP TABLE IF EXISTS tbl_a; +--let $CHILD2_2_CREATE_TABLES_BACKUP= $CHILD2_2_CREATE_TABLES +let $CHILD2_2_CREATE_TABLES= + CREATE TABLE tbl_a ( + id bigint NOT NULL, + node text, + PRIMARY KEY (id) + ) $CHILD2_2_ENGINE $CHILD2_2_CHARSET; +--let $CHILD2_2_SELECT_TABLES_BACKUP= $CHILD2_2_SELECT_TABLES +let $CHILD2_2_SELECT_TABLES= + SELECT * FROM tbl_a ORDER BY id; diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_22246.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_22246.result new file mode 100644 index 00000000000..749c750e018 --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_22246.result @@ -0,0 +1,79 @@ +for master_1 +for child2 +child2_1 +child2_2 +child2_3 +for child3 + +this test is for MDEV-22246 + +drop and create databases +connection master_1; +CREATE DATABASE auto_test_local; +USE auto_test_local; +connection child2_1; +SET @old_log_output = @@global.log_output; +SET GLOBAL log_output = 'TABLE,FILE'; +CREATE DATABASE auto_test_remote; +USE auto_test_remote; +connection child2_2; +SET @old_log_output = @@global.log_output; +SET GLOBAL log_output = 'TABLE,FILE'; +CREATE DATABASE auto_test_remote2; +USE auto_test_remote2; + +create table and insert +connection child2_1; +CHILD2_1_CREATE_TABLES +TRUNCATE TABLE mysql.general_log; +connection child2_2; +CHILD2_2_CREATE_TABLES +TRUNCATE TABLE mysql.general_log; +connection master_1; +CREATE TABLE tbl_a ( +id bigint NOT NULL, +node text, +PRIMARY KEY (id) +) MASTER_1_ENGINE MASTER_1_CHARSET MASTER_1_COMMENT_2_1 +INSERT INTO tbl_a (id,node) VALUES (1,'DB-G0'),(2,'DB-G1'); + +select test 1 +connection child2_1; +TRUNCATE TABLE mysql.general_log; +connection child2_2; +TRUNCATE TABLE mysql.general_log; +connection master_1; +SELECT * FROM tbl_a; +id node +2 DB-G1 +1 DB-G0 +SELECT * FROM tbl_a WHERE id != 0; +id node +2 DB-G1 +1 DB-G0 +connection child2_1; +SELECT * FROM tbl_a ORDER BY id; +id node +2 DB-G1 +connection child2_2; +SELECT * FROM tbl_a ORDER BY id; +id node +1 DB-G0 + +deinit +connection master_1; +DROP DATABASE IF EXISTS auto_test_local; +connection child2_1; +DROP DATABASE IF EXISTS auto_test_remote; +SET GLOBAL log_output = @old_log_output; +connection child2_2; +DROP DATABASE IF EXISTS auto_test_remote2; +SET GLOBAL log_output = @old_log_output; +for master_1 +for child2 +child2_1 +child2_2 +child2_3 +for child3 + +end of test diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_22246.cnf b/storage/spider/mysql-test/spider/bugfix/t/mdev_22246.cnf new file mode 100644 index 00000000000..e0ffb99c38e --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_22246.cnf @@ -0,0 +1,4 @@ +!include include/default_mysqld.cnf +!include ../my_1_1.cnf +!include ../my_2_1.cnf +!include ../my_2_2.cnf diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_22246.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_22246.test new file mode 100644 index 00000000000..63b04c14e11 --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_22246.test @@ -0,0 +1,92 @@ +--source ../include/mdev_22246_init.inc +--echo +--echo this test is for MDEV-22246 +--echo +--echo drop and create databases +--connection master_1 +--disable_warnings +CREATE DATABASE auto_test_local; +USE auto_test_local; + +--connection child2_1 +SET @old_log_output = @@global.log_output; +SET GLOBAL log_output = 'TABLE,FILE'; +CREATE DATABASE auto_test_remote; +USE auto_test_remote; + +--connection child2_2 +SET @old_log_output = @@global.log_output; +SET GLOBAL log_output = 'TABLE,FILE'; +CREATE DATABASE auto_test_remote2; +USE auto_test_remote2; +--enable_warnings + +--echo +--echo create table and insert + +--connection child2_1 +--disable_query_log +echo CHILD2_1_CREATE_TABLES; +eval $CHILD2_1_CREATE_TABLES; +--enable_query_log +TRUNCATE TABLE mysql.general_log; + +--connection child2_2 +--disable_query_log +echo CHILD2_2_CREATE_TABLES; +eval $CHILD2_2_CREATE_TABLES; +--enable_query_log +TRUNCATE TABLE mysql.general_log; + +--connection master_1 +--disable_query_log +echo CREATE TABLE tbl_a ( + id bigint NOT NULL, + node text, + PRIMARY KEY (id) +) MASTER_1_ENGINE MASTER_1_CHARSET MASTER_1_COMMENT_2_1; +eval CREATE TABLE tbl_a ( + id bigint NOT NULL, + node text, + PRIMARY KEY (id) +) $MASTER_1_ENGINE $MASTER_1_CHARSET $MASTER_1_COMMENT_2_1; +--enable_query_log +INSERT INTO tbl_a (id,node) VALUES (1,'DB-G0'),(2,'DB-G1'); + +--echo +--echo select test 1 + +--connection child2_1 +TRUNCATE TABLE mysql.general_log; + +--connection child2_2 +TRUNCATE TABLE mysql.general_log; + +--connection master_1 +SELECT * FROM tbl_a; +SELECT * FROM tbl_a WHERE id != 0; + +--connection child2_1 +eval $CHILD2_1_SELECT_TABLES; + +--connection child2_2 +eval $CHILD2_2_SELECT_TABLES; + +--echo +--echo deinit +--disable_warnings +--connection master_1 +DROP DATABASE IF EXISTS auto_test_local; + +--connection child2_1 +DROP DATABASE IF EXISTS auto_test_remote; +SET GLOBAL log_output = @old_log_output; + +--connection child2_2 +DROP DATABASE IF EXISTS auto_test_remote2; +SET GLOBAL log_output = @old_log_output; + +--enable_warnings +--source ../include/mdev_22246_deinit.inc +--echo +--echo end of test diff --git a/storage/spider/spd_db_conn.cc b/storage/spider/spd_db_conn.cc index a54067281b1..08d2591a07e 100644 --- a/storage/spider/spd_db_conn.cc +++ b/storage/spider/spd_db_conn.cc @@ -1717,6 +1717,7 @@ int spider_db_append_key_where_internal( int key_count; uint length; uint store_length; + uint current_pos = str->length(); const uchar *ptr, *another_ptr; const key_range *use_key, *another_key; KEY_PART_INFO *key_part; @@ -2690,6 +2691,11 @@ int spider_db_append_key_where_internal( DBUG_RETURN(error_num); end: + if (spider->multi_range_num && current_pos == str->length()) + { + DBUG_PRINT("info", ("spider no key where condition")); + dbton_hdl->no_where_cond = TRUE; + } /* use condition */ if (dbton_hdl->append_condition_part(NULL, 0, sql_type, FALSE)) DBUG_RETURN(HA_ERR_OUT_OF_MEM); diff --git a/storage/spider/spd_db_include.h b/storage/spider/spd_db_include.h index 046fe66435c..9b005ba7ccd 100644 --- a/storage/spider/spd_db_include.h +++ b/storage/spider/spd_db_include.h @@ -1323,6 +1323,7 @@ public: #ifdef SPIDER_HAS_GROUP_BY_HANDLER SPIDER_LINK_IDX_CHAIN *link_idx_chain; #endif + bool no_where_cond; spider_db_handler(ha_spider *spider, spider_db_share *db_share) : dbton_id(db_share->dbton_id), spider(spider), db_share(db_share), first_link_idx(-1) {} From 4a90bb85c0cb1407b0f112245e47ec995dc767c7 Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Mon, 24 Aug 2020 20:53:51 +0300 Subject: [PATCH 05/15] InnoDB: fix debug assertion --- storage/innobase/row/row0upd.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/innobase/row/row0upd.cc b/storage/innobase/row/row0upd.cc index 63c1ea8d662..800c7a6d1f3 100644 --- a/storage/innobase/row/row0upd.cc +++ b/storage/innobase/row/row0upd.cc @@ -2438,7 +2438,7 @@ row_upd_sec_index_entry( #ifdef UNIV_DEBUG mtr_commit(&mtr); mtr_start(&mtr); - ut_ad(btr_validate_index(index, 0, false)); + ut_ad(btr_validate_index(index, 0, false) == DB_SUCCESS); ut_ad(0); #endif /* UNIV_DEBUG */ break; From a16f4927dbde19c546b1ba3726529914f8c91730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Mon, 24 Aug 2020 14:09:40 +0300 Subject: [PATCH 06/15] MDEV-22055: Assertion `active() == false' failed in wsrep::transaction::start_transaction upon ROLLBACK AND CHAIN The optional AND CHAIN clause is a convenience for initiating a new transaction as soon as the old transaction terminates. Therefore, do not start new transaction if it is already started at wsrep_start_transaction. --- mysql-test/suite/galera/r/MDEV-22055.result | 18 ++++++++++++++++++ mysql-test/suite/galera/t/MDEV-22055.test | 19 +++++++++++++++++++ sql/wsrep_trans_observer.h | 8 +++++--- 3 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 mysql-test/suite/galera/r/MDEV-22055.result create mode 100644 mysql-test/suite/galera/t/MDEV-22055.test diff --git a/mysql-test/suite/galera/r/MDEV-22055.result b/mysql-test/suite/galera/r/MDEV-22055.result new file mode 100644 index 00000000000..651f8501a0a --- /dev/null +++ b/mysql-test/suite/galera/r/MDEV-22055.result @@ -0,0 +1,18 @@ +connection node_2; +connection node_1; +ROLLBACK AND CHAIN; +CREATE TABLE t1(a int not null primary key) engine=innodb; +INSERT INTO t1 values (1); +BEGIN; +INSERT INTO t1 values (2); +ROLLBACK AND CHAIN; +SELECT * FROM t1; +a +1 +connection node_2; +SET SESSION wsrep_sync_wait=15; +SELECT * FROM t1; +a +1 +connection node_1; +DROP TABLE t1; diff --git a/mysql-test/suite/galera/t/MDEV-22055.test b/mysql-test/suite/galera/t/MDEV-22055.test new file mode 100644 index 00000000000..ae29c456bf0 --- /dev/null +++ b/mysql-test/suite/galera/t/MDEV-22055.test @@ -0,0 +1,19 @@ +--source include/galera_cluster.inc + +ROLLBACK AND CHAIN; + +CREATE TABLE t1(a int not null primary key) engine=innodb; +INSERT INTO t1 values (1); + +BEGIN; +INSERT INTO t1 values (2); +ROLLBACK AND CHAIN; + +SELECT * FROM t1; + +--connection node_2 +SET SESSION wsrep_sync_wait=15; +SELECT * FROM t1; + +--connection node_1 +DROP TABLE t1; diff --git a/sql/wsrep_trans_observer.h b/sql/wsrep_trans_observer.h index 1044cab76ad..05970e8b12f 100644 --- a/sql/wsrep_trans_observer.h +++ b/sql/wsrep_trans_observer.h @@ -133,9 +133,11 @@ static inline size_t wsrep_fragments_certified_for_stmt(THD* thd) static inline int wsrep_start_transaction(THD* thd, wsrep_trx_id_t trx_id) { - return (thd->wsrep_cs().state() != wsrep::client_state::s_none ? - thd->wsrep_cs().start_transaction(wsrep::transaction_id(trx_id)) : - 0); + if (thd->wsrep_cs().state() != wsrep::client_state::s_none) { + if (wsrep_is_active(thd) == false) + return thd->wsrep_cs().start_transaction(wsrep::transaction_id(trx_id)); + } + return 0; } /**/ From 88e70f4caea34e0d7677b1fa646151b8b87dd3ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Mon, 24 Aug 2020 16:50:53 +0300 Subject: [PATCH 07/15] MDEV-23558: Galera heap-buffer-overflow at wsrep_schema.cc:1067 Key buffer needs to contain max field widths i.e. add MAX_FIELD_WIDTH. --- sql/wsrep_schema.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sql/wsrep_schema.cc b/sql/wsrep_schema.cc index 619a535f916..df9c7b78c9b 100644 --- a/sql/wsrep_schema.cc +++ b/sql/wsrep_schema.cc @@ -935,7 +935,7 @@ int Wsrep_schema::update_fragment_meta(THD* thd, Wsrep_schema_impl::binlog_off binlog_off(thd); int error; - uchar key[MAX_KEY_LENGTH]; + uchar key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; key_part_map key_map= 0; TABLE* frag_table= 0; @@ -997,7 +997,7 @@ static int remove_fragment(THD* thd, seqno.get()); int ret= 0; int error; - uchar key[MAX_KEY_LENGTH]; + uchar key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; key_part_map key_map= 0; DBUG_ASSERT(server_id.is_undefined() == false); @@ -1120,7 +1120,7 @@ int Wsrep_schema::replay_transaction(THD* orig_thd, int ret= 1; int error; TABLE* frag_table= 0; - uchar key[MAX_KEY_LENGTH]; + uchar key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; key_part_map key_map= 0; for (std::vector::const_iterator i= fragments.begin(); From 6fa40b85be8cd35d337a4b4b7cf910a81518d298 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 25 Aug 2020 10:15:04 +0300 Subject: [PATCH 08/15] MDEV-23554 Wrong default value for foreign_key_checks variable Sys_var_bit::session_save_default() ignored reverse_semantics property. --- mysql-test/suite/innodb/r/foreign_key.result | 1 - mysql-test/suite/innodb/t/foreign_key.test | 1 - .../suite/sys_vars/r/foreign_key_checks_basic.result | 2 +- mysql-test/suite/sys_vars/r/unique_checks_basic.result | 2 +- sql/sys_vars.ic | 7 +++++-- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/mysql-test/suite/innodb/r/foreign_key.result b/mysql-test/suite/innodb/r/foreign_key.result index bab4ea16643..fb4175c0327 100644 --- a/mysql-test/suite/innodb/r/foreign_key.result +++ b/mysql-test/suite/innodb/r/foreign_key.result @@ -167,7 +167,6 @@ PRIMARY KEY (store_id), UNIQUE KEY idx_unique_manager (manager_staff_id), CONSTRAINT fk_store_staff FOREIGN KEY (manager_staff_id) REFERENCES staff (staff_id) ON DELETE RESTRICT ON UPDATE CASCADE ) ENGINE=InnoDB; -SET FOREIGN_KEY_CHECKS=DEFAULT; LOCK TABLE staff WRITE; UNLOCK TABLES; DROP TABLES staff, store; diff --git a/mysql-test/suite/innodb/t/foreign_key.test b/mysql-test/suite/innodb/t/foreign_key.test index 40bc3a32a4f..e0d83338dc2 100644 --- a/mysql-test/suite/innodb/t/foreign_key.test +++ b/mysql-test/suite/innodb/t/foreign_key.test @@ -136,7 +136,6 @@ CREATE TABLE store ( UNIQUE KEY idx_unique_manager (manager_staff_id), CONSTRAINT fk_store_staff FOREIGN KEY (manager_staff_id) REFERENCES staff (staff_id) ON DELETE RESTRICT ON UPDATE CASCADE ) ENGINE=InnoDB; -SET FOREIGN_KEY_CHECKS=DEFAULT; LOCK TABLE staff WRITE; UNLOCK TABLES; diff --git a/mysql-test/suite/sys_vars/r/foreign_key_checks_basic.result b/mysql-test/suite/sys_vars/r/foreign_key_checks_basic.result index 834d693edb8..0bf9d859d40 100644 --- a/mysql-test/suite/sys_vars/r/foreign_key_checks_basic.result +++ b/mysql-test/suite/sys_vars/r/foreign_key_checks_basic.result @@ -7,7 +7,7 @@ SET @@session.foreign_key_checks = 1; SET @@session.foreign_key_checks = DEFAULT; SELECT @@session.foreign_key_checks; @@session.foreign_key_checks -0 +1 '#---------------------FN_DYNVARS_032_02-------------------------#' SET foreign_key_checks = 1; SELECT @@foreign_key_checks; diff --git a/mysql-test/suite/sys_vars/r/unique_checks_basic.result b/mysql-test/suite/sys_vars/r/unique_checks_basic.result index 60cf2795309..83fb9edb4cd 100644 --- a/mysql-test/suite/sys_vars/r/unique_checks_basic.result +++ b/mysql-test/suite/sys_vars/r/unique_checks_basic.result @@ -7,7 +7,7 @@ SET @@session.unique_checks= 1; SET @@session.unique_checks= DEFAULT; SELECT @@session.unique_checks; @@session.unique_checks -0 +1 '#--------------------FN_DYNVARS_005_04-------------------------#' SET @@session.unique_checks =1; SELECT @@session.unique_checks; diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic index a18440e550d..f547bd741fc 100644 --- a/sql/sys_vars.ic +++ b/sql/sys_vars.ic @@ -1704,13 +1704,16 @@ public: return false; } void session_save_default(THD *thd, set_var *var) - { var->save_result.ulonglong_value= global_var(ulonglong) & bitmask; } + { + var->save_result.ulonglong_value= + (reverse_semantics == !(global_var(ulonglong) & bitmask)); + } void global_save_default(THD *thd, set_var *var) { var->save_result.ulonglong_value= option.def_value; } uchar *valptr(THD *thd, ulonglong val) { - thd->sys_var_tmp.my_bool_value= reverse_semantics ^ ((val & bitmask) != 0); + thd->sys_var_tmp.my_bool_value= (reverse_semantics == !(val & bitmask)); return (uchar*) &thd->sys_var_tmp.my_bool_value; } uchar *session_value_ptr(THD *thd, const LEX_STRING *base) From 0be70a1b773ce66ef1803fcce19522fd9c60c07d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Mon, 17 Aug 2020 08:57:13 +0300 Subject: [PATCH 09/15] MDEV-23483: Set Galera SST thd as system thread Revert change to MDL and set SST donor thread as a system thread. Joiner thread was already a system thread. --- mysql-test/suite/galera/t/mdev-22543.test | 20 +++++++++--------- sql/mdl.cc | 25 ++++++++--------------- sql/wsrep_sst.cc | 18 +++++----------- sql/wsrep_utils.cc | 4 +++- sql/wsrep_utils.h | 2 +- 5 files changed, 27 insertions(+), 42 deletions(-) diff --git a/mysql-test/suite/galera/t/mdev-22543.test b/mysql-test/suite/galera/t/mdev-22543.test index 53662e36942..1e7d3712639 100644 --- a/mysql-test/suite/galera/t/mdev-22543.test +++ b/mysql-test/suite/galera/t/mdev-22543.test @@ -5,15 +5,15 @@ --source include/galera_cluster.inc --source include/have_debug.inc --source include/have_debug_sync.inc - + --let $node_1 = node_1 --let $node_2 = node_2 --source include/auto_increment_offset_save.inc - + --let $galera_connection_name = node_1_ctrl --let $galera_server_number = 1 --source include/galera_connect.inc - + # # Run UPDATE on node_1 and make it block before table locks are taken. # This should block FTWRL. @@ -23,10 +23,10 @@ CREATE TABLE t1 (f1 INT PRIMARY KEY, f2 INT); INSERT INTO t1 VALUES (1, 1); SET DEBUG_SYNC = "before_lock_tables_takes_lock SIGNAL sync_point_reached WAIT_FOR sync_point_continue"; --send UPDATE t1 SET f2 = 2 WHERE f1 = 1 - + --connection node_1_ctrl SET DEBUG_SYNC = "now WAIT_FOR sync_point_reached"; - + # # Restart node_2, force SST. # @@ -40,19 +40,19 @@ SET DEBUG_SYNC = "now WAIT_FOR sync_point_reached"; # If the bug is present, FTWRL times out on node_1 in couple of # seconds and node_2 fails to join. --sleep 10 - + --connection node_1_ctrl SET DEBUG_SYNC = "now SIGNAL sync_point_continue"; - + --connection node_1 --reap SET DEBUG_SYNC = "RESET"; - + --connection node_2 --enable_reconnect --source include/wait_until_connected_again.inc - + --connection node_1 DROP TABLE t1; - + --source include/auto_increment_offset_restore.inc diff --git a/sql/mdl.cc b/sql/mdl.cc index 14a1f17fe86..9eeb82eeffd 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -25,7 +25,6 @@ #include #include "wsrep_mysqld.h" #include "wsrep_thd.h" -#include "wsrep_sst.h" #ifdef HAVE_PSI_INTERFACE static PSI_mutex_key key_MDL_wait_LOCK_wait_status; @@ -2138,26 +2137,18 @@ MDL_context::acquire_lock(MDL_request *mdl_request, double lock_wait_timeout) wait_status= m_wait.timed_wait(m_owner, &abs_shortwait, FALSE, mdl_request->key.get_wait_state_name()); - THD* thd= m_owner->get_thd(); - if (wait_status != MDL_wait::EMPTY) break; /* Check if the client is gone while we were waiting. */ - if (! thd_is_connected(thd)) + if (! thd_is_connected(m_owner->get_thd())) { -#if defined(WITH_WSREP) && !defined(EMBEDDED_LIBRARY) - // During SST client might not be connected - if (!wsrep_is_sst_progress()) -#endif - { - /* - * The client is disconnected. Don't wait forever: - * assume it's the same as a wait timeout, this - * ensures all error handling is correct. - */ - wait_status= MDL_wait::TIMEOUT; - break; - } + /* + * The client is disconnected. Don't wait forever: + * assume it's the same as a wait timeout, this + * ensures all error handling is correct. + */ + wait_status= MDL_wait::TIMEOUT; + break; } mysql_prlock_wrlock(&lock->m_rwlock); diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc index a6accd52910..af3a80cb67c 100644 --- a/sql/wsrep_sst.cc +++ b/sql/wsrep_sst.cc @@ -1,4 +1,4 @@ -/* Copyright 2008-2015 Codership Oy +/* Copyright 2008-2020 Codership Oy 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 @@ -192,7 +192,6 @@ bool wsrep_before_SE() static bool sst_complete = false; static bool sst_needed = false; -static bool sst_in_progress = false; #define WSREP_EXTEND_TIMEOUT_INTERVAL 30 #define WSREP_TIMEDWAIT_SECONDS 10 @@ -1545,11 +1544,11 @@ static void* sst_donor_thread (void* a) wsrep_uuid_t ret_uuid= WSREP_UUID_UNDEFINED; // seqno of complete SST wsrep_seqno_t ret_seqno= WSREP_SEQNO_UNDEFINED; - // SST is now in progress - sst_in_progress= true; - wsp::thd thd(FALSE); // we turn off wsrep_on for this THD so that it can - // operate with wsrep_ready == OFF + // We turn off wsrep_on for this THD so that it can + // operate with wsrep_ready == OFF + // We also set this SST thread THD as system thread + wsp::thd thd(FALSE, true); wsp::process proc(arg->cmd, "r", arg->env); err= proc.error(); @@ -1648,8 +1647,6 @@ wait_signal: wsrep->sst_sent (wsrep, &state_id, -err); proc.wait(); - sst_in_progress= false; - return NULL; } @@ -1824,8 +1821,3 @@ void wsrep_SE_initialized() { SE_initialized = true; } - -bool wsrep_is_sst_progress() -{ - return (sst_in_progress); -} diff --git a/sql/wsrep_utils.cc b/sql/wsrep_utils.cc index 599ece4cb40..e86be3e5d93 100644 --- a/sql/wsrep_utils.cc +++ b/sql/wsrep_utils.cc @@ -413,7 +413,7 @@ process::wait () return err_; } -thd::thd (my_bool won) : init(), ptr(new THD(0)) +thd::thd (my_bool won, bool system_thread) : init(), ptr(new THD(0)) { if (ptr) { @@ -421,6 +421,8 @@ thd::thd (my_bool won) : init(), ptr(new THD(0)) ptr->store_globals(); ptr->variables.option_bits&= ~OPTION_BIN_LOG; // disable binlog ptr->variables.wsrep_on = won; + if (system_thread) + ptr->system_thread= SYSTEM_THREAD_GENERIC; ptr->security_ctx->master_access= ~(ulong)0; lex_start(ptr); } diff --git a/sql/wsrep_utils.h b/sql/wsrep_utils.h index 616a6d3f457..d4d9e62a620 100644 --- a/sql/wsrep_utils.h +++ b/sql/wsrep_utils.h @@ -298,7 +298,7 @@ class thd public: - thd(my_bool wsrep_on); + thd(my_bool wsrep_on, bool system_thread=false); ~thd(); THD* const ptr; }; From c0e5cf79ad13fad4b34fa7d0777cc90035db93c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 25 Aug 2020 15:23:20 +0300 Subject: [PATCH 10/15] MDEV-22543: Remove orphan declaration of wsrep_is_sst_progress --- sql/wsrep_sst.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/sql/wsrep_sst.h b/sql/wsrep_sst.h index 5a749d529fb..063cab5f0f1 100644 --- a/sql/wsrep_sst.h +++ b/sql/wsrep_sst.h @@ -74,14 +74,12 @@ extern void wsrep_SE_init_grab(); /*! grab init critical section */ extern void wsrep_SE_init_wait(); /*! wait for SE init to complete */ extern void wsrep_SE_init_done(); /*! signal that SE init is complte */ extern void wsrep_SE_initialized(); /*! mark SE initialization complete */ -extern bool wsrep_is_sst_progress(); #else #define wsrep_SE_initialized() do { } while(0) #define wsrep_SE_init_grab() do { } while(0) #define wsrep_SE_init_done() do { } while(0) #define wsrep_sst_continue() (0) -#define wsrep_is_sst_progress() (0) #endif /* WITH_WSREP */ #endif /* WSREP_SST_H */ From 8cf8ad86d4b6f3479d80f3d8e8c2bcf463966924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 25 Aug 2020 15:32:15 +0300 Subject: [PATCH 11/15] MDEV-23547 InnoDB: Failing assertion: *len in row_upd_ext_fetch This bug was originally repeated on 10.4 after defining a UNIQUE KEY on a TEXT column, which is implemented by MDEV-371 by creating the index on a hidden virtual column. While row_vers_vc_matches_cluster() is executing in a purge thread to find out if an index entry may be removed in a secondary index that comprises a virtual column, another purge thread may process the undo log record that this check is interested in, and write a null BLOB pointer in that record. This would trip the assertion. To prevent this from occurring, we must propagate the 'missing BLOB' error up the call stack. row_upd_ext_fetch(): Return NULL when the error occurs. row_upd_index_replace_new_col_val(): Return whether the previous version was built successfully. row_upd_index_replace_new_col_vals_index_pos(): Check the error result. Yes, we would intentionally crash on this error if it occurs outside the purge thread. row_upd_index_replace_new_col_vals(): Check for the error condition, and simplify the logic. trx_undo_prev_version_build(): Check for the error condition. --- storage/innobase/include/row0upd.h | 30 +++----- storage/innobase/row/row0upd.cc | 115 ++++++++++++++--------------- storage/innobase/trx/trx0rec.cc | 6 +- 3 files changed, 72 insertions(+), 79 deletions(-) diff --git a/storage/innobase/include/row0upd.h b/storage/innobase/include/row0upd.h index 04701042658..b60770b01fb 100644 --- a/storage/innobase/include/row0upd.h +++ b/storage/innobase/include/row0upd.h @@ -257,24 +257,18 @@ row_upd_index_replace_new_col_vals_index_pos( mem_heap_t* heap) /*!< in: memory heap for allocating and copying the new values */ MY_ATTRIBUTE((nonnull)); -/***********************************************************//** -Replaces the new column values stored in the update vector to the index entry -given. */ -void -row_upd_index_replace_new_col_vals( -/*===============================*/ - dtuple_t* entry, /*!< in/out: index entry where replaced; - the clustered index record must be - covered by a lock or a page latch to - prevent deletion (rollback or purge) */ - dict_index_t* index, /*!< in: index; NOTE that this may also be a - non-clustered index */ - const upd_t* update, /*!< in: an update vector built for the - CLUSTERED index so that the field number in - an upd_field is the clustered index position */ - mem_heap_t* heap) /*!< in: memory heap for allocating and - copying the new values */ - MY_ATTRIBUTE((nonnull)); +/** Replace the new column values stored in the update vector, +during trx_undo_prev_version_build(). +@param entry clustered index tuple where the values are replaced + (the clustered index leaf page latch must be held) +@param index clustered index +@param update update vector for the clustered index +@param heap memory heap for allocating and copying values +@return whether the previous version was built successfully */ +bool +row_upd_index_replace_new_col_vals(dtuple_t *entry, const dict_index_t &index, + const upd_t *update, mem_heap_t *heap) + MY_ATTRIBUTE((nonnull, warn_unused_result)); /***********************************************************//** Replaces the new column values stored in the update vector. */ void diff --git a/storage/innobase/row/row0upd.cc b/storage/innobase/row/row0upd.cc index 800c7a6d1f3..6d210cfd6bb 100644 --- a/storage/innobase/row/row0upd.cc +++ b/storage/innobase/row/row0upd.cc @@ -1206,7 +1206,9 @@ containing also the reference to the external part @param[in,out] len input - length of prefix to fetch; output: fetched length of the prefix @param[in,out] heap heap where to allocate -@return BLOB prefix */ +@return BLOB prefix +@retval NULL if the record is incomplete (should only happen +in row_vers_vc_matches_cluster() executed concurrently with another purge) */ static byte* row_upd_ext_fetch( @@ -1221,10 +1223,7 @@ row_upd_ext_fetch( *len = btr_copy_externally_stored_field_prefix( buf, *len, page_size, data, local_len); - /* We should never update records containing a half-deleted BLOB. */ - ut_a(*len); - - return(buf); + return *len ? buf : NULL; } /** Replaces the new column value stored in the update vector in @@ -1235,9 +1234,11 @@ the given index entry field. @param[in] uf update field @param[in,out] heap memory heap for allocating and copying the new value -@param[in] page_size page size */ +@param[in] page_size page size +@return whether the previous version was built successfully */ +MY_ATTRIBUTE((nonnull, warn_unused_result)) static -void +bool row_upd_index_replace_new_col_val( dfield_t* dfield, const dict_field_t* field, @@ -1252,7 +1253,7 @@ row_upd_index_replace_new_col_val( dfield_copy_data(dfield, &uf->new_val); if (dfield_is_null(dfield)) { - return; + return true; } len = dfield_get_len(dfield); @@ -1270,6 +1271,9 @@ row_upd_index_replace_new_col_val( data = row_upd_ext_fetch(data, l, page_size, &len, heap); + if (UNIV_UNLIKELY(!data)) { + return false; + } } len = dtype_get_at_most_n_mbchars(col->prtype, @@ -1283,7 +1287,7 @@ row_upd_index_replace_new_col_val( dfield_dup(dfield, heap); } - return; + return true; } switch (uf->orig_len) { @@ -1322,6 +1326,8 @@ row_upd_index_replace_new_col_val( dfield_set_ext(dfield); break; } + + return true; } /***********************************************************//** @@ -1379,68 +1385,57 @@ row_upd_index_replace_new_col_vals_index_pos( update, i, false); } - if (uf) { - row_upd_index_replace_new_col_val( - dtuple_get_nth_field(entry, i), - field, col, uf, heap, page_size); + if (uf && UNIV_UNLIKELY(!row_upd_index_replace_new_col_val( + dtuple_get_nth_field(entry, i), + field, col, uf, heap, + page_size))) { + ut_error; } } } -/***********************************************************//** -Replaces the new column values stored in the update vector to the index entry -given. */ -void -row_upd_index_replace_new_col_vals( -/*===============================*/ - dtuple_t* entry, /*!< in/out: index entry where replaced; - the clustered index record must be - covered by a lock or a page latch to - prevent deletion (rollback or purge) */ - dict_index_t* index, /*!< in: index; NOTE that this may also be a - non-clustered index */ - const upd_t* update, /*!< in: an update vector built for the - CLUSTERED index so that the field number in - an upd_field is the clustered index position */ - mem_heap_t* heap) /*!< in: memory heap for allocating and - copying the new values */ +/** Replace the new column values stored in the update vector, +during trx_undo_prev_version_build(). +@param entry clustered index tuple where the values are replaced + (the clustered index leaf page latch must be held) +@param index clustered index +@param update update vector for the clustered index +@param heap memory heap for allocating and copying values +@return whether the previous version was built successfully */ +bool +row_upd_index_replace_new_col_vals(dtuple_t *entry, const dict_index_t &index, + const upd_t *update, mem_heap_t *heap) { - ulint i; - const dict_index_t* clust_index - = dict_table_get_first_index(index->table); - const page_size_t& page_size = dict_table_page_size(index->table); + ut_ad(index.is_primary()); + const page_size_t& page_size= dict_table_page_size(index.table); - ut_ad(!index->table->skip_alter_undo); + ut_ad(!index.table->skip_alter_undo); + dtuple_set_info_bits(entry, update->info_bits); - dtuple_set_info_bits(entry, update->info_bits); + for (ulint i= 0; i < index.n_fields; i++) + { + const dict_field_t *field= &index.fields[i]; + const dict_col_t* col= dict_field_get_col(field); + const upd_field_t *uf; - for (i = 0; i < dict_index_get_n_fields(index); i++) { - const dict_field_t* field; - const dict_col_t* col; - const upd_field_t* uf; + if (col->is_virtual()) + { + const dict_v_col_t *vcol= reinterpret_cast(col); + uf= upd_get_field_by_field_no(update, vcol->v_pos, true); + } + else + uf= upd_get_field_by_field_no(update, dict_col_get_clust_pos(col, &index), + false); - field = dict_index_get_nth_field(index, i); - col = dict_field_get_col(field); - if (dict_col_is_virtual(col)) { - const dict_v_col_t* vcol = reinterpret_cast< - const dict_v_col_t*>( - col); + if (!uf) + continue; - uf = upd_get_field_by_field_no( - update, vcol->v_pos, true); - } else { - uf = upd_get_field_by_field_no( - update, - dict_col_get_clust_pos(col, clust_index), - false); - } + if (!row_upd_index_replace_new_col_val(dtuple_get_nth_field(entry, i), + field, col, uf, heap, page_size)) + return false; + } - if (uf) { - row_upd_index_replace_new_col_val( - dtuple_get_nth_field(entry, i), - field, col, uf, heap, page_size); - } - } + return true; } /** Replaces the virtual column values stored in the update vector. diff --git a/storage/innobase/trx/trx0rec.cc b/storage/innobase/trx/trx0rec.cc index ee022b4f1fd..623d8990381 100644 --- a/storage/innobase/trx/trx0rec.cc +++ b/storage/innobase/trx/trx0rec.cc @@ -2446,7 +2446,11 @@ trx_undo_prev_version_build( /* The page containing the clustered index record corresponding to entry is latched in mtr. Thus the following call is safe. */ - row_upd_index_replace_new_col_vals(entry, index, update, heap); + if (!row_upd_index_replace_new_col_vals(entry, *index, update, + heap)) { + ut_a(v_status & TRX_UNDO_PREV_IN_PURGE); + return false; + } /* Get number of externally stored columns in updated record */ const ulint n_ext = dtuple_get_n_ext(entry); From 6586bb51f3167fad9e353e8c9cd69f51e0acec21 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 25 Aug 2020 22:21:07 +0300 Subject: [PATCH 12/15] MDEV-23467 SIGSEGV in fill_record/fill_record_n_invoke_before_triggers on INSERT DELAYED Field::make_new_field() resets invisible property (needed for "CREATE .. SELECT" f.ex.). Recover invisible property in Delayed_insert::get_local_table() (unireg_check works by the same principle). --- mysql-test/main/invisible_field.result | 6 ++++++ mysql-test/main/invisible_field.test | 8 ++++++++ sql/sql_insert.cc | 1 + 3 files changed, 15 insertions(+) diff --git a/mysql-test/main/invisible_field.result b/mysql-test/main/invisible_field.result index 053a9fd8e50..ee45567d212 100644 --- a/mysql-test/main/invisible_field.result +++ b/mysql-test/main/invisible_field.result @@ -617,3 +617,9 @@ a b 1 3 2 4 drop table t1; +# +# MDEV-23467 SIGSEGV in fill_record/fill_record_n_invoke_before_triggers on INSERT DELAYED +# +create table t1 (a int, b int invisible); +insert delayed into t1 values (1); +drop table t1; diff --git a/mysql-test/main/invisible_field.test b/mysql-test/main/invisible_field.test index 0e3994a78ce..7a48347ec29 100644 --- a/mysql-test/main/invisible_field.test +++ b/mysql-test/main/invisible_field.test @@ -271,3 +271,11 @@ select a,b from t1; #cleanup drop table t1; + +--echo # +--echo # MDEV-23467 SIGSEGV in fill_record/fill_record_n_invoke_before_triggers on INSERT DELAYED +--echo # +create table t1 (a int, b int invisible); +insert delayed into t1 values (1); +# cleanup +drop table t1; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index a24cb61449b..b574e7ab9d7 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2614,6 +2614,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) if (!(*field= (*org_field)->make_new_field(client_thd->mem_root, copy, 1))) goto error; (*field)->unireg_check= (*org_field)->unireg_check; + (*field)->invisible= (*org_field)->invisible; (*field)->orig_table= copy; // Remove connection (*field)->move_field_offset(adjust_ptrs); // Point at copy->record[0] memdup_vcol(client_thd, (*field)->vcol_info); From 95831888e8a89a4e141e76d51dbfc0701552c824 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 25 Aug 2020 22:21:08 +0300 Subject: [PATCH 13/15] part_records() signature fix --- sql/ha_partition.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 6a12c8a4800..b80c2174658 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -1614,9 +1614,8 @@ public: return h; } - ha_rows part_records(void *_part_elem) + ha_rows part_records(partition_element *part_elem) { - partition_element *part_elem= reinterpret_cast(_part_elem); DBUG_ASSERT(m_part_info); uint32 sub_factor= m_part_info->num_subparts ? m_part_info->num_subparts : 1; uint32 part_id= part_elem->id * sub_factor; From 65f30050aafb3821ba63a3b837c98cf4d2334254 Mon Sep 17 00:00:00 2001 From: Varun Gupta Date: Mon, 24 Aug 2020 17:22:21 +0530 Subject: [PATCH 14/15] MDEV-18335: Assertion `!error || error == 137' failed in subselect_rowid_merge_engine::init When duplicates are removed from a table using a hash, if the record is a duplicate it is marked as deleted. The handler API check if the record is deleted and send an error flag HA_ERR_RECORD_DELETED. When we scan over the table if the thread is not killed then we skip the records marked as HA_ERR_RECORD_DELETED. The issue here is when a query is aborted by a user (this is happening when the LIMIT for ROWS EXAMINED is exceeded), the scan over the table does not skip the records for which HA_ERR_RECORD_DELETED is sent. It just returns an error flag HA_ERR_ABORTED_BY_USER. This error flag is not checked at the upper level and hence we hit the assert. If the query is aborted by the user we should just skip reading rows and return control to the upper levels of execution. --- mysql-test/main/subselect4.result | 31 ++++++++++++++++++++++++++ mysql-test/main/subselect4.test | 36 +++++++++++++++++++++++++++++++ sql/item_subselect.cc | 3 +++ 3 files changed, 70 insertions(+) diff --git a/mysql-test/main/subselect4.result b/mysql-test/main/subselect4.result index d1d3b514549..020a7064291 100644 --- a/mysql-test/main/subselect4.result +++ b/mysql-test/main/subselect4.result @@ -2686,4 +2686,35 @@ SELECT * FROM t2; f bar DROP TABLE t1, t2; +# +# MDEV-18335: Assertion `!error || error == 137' failed in subselect_rowid_merge_engine::init +# +CREATE TABLE t1 (i1 int,v1 varchar(1),KEY (v1,i1)); +INSERT INTO t1 VALUES +(9,'y'),(4,'q'),(0,null),(0,'p'),(null,'j'); +CREATE TABLE t2 (pk int); +INSERT INTO t2 VALUES (1),(2); +CREATE TABLE t3 (v2 varchar(1)); +INSERT INTO t3 VALUES +('p'),('j'),('y'),('q'); +CREATE TABLE t4 (v2 varchar(1)); +INSERT INTO t4 VALUES +('a'),('a'),('b'),('b'),('c'),('c'), +('d'),('d'),('e'),('e'),('f'),('f'), +('g'),('g'),('h'),('h'),('i'),('i'), +('j'),('j'),('k'),('k'),('l'),('l'), +('m'),('m'),('n'),('n'),('o'),('o'), +('p'),('p'),('q'),('q'),('r'),('r'), +('s'),('s'),('t'),('t'),('u'),('u'),('v'),('v'), +('w'),('w'),('x'),('x'), (NULL),(NULL); +SET @save_join_cache_level=@@join_cache_level; +SET join_cache_level=0; +select 1 +from t2 join t1 on +('i','w') not in (select t1.v1,t4.v2 from t4,t1,t3 where t3.v2 = t1.v1) LIMIT ROWS EXAMINED 500; +1 +Warnings: +Warning 1931 Query execution was interrupted. The query examined at least 3020 rows, which exceeds LIMIT ROWS EXAMINED (500). The query result may be incomplete +SET join_cache_level= @save_join_cache_level; +DROP TABLE t1,t2,t3,t4; # End of 10.2 tests diff --git a/mysql-test/main/subselect4.test b/mysql-test/main/subselect4.test index 6eada9b27d9..6f5eb1f2985 100644 --- a/mysql-test/main/subselect4.test +++ b/mysql-test/main/subselect4.test @@ -2201,4 +2201,40 @@ SELECT * FROM t2; DROP TABLE t1, t2; +--echo # +--echo # MDEV-18335: Assertion `!error || error == 137' failed in subselect_rowid_merge_engine::init +--echo # + +CREATE TABLE t1 (i1 int,v1 varchar(1),KEY (v1,i1)); +INSERT INTO t1 VALUES +(9,'y'),(4,'q'),(0,null),(0,'p'),(null,'j'); + +CREATE TABLE t2 (pk int); +INSERT INTO t2 VALUES (1),(2); + +CREATE TABLE t3 (v2 varchar(1)); +INSERT INTO t3 VALUES +('p'),('j'),('y'),('q'); + +CREATE TABLE t4 (v2 varchar(1)); +INSERT INTO t4 VALUES +('a'),('a'),('b'),('b'),('c'),('c'), +('d'),('d'),('e'),('e'),('f'),('f'), +('g'),('g'),('h'),('h'),('i'),('i'), +('j'),('j'),('k'),('k'),('l'),('l'), +('m'),('m'),('n'),('n'),('o'),('o'), +('p'),('p'),('q'),('q'),('r'),('r'), +('s'),('s'),('t'),('t'),('u'),('u'),('v'),('v'), +('w'),('w'),('x'),('x'), (NULL),(NULL); + +SET @save_join_cache_level=@@join_cache_level; +SET join_cache_level=0; + +select 1 +from t2 join t1 on +('i','w') not in (select t1.v1,t4.v2 from t4,t1,t3 where t3.v2 = t1.v1) LIMIT ROWS EXAMINED 500; +SET join_cache_level= @save_join_cache_level; + +DROP TABLE t1,t2,t3,t4; + --echo # End of 10.2 tests diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index c32cd5ef84a..bc5111667ff 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -6281,6 +6281,9 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts, while (TRUE) { error= tmp_table->file->ha_rnd_next(tmp_table->record[0]); + + if (error == HA_ERR_ABORTED_BY_USER) + break; /* This is a temp table that we fully own, there should be no other cause to stop the iteration than EOF. From 21a96581fd1dd11c5605dd89e3adbaabdf6de5eb Mon Sep 17 00:00:00 2001 From: Stepan Patryshev Date: Wed, 26 Aug 2020 04:16:55 +0300 Subject: [PATCH 15/15] MDEV-23583 Fix up community suite/galera_3nodes/disabled.def --- mysql-test/suite/galera_3nodes/disabled.def | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/mysql-test/suite/galera_3nodes/disabled.def b/mysql-test/suite/galera_3nodes/disabled.def index 1683485981b..415da407cf7 100644 --- a/mysql-test/suite/galera_3nodes/disabled.def +++ b/mysql-test/suite/galera_3nodes/disabled.def @@ -1,2 +1,21 @@ +############################################################################## +# +# List the test cases that are to be disabled temporarily. +# +# Separate the test case name and the comment with ':'. +# +# : MDEV- +# +# Do not use any TAB characters for whitespace. +# +############################################################################## + +galera_ipv6_mariabackup : MDEV-23573 Could not open '../galera/include/have_mariabackup.inc' +galera_ipv6_mariabackup_section : MDEV-23574 Could not open '../galera/include/have_mariabackup.inc' +galera_ipv6_mysqldump : MDEV-23576 WSREP_SST: [ERROR] rsync daemon port '16008' has been taken +galera_ipv6_rsyn : MDEV-23581 WSREP_SST: [ERROR] rsync daemon port '16008' has been taken +galera_ipv6_rsync_section : MDEV-23580 WSREP_SST: [ERROR] rsync daemon port '16008' has been taken +galera_ipv6_xtrabackup-v2 : MDEV-23575 WSREP_SST: [ERROR] innobackupex not in path +galera_ist_gcache_rollover : MDEV-23578 WSREP: exception caused by message: {v=0,t=1,ut=255,o=4,s=0,sr=0,as=1,f=6,src=50524cfe,srcvid=view_id(REG,50524cfe,4),insvid=view_id(UNKNOWN,00000000,0),ru=00000000,r=[-1,-1],fs=75,nl=(} galera_slave_options_do :MDEV-8798 galera_slave_options_ignore : MDEV-8798