diff options
author | Sergei Golubchik <serg@mariadb.org> | 2022-05-08 15:02:19 +0200 |
---|---|---|
committer | Sergei Golubchik <serg@mariadb.org> | 2022-05-08 23:03:08 +0200 |
commit | a70a1cf3f4ed10c9d9194b2b8df6b6f08551a937 (patch) | |
tree | 517124ec5059f48bff1e02b934355e8162183efc /sql | |
parent | 40b8f3ec1a76fc23eb6bf9c5a8fef1debcbf5843 (diff) | |
parent | 79660e59ee8fcd23f928c72dc77682b875bd58ce (diff) | |
download | mariadb-git-a70a1cf3f4ed10c9d9194b2b8df6b6f08551a937.tar.gz |
Merge branch '10.3' into 10.4
Diffstat (limited to 'sql')
36 files changed, 609 insertions, 216 deletions
diff --git a/sql/field.cc b/sql/field.cc index 7d34054a4a2..b5f7e468d46 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -3207,11 +3207,12 @@ Field_new_decimal::Field_new_decimal(uchar *ptr_arg, uint8 dec_arg,bool zero_arg, bool unsigned_arg) :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, dec_arg, zero_arg, unsigned_arg) + unireg_check_arg, field_name_arg, + MY_MIN(dec_arg, DECIMAL_MAX_SCALE), zero_arg, unsigned_arg) { precision= get_decimal_precision(len_arg, dec_arg, unsigned_arg); - DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION) && - (dec <= DECIMAL_MAX_SCALE)); + DBUG_ASSERT(precision <= DECIMAL_MAX_PRECISION); + DBUG_ASSERT(dec <= DECIMAL_MAX_SCALE); bin_size= my_decimal_get_binary_size(precision, dec); } diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 0fcf966f71b..bf430a00f18 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -4012,6 +4012,8 @@ int ha_partition::external_lock(THD *thd, int lock_type) if (lock_type == F_UNLCK) { bitmap_clear_all(used_partitions); + if (m_lock_type == F_WRLCK && m_part_info->vers_require_hist_part(thd)) + m_part_info->vers_check_limit(thd); } else { @@ -4032,13 +4034,7 @@ int ha_partition::external_lock(THD *thd, int lock_type) { if (m_part_info->part_expr) m_part_info->part_expr->walk(&Item::register_field_in_read_map, 1, 0); - if (m_part_info->part_type == VERSIONING_PARTITION && - /* TODO: MDEV-20345 exclude more inapproriate commands like INSERT - These commands may be excluded because working history partition is needed - only for versioned DML. */ - thd->lex->sql_command != SQLCOM_SELECT && - thd->lex->sql_command != SQLCOM_INSERT_SELECT && - (error= m_part_info->vers_set_hist_part(thd))) + if ((error= m_part_info->vers_set_hist_part(thd))) goto err_handler; } DBUG_RETURN(0); @@ -4191,11 +4187,7 @@ int ha_partition::start_stmt(THD *thd, thr_lock_type lock_type) case TL_WRITE_ONLY: if (m_part_info->part_expr) m_part_info->part_expr->walk(&Item::register_field_in_read_map, 1, 0); - if (m_part_info->part_type == VERSIONING_PARTITION && - // TODO: MDEV-20345 (see above) - thd->lex->sql_command != SQLCOM_SELECT && - thd->lex->sql_command != SQLCOM_INSERT_SELECT) - error= m_part_info->vers_set_hist_part(thd); + error= m_part_info->vers_set_hist_part(thd); default:; } DBUG_RETURN(error); diff --git a/sql/handler.cc b/sql/handler.cc index 22f5aa4e7e7..4092ee99e0b 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -5182,6 +5182,9 @@ int handler::calculate_checksum() for (uint i= 0; i < table->s->fields; i++ ) { Field *f= table->field[i]; + if (!f->stored_in_db()) + continue; + if (! thd->variables.old_mode && f->is_real_null(0)) { diff --git a/sql/item.cc b/sql/item.cc index 4c699bb974d..a791d5bf8ca 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -924,8 +924,7 @@ bool Item_field::check_field_expression_processor(void *arg) (!field->vcol_info && !org_field->vcol_info)) && field->field_index >= org_field->field_index)) { - my_error(ER_EXPRESSION_REFERS_TO_UNINIT_FIELD, - MYF(0), + my_error(ER_EXPRESSION_REFERS_TO_UNINIT_FIELD, MYF(0), org_field->field_name.str, field->field_name.str); return 1; } @@ -9377,6 +9376,12 @@ bool Item_default_value::eq(const Item *item, bool binary_cmp) const } +bool Item_default_value::check_field_expression_processor(void *) +{ + field->default_value= ((Item_field *)(arg->real_item()))->field->default_value; + return 0; +} + bool Item_default_value::fix_fields(THD *thd, Item **items) { Item *real_arg; @@ -9418,7 +9423,6 @@ bool Item_default_value::fix_fields(THD *thd, Item **items) } if (!(def_field= (Field*) thd->alloc(field_arg->field->size_of()))) goto error; - cached_field= def_field; memcpy((void *)def_field, (void *)field_arg->field, field_arg->field->size_of()); def_field->reset_fields(); @@ -9447,8 +9451,7 @@ error: void Item_default_value::cleanup() { - delete cached_field; // Free cached blob data - cached_field= 0; + delete field; // Free cached blob data Item_field::cleanup(); } diff --git a/sql/item.h b/sql/item.h index af6f350216d..f074ea1e3da 100644 --- a/sql/item.h +++ b/sql/item.h @@ -6457,12 +6457,11 @@ class Item_default_value : public Item_field void calculate(); public: Item *arg; - Field *cached_field; Item_default_value(THD *thd, Name_resolution_context *context_arg, Item *a, bool vcol_assignment_arg) :Item_field(thd, context_arg, (const char *)NULL, (const char *)NULL, &null_clex_str), vcol_assignment_ok(vcol_assignment_arg), - arg(a), cached_field(NULL) {} + arg(a) {} enum Type type() const { return DEFAULT_VALUE_ITEM; } bool eq(const Item *item, bool binary_cmp) const; bool fix_fields(THD *, Item **); @@ -6475,6 +6474,10 @@ public: bool get_date(THD *thd, MYSQL_TIME *ltime,date_mode_t fuzzydate); bool val_native(THD *thd, Native *to); bool val_native_result(THD *thd, Native *to); + longlong val_datetime_packed(THD *thd) + { return Item::val_datetime_packed(thd); } + longlong val_time_packed(THD *thd) + { return Item::val_time_packed(thd); } /* Result variants */ double val_result(); @@ -6495,16 +6498,16 @@ public: return false; } table_map used_tables() const; - virtual void update_used_tables() + void update_used_tables() { if (field && field->default_value) field->default_value->expr->update_used_tables(); } bool vcol_assignment_allowed_value() const { return vcol_assignment_ok; } - Field *get_tmp_table_field() { return 0; } Item *get_tmp_table_item(THD *thd) { return this; } Item_field *field_for_view_update() { return 0; } bool update_vcol_processor(void *arg) { return 0; } + bool check_field_expression_processor(void *arg); bool check_func_default_processor(void *arg) { return true; } bool walk(Item_processor processor, bool walk_subquery, void *args) @@ -6782,7 +6785,7 @@ public: for any value. */ -class Item_cache: public Item, +class Item_cache: public Item_fixed_hybrid, public Type_handler_hybrid_field_type { protected: @@ -6813,7 +6816,7 @@ public: bool null_value_inside; Item_cache(THD *thd): - Item(thd), + Item_fixed_hybrid(thd), Type_handler_hybrid_field_type(&type_handler_string), example(0), cached_field(0), value_cached(0), @@ -6822,10 +6825,11 @@ public: maybe_null= 1; null_value= 1; null_value_inside= true; + fixed= 1; } protected: Item_cache(THD *thd, const Type_handler *handler): - Item(thd), + Item_fixed_hybrid(thd), Type_handler_hybrid_field_type(handler), example(0), cached_field(0), value_cached(0), @@ -6834,6 +6838,7 @@ protected: maybe_null= 1; null_value= 1; null_value_inside= true; + fixed= 1; } public: @@ -6885,10 +6890,17 @@ public: } return mark_unsupported_function("cache", arg, VCOL_IMPOSSIBLE); } + bool fix_fields(THD *thd, Item **ref) + { + fixed= 1; + if (example && !example->is_fixed()) + return example->fix_fields(thd, ref); + return 0; + } void cleanup() { clear(); - Item::cleanup(); + Item_fixed_hybrid::cleanup(); } /** Check if saved item has a non-NULL value. diff --git a/sql/item_func.cc b/sql/item_func.cc index 1660652001c..935208423a4 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2447,7 +2447,7 @@ void Item_func_round::fix_arg_decimal() set_handler(&type_handler_newdecimal); unsigned_flag= args[0]->unsigned_flag; decimals= args[0]->decimals; - max_length= float_length(args[0]->decimals) + 1; + max_length= args[0]->max_length; } } diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index d19e89ad6af..50b8d9192c5 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -773,6 +773,7 @@ bool Item_subselect::exec() DBUG_ENTER("Item_subselect::exec"); DBUG_ASSERT(fixed); + DBUG_ASSERT(!eliminated); DBUG_EXECUTE_IF("Item_subselect", Item::Print print(this, @@ -1282,11 +1283,18 @@ bool Item_singlerow_subselect::fix_length_and_dec() } unsigned_flag= value->unsigned_flag; /* - If there are not tables in subquery then ability to have NULL value - depends on SELECT list (if single row subquery have tables then it - always can be NULL if there are not records fetched). + If the subquery has no tables (1) and is not a UNION (2), like: + + (SELECT subq_value) + + then its NULLability is the same as subq_value's NULLability. + + (1): A subquery that uses a table will return NULL when the table is empty. + (2): A UNION subquery will return NULL if it produces a "Subquery returns + more than one row" error. */ - if (engine->no_tables()) + if (engine->no_tables() && + engine->engine_type() != subselect_engine::UNION_ENGINE) maybe_null= engine->may_be_null(); else { @@ -1322,6 +1330,16 @@ Item* Item_singlerow_subselect::expr_cache_insert_transformer(THD *tmp_thd, DBUG_ASSERT(thd == tmp_thd); + /* + Do not create subquery cache if the subquery was eliminated. + The optimizer may eliminate subquery items (see + eliminate_subselect_processor). However it does not update + all query's data structures, so the eliminated item may be + still reachable. + */ + if (eliminated) + DBUG_RETURN(this); + if (expr_cache) DBUG_RETURN(expr_cache); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 8098d1aa5e0..f80b89bc828 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1540,6 +1540,8 @@ void Item_sum_sum::fix_length_and_dec_decimal() decimals= args[0]->decimals; /* SUM result can't be longer than length(arg) + length(MAX_ROWS) */ int precision= args[0]->decimal_precision() + DECIMAL_LONGLONG_DIGITS; + decimals= MY_MIN(decimals, DECIMAL_MAX_SCALE); + precision= MY_MIN(precision, DECIMAL_MAX_PRECISION); max_length= my_decimal_precision_to_length_no_truncation(precision, decimals, unsigned_flag); @@ -1950,12 +1952,12 @@ void Item_sum_avg::fix_length_and_dec_decimal() { Item_sum_sum::fix_length_and_dec_decimal(); int precision= args[0]->decimal_precision() + prec_increment; - decimals= MY_MIN(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE); + decimals= MY_MIN(args[0]->decimal_scale() + prec_increment, DECIMAL_MAX_SCALE); max_length= my_decimal_precision_to_length_no_truncation(precision, decimals, unsigned_flag); f_precision= MY_MIN(precision+DECIMAL_LONGLONG_DIGITS, DECIMAL_MAX_PRECISION); - f_scale= args[0]->decimals; + f_scale= args[0]->decimal_scale(); dec_bin_size= my_decimal_get_binary_size(f_precision, f_scale); } diff --git a/sql/partition_info.cc b/sql/partition_info.cc index edcdd6d2b37..90ef388f3b9 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -835,6 +835,9 @@ bool partition_info::has_unique_name(partition_element *element) int partition_info::vers_set_hist_part(THD *thd) { + if (!vers_require_hist_part(thd)) + return 0; + if (table->pos_in_table_list && table->pos_in_table_list->partition_names) { @@ -843,12 +846,10 @@ int partition_info::vers_set_hist_part(THD *thd) if (vers_info->limit) { ha_partition *hp= (ha_partition*)(table->file); - partition_element *next= NULL; + partition_element *next; List_iterator<partition_element> it(partitions); - while (next != vers_info->hist_part) - next= it++; - DBUG_ASSERT(bitmap_is_set(&read_partitions, next->id)); - ha_rows records= hp->part_records(next); + ha_rows records= 0; + vers_info->hist_part= partitions.head(); while ((next= it++) != vers_info->now_part) { DBUG_ASSERT(bitmap_is_set(&read_partitions, next->id)); @@ -858,12 +859,8 @@ int partition_info::vers_set_hist_part(THD *thd) vers_info->hist_part= next; records= next_records; } - if (records > vers_info->limit) - { - if (next == vers_info->now_part) - goto warn; + if (records >= vers_info->limit && next != vers_info->now_part) vers_info->hist_part= next; - } return 0; } @@ -885,11 +882,45 @@ int partition_info::vers_set_hist_part(THD *thd) } } return 0; -warn: - my_error(WARN_VERS_PART_FULL, MYF(ME_WARNING|ME_ERROR_LOG), - table->s->db.str, table->s->table_name.str, - vers_info->hist_part->partition_name); - return 0; +} + + +/** + Warn at the end of DML command if the last history partition is out of LIMIT. +*/ +void partition_info::vers_check_limit(THD *thd) +{ + if (!vers_info->limit || + vers_info->hist_part->id + 1 < vers_info->now_part->id) + return; + + /* + NOTE: at this point read_partitions bitmap is already pruned by DML code, + we have to set read bits for working history partition. We could use + bitmap_set_all(), but this is not optimal since there can be quite a number + of partitions. + */ + const uint32 sub_factor= num_subparts ? num_subparts : 1; + uint32 part_id= vers_info->hist_part->id * sub_factor; + const uint32 part_id_end= part_id + sub_factor; + DBUG_ASSERT(part_id_end <= num_parts * sub_factor); + for (; part_id < part_id_end; ++part_id) + bitmap_set_bit(&read_partitions, part_id); + + ha_partition *hp= (ha_partition*)(table->file); + ha_rows hist_rows= hp->part_records(vers_info->hist_part); + if (hist_rows >= vers_info->limit) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + WARN_VERS_PART_FULL, + ER_THD(thd, WARN_VERS_PART_FULL), + table->s->db.str, table->s->table_name.str, + vers_info->hist_part->partition_name); + + sql_print_warning(ER_THD(thd, WARN_VERS_PART_FULL), + table->s->db.str, table->s->table_name.str, + vers_info->hist_part->partition_name); + } } diff --git a/sql/partition_info.h b/sql/partition_info.h index 9fb20268738..2f8b1a7fafd 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -421,7 +421,13 @@ public: vers_info->limit= limit; return !limit; } + bool vers_require_hist_part(THD *thd) const + { + return part_type == VERSIONING_PARTITION && + thd->lex->vers_history_generating(); + } int vers_set_hist_part(THD *thd); + void vers_check_limit(THD *thd); bool vers_setup_expression(THD *thd, uint32 alter_add= 0); /* Stage 1. */ partition_element *get_partition(uint part_id) { diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h index b633716bfe5..081f7e309f4 100644 --- a/sql/rpl_gtid.h +++ b/sql/rpl_gtid.h @@ -26,6 +26,7 @@ extern const LEX_CSTRING rpl_gtid_slave_state_table_name; class String; +#define PARAM_GTID(G) G.domain_id, G.server_id, G.seq_no struct rpl_gtid { diff --git a/sql/slave.cc b/sql/slave.cc index 029fd0f5aaf..d0bc7e474df 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -3240,9 +3240,9 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full, protocol->store((uint32) mi->master_id); // SQL_Delay // Master_Ssl_Crl - protocol->store(mi->ssl_ca, &my_charset_bin); + protocol->store(mi->ssl_crl, &my_charset_bin); // Master_Ssl_Crlpath - protocol->store(mi->ssl_capath, &my_charset_bin); + protocol->store(mi->ssl_crlpath, &my_charset_bin); // Using_Gtid protocol->store(mi->using_gtid_astext(mi->using_gtid), &my_charset_bin); // Gtid_IO_Pos @@ -3334,7 +3334,7 @@ bool show_all_master_info(THD* thd) String gtid_pos; Master_info **tmp; List<Item> field_list; - DBUG_ENTER("show_master_info"); + DBUG_ENTER("show_all_master_info"); mysql_mutex_assert_owner(&LOCK_active_mi); gtid_pos.length(0); @@ -6615,17 +6615,75 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) mi->gtid_event_seen= true; /* - We have successfully queued to relay log everything before this GTID, so + Unless the previous group is malformed, + we have successfully queued to relay log everything before this GTID, so in case of reconnect we can start from after any previous GTID. - (Normally we would have updated gtid_current_pos earlier at the end of - the previous event group, but better leave an extra check here for - safety). + (We must have updated gtid_current_pos earlier at the end of + the previous event group. Unless ...) */ - if (mi->events_queued_since_last_gtid) + if (unlikely(mi->events_queued_since_last_gtid > 0)) { - mi->gtid_current_pos.update(&mi->last_queued_gtid); - mi->events_queued_since_last_gtid= 0; + /* + ...unless the last group has not been completed. An assert below + can be satisfied only with the strict mode that ensures + against "genuine" gtid duplicates. + */ + rpl_gtid *gtid_in_slave_state= + mi->gtid_current_pos.find(mi->last_queued_gtid.domain_id); + + // Slave gtid state must not have updated yet to the last received gtid. + DBUG_ASSERT((mi->using_gtid == Master_info::USE_GTID_NO || + !opt_gtid_strict_mode) || + (!gtid_in_slave_state || + !(*gtid_in_slave_state == mi->last_queued_gtid))); + + DBUG_EXECUTE_IF("slave_discard_xid_for_gtid_0_x_1000", + { + /* Inject an event group that is missing its XID commit event. */ + if ((mi->last_queued_gtid.domain_id == 0 && + mi->last_queued_gtid.seq_no == 1000) || + (mi->last_queued_gtid.domain_id == 1 && + mi->last_queued_gtid.seq_no == 32)) + { + sql_print_warning( + "Unexpected break of being relay-logged GTID %u-%u-%llu " + "event group by the current GTID event %u-%u-%llu", + PARAM_GTID(mi->last_queued_gtid),PARAM_GTID(event_gtid)); + DBUG_SET("-d,slave_discard_xid_for_gtid_0_x_1000"); + goto dbug_gtid_accept; + } + }); + error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE; + sql_print_error("Unexpected break of being relay-logged GTID %u-%u-%llu " + "event group by the current GTID event %u-%u-%llu", + PARAM_GTID(mi->last_queued_gtid),PARAM_GTID(event_gtid)); + goto err; } + else if (unlikely(mi->gtid_reconnect_event_skip_count > 0)) + { + if (mi->gtid_reconnect_event_skip_count == + mi->events_queued_since_last_gtid) + { + DBUG_ASSERT(event_gtid == mi->last_queued_gtid); + + goto default_action; + } + + DBUG_ASSERT(0); + } + // else_likely{... +#ifndef DBUG_OFF +dbug_gtid_accept: + DBUG_EXECUTE_IF("slave_discard_gtid_0_x_1002", + { + if (mi->last_queued_gtid.server_id == 27697 && + mi->last_queued_gtid.seq_no == 1002) + { + DBUG_SET("-d,slave_discard_gtid_0_x_1002"); + goto skip_relay_logging; + } + }); +#endif mi->last_queued_gtid= event_gtid; mi->last_queued_gtid_standalone= (gtid_flag & Gtid_log_event::FL_STANDALONE) != 0; @@ -6635,6 +6693,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) ++mi->events_queued_since_last_gtid; inc_pos= event_len; + // ...} eof else_likely } break; /* @@ -6713,6 +6772,12 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) case XID_EVENT: DBUG_EXECUTE_IF("slave_discard_xid_for_gtid_0_x_1000", { + if (mi->last_queued_gtid.server_id == 27697 && + mi->last_queued_gtid.seq_no == 1000) + { + DBUG_SET("-d,slave_discard_xid_for_gtid_0_x_1000"); + goto skip_relay_logging; + } /* Inject an event group that is missing its XID commit event. */ if (mi->last_queued_gtid.domain_id == 0 && mi->last_queued_gtid.seq_no == 1000) @@ -6758,15 +6823,48 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) } };); - if (mi->using_gtid != Master_info::USE_GTID_NO && mi->gtid_event_seen) + if (mi->using_gtid != Master_info::USE_GTID_NO) { - if (unlikely(mi->gtid_reconnect_event_skip_count)) + if (likely(mi->gtid_event_seen)) { - --mi->gtid_reconnect_event_skip_count; - gtid_skip_enqueue= true; + if (unlikely(mi->gtid_reconnect_event_skip_count)) + { + if (!got_gtid_event && + mi->gtid_reconnect_event_skip_count == + mi->events_queued_since_last_gtid) + goto gtid_not_start; // the 1st re-sent must be gtid + + --mi->gtid_reconnect_event_skip_count; + gtid_skip_enqueue= true; + } + else if (likely(mi->events_queued_since_last_gtid)) + { + DBUG_ASSERT(!got_gtid_event); + + ++mi->events_queued_since_last_gtid; + } + else if (Log_event::is_group_event((Log_event_type) (uchar) + buf[EVENT_TYPE_OFFSET])) + { + goto gtid_not_start; // no first gtid event in this group + } + } + else if (Log_event::is_group_event((Log_event_type) (uchar) + buf[EVENT_TYPE_OFFSET])) + { + gtid_not_start: + + DBUG_ASSERT(!got_gtid_event); + + error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE; + sql_print_error("The current group of events starts with " + "a non-GTID %s event; " + "the last seen GTID is %u-%u-%llu", + Log_event::get_type_str((Log_event_type) (uchar) + buf[EVENT_TYPE_OFFSET]), + mi->last_queued_gtid); + goto err; } - else if (mi->events_queued_since_last_gtid) - ++mi->events_queued_since_last_gtid; } if (!is_compress_event) @@ -6959,11 +7057,13 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) The whole of the current event group is queued. So in case of reconnect we can start from after the current GTID. */ - if (mi->gtid_reconnect_event_skip_count) + if (gtid_skip_enqueue) { bool first= true; StringBuffer<1024> gtid_text; + DBUG_ASSERT(mi->events_queued_since_last_gtid > 1); + rpl_slave_state_tostring_helper(>id_text, &mi->last_queued_gtid, &first); sql_print_error("Slave IO thread received a terminal event from " @@ -6973,12 +7073,39 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) gtid_text.ptr(), mi->gtid_reconnect_event_skip_count, mi->events_queued_since_last_gtid); + error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE; + goto err; } mi->gtid_current_pos.update(&mi->last_queued_gtid); mi->events_queued_since_last_gtid= 0; - /* Reset the domain_id_filter flag. */ - mi->domain_id_filter.reset_filter(); + if (unlikely(gtid_skip_enqueue)) + { + error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE; + sql_print_error("Recieved a group closing %s event " + "at %llu position in the group while there are " + "still %llu events to skip upon reconnecting; " + "the last seen GTID is %u-%u-%llu", + Log_event::get_type_str((Log_event_type) (uchar) + buf[EVENT_TYPE_OFFSET]), + (mi->events_queued_since_last_gtid - + mi->gtid_reconnect_event_skip_count), + mi->events_queued_since_last_gtid, + mi->last_queued_gtid); + goto err; + } + else + { + /* + The whole of the current event group is queued. So in case of + reconnect we can start from after the current GTID. + */ + mi->gtid_current_pos.update(&mi->last_queued_gtid); + mi->events_queued_since_last_gtid= 0; + + /* Reset the domain_id_filter flag. */ + mi->domain_id_filter.reset_filter(); + } } skip_relay_logging: diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc index e4ffbdcb155..e97f24c5487 100644 --- a/sql/sp_cache.cc +++ b/sql/sp_cache.cc @@ -312,3 +312,12 @@ sp_cache::cleanup() { my_hash_free(&m_hashtable); } + + +void Sp_caches::sp_caches_clear() +{ + sp_cache_clear(&sp_proc_cache); + sp_cache_clear(&sp_func_cache); + sp_cache_clear(&sp_package_spec_cache); + sp_cache_clear(&sp_package_body_cache); +} diff --git a/sql/sp_head.cc b/sql/sp_head.cc index ab950c50251..6102769a47d 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -769,7 +769,7 @@ void sp_head::set_stmt_end(THD *thd) { Lex_input_stream *lip= & thd->m_parser_state->m_lip; /* shortcut */ - const char *end_ptr= lip->get_cpp_ptr(); /* shortcut */ + const char *end_ptr= lip->get_cpp_tok_start(); /* shortcut */ /* Make the string of parameters. */ diff --git a/sql/sql_base.cc b/sql/sql_base.cc index ef7a075e304..42285e9b3a4 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -907,7 +907,14 @@ void close_thread_tables(THD *thd) table->s->table_name.str, (ulong) table->query_id)); if (thd->locked_tables_mode) + { +#ifdef WITH_PARTITION_STORAGE_ENGINE + if (table->part_info && table->part_info->vers_require_hist_part(thd) && + !thd->stmt_arena->is_stmt_prepare()) + table->part_info->vers_check_limit(thd); +#endif table->vcol_cleanup_expr(thd); + } /* Detach MERGE children after every statement. Even under LOCK TABLES. */ if (thd->locked_tables_mode <= LTM_LOCK_TABLES || diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 6fb657d3c00..775f3c173ef 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2000, 2015, Oracle and/or its affiliates. - Copyright (c) 2008, 2021, MariaDB Corporation. + Copyright (c) 2008, 2022, 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 @@ -818,11 +818,6 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) (my_hash_get_key) get_sequence_last_key, (my_hash_free_key) free_sequence_last, HASH_THREAD_SPECIFIC); - sp_proc_cache= NULL; - sp_func_cache= NULL; - sp_package_spec_cache= NULL; - sp_package_body_cache= NULL; - /* For user vars replication*/ if (opt_bin_log) my_init_dynamic_array(&user_var_events, @@ -1431,10 +1426,7 @@ void THD::change_user(void) my_hash_init(&sequences, system_charset_info, SEQUENCES_HASH_SIZE, 0, 0, (my_hash_get_key) get_sequence_last_key, (my_hash_free_key) free_sequence_last, HASH_THREAD_SPECIFIC); - sp_cache_clear(&sp_proc_cache); - sp_cache_clear(&sp_func_cache); - sp_cache_clear(&sp_package_spec_cache); - sp_cache_clear(&sp_package_body_cache); + sp_caches_clear(); opt_trace.delete_traces(); } @@ -1563,10 +1555,7 @@ void THD::cleanup(void) my_hash_free(&user_vars); my_hash_free(&sequences); - sp_cache_clear(&sp_proc_cache); - sp_cache_clear(&sp_func_cache); - sp_cache_clear(&sp_package_spec_cache); - sp_cache_clear(&sp_package_body_cache); + sp_caches_clear(); auto_inc_intervals_forced.empty(); auto_inc_intervals_in_cur_stmt_for_binlog.empty(); @@ -6420,47 +6409,84 @@ int THD::decide_logging_format(TABLE_LIST *tables) DBUG_RETURN(0); } -int THD::decide_logging_format_low(TABLE *table) + +/* + Reconsider logging format in case of INSERT...ON DUPLICATE KEY UPDATE + for tables with more than one unique keys in case of MIXED binlog format. + + Unsafe means that a master could execute the statement differently than + the slave. + This could can happen in the following cases: + - The unique check are done in different order on master or slave + (different engine or different key order). + - There is a conflict on another key than the first and before the + statement is committed, another connection commits a row that conflicts + on an earlier unique key. Example follows: + + Below a and b are unique keys, the table has a row (1,1,0) + connection 1: + INSERT INTO t1 set a=2,b=1,c=0 ON DUPLICATE KEY UPDATE c=1; + connection 2: + INSERT INTO t1 set a=2,b=2,c=0; + + If 2 commits after 1 has been executed but before 1 has committed + (and are thus put before the other in the binary log), one will + get different data on the slave: + (1,1,1),(2,2,1) instead of (1,1,1),(2,2,0) +*/ + +void THD::reconsider_logging_format_for_iodup(TABLE *table) { - /* - INSERT...ON DUPLICATE KEY UPDATE on a table with more than one unique keys - can be unsafe. - */ - if(wsrep_binlog_format() <= BINLOG_FORMAT_STMT && - !is_current_stmt_binlog_format_row() && - !lex->is_stmt_unsafe() && - lex->sql_command == SQLCOM_INSERT && - lex->duplicates == DUP_UPDATE) + DBUG_ENTER("reconsider_logging_format_for_iodup"); + enum_binlog_format bf= (enum_binlog_format) wsrep_binlog_format(); + + DBUG_ASSERT(lex->duplicates == DUP_UPDATE); + + if (bf <= BINLOG_FORMAT_STMT && + !is_current_stmt_binlog_format_row()) { + KEY *end= table->s->key_info + table->s->keys; uint unique_keys= 0; - uint keys= table->s->keys, i= 0; - Field *field; - for (KEY* keyinfo= table->s->key_info; - i < keys && unique_keys <= 1; i++, keyinfo++) - if (keyinfo->flags & HA_NOSAME && - !(keyinfo->key_part->field->flags & AUTO_INCREMENT_FLAG && - //User given auto inc can be unsafe - !keyinfo->key_part->field->val_int())) + + for (KEY *keyinfo= table->s->key_info; keyinfo < end ; keyinfo++) + { + if (keyinfo->flags & HA_NOSAME) { + /* + We assume that the following cases will guarantee that the + key is unique if a key part is not set: + - The key part is an autoincrement (autogenerated) + - The key part has a default value that is null and it not + a virtual field that will be calculated later. + */ for (uint j= 0; j < keyinfo->user_defined_key_parts; j++) { - field= keyinfo->key_part[j].field; - if(!bitmap_is_set(table->write_set,field->field_index)) - goto exit; + Field *field= keyinfo->key_part[j].field; + if (!bitmap_is_set(table->write_set, field->field_index)) + { + /* Check auto_increment */ + if (field == table->next_number_field) + goto exit; + if (field->is_real_null() && !field->default_value) + goto exit; + } } - unique_keys++; + if (unique_keys++) + break; exit:; } - + } if (unique_keys > 1) { - lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_TWO_KEYS); - binlog_unsafe_warning_flags|= lex->get_stmt_unsafe_flags(); + if (bf == BINLOG_FORMAT_STMT && !lex->is_stmt_unsafe()) + { + lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_TWO_KEYS); + binlog_unsafe_warning_flags|= lex->get_stmt_unsafe_flags(); + } set_current_stmt_binlog_format_row_if_mixed(); - return 1; } } - return 0; + DBUG_VOID_RETURN; } /* diff --git a/sql/sql_class.h b/sql/sql_class.h index 0d1e14b1679..19466b3de90 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1,6 +1,6 @@ /* Copyright (c) 2000, 2016, Oracle and/or its affiliates. - Copyright (c) 2009, 2021, MariaDB Corporation. + Copyright (c) 2009, 2022, 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 @@ -1054,7 +1054,7 @@ public: /* We build without RTTI, so dynamic_cast can't be used. */ enum Type { - STATEMENT, PREPARED_STATEMENT, STORED_PROCEDURE, TABLE_ARENA + STATEMENT, PREPARED_STATEMENT, STORED_PROCEDURE }; Query_arena(MEM_ROOT *mem_root_arg, enum enum_state state_arg) : @@ -2169,6 +2169,39 @@ struct wait_for_commit void reinit(); }; + +class Sp_caches +{ +public: + sp_cache *sp_proc_cache; + sp_cache *sp_func_cache; + sp_cache *sp_package_spec_cache; + sp_cache *sp_package_body_cache; + Sp_caches() + :sp_proc_cache(NULL), + sp_func_cache(NULL), + sp_package_spec_cache(NULL), + sp_package_body_cache(NULL) + { } + ~Sp_caches() + { + // All caches must be freed by the caller explicitly + DBUG_ASSERT(sp_proc_cache == NULL); + DBUG_ASSERT(sp_func_cache == NULL); + DBUG_ASSERT(sp_package_spec_cache == NULL); + DBUG_ASSERT(sp_package_body_cache == NULL); + } + void sp_caches_swap(Sp_caches &rhs) + { + swap_variables(sp_cache*, sp_proc_cache, rhs.sp_proc_cache); + swap_variables(sp_cache*, sp_func_cache, rhs.sp_func_cache); + swap_variables(sp_cache*, sp_package_spec_cache, rhs.sp_package_spec_cache); + swap_variables(sp_cache*, sp_package_body_cache, rhs.sp_package_body_cache); + } + void sp_caches_clear(); +}; + + extern "C" void my_message_sql(uint error, const char *str, myf MyFlags); @@ -2189,7 +2222,8 @@ class THD: public THD_count, /* this must be first */ */ public Item_change_list, public MDL_context_owner, - public Open_tables_state + public Open_tables_state, + public Sp_caches { private: inline bool is_stmt_prepare() const @@ -3183,10 +3217,6 @@ public: enum_sql_command last_sql_command; // Last sql_command exceuted in mysql_execute_command() sp_rcontext *spcont; // SP runtime context - sp_cache *sp_proc_cache; - sp_cache *sp_func_cache; - sp_cache *sp_package_spec_cache; - sp_cache *sp_package_body_cache; /** number of name_const() substitutions, see sp_head.cc:subst_spvars() */ uint query_name_consts; @@ -3969,8 +3999,7 @@ public: bool is_item_tree_change_register_required() { - return !stmt_arena->is_conventional() - || stmt_arena->type() == Query_arena::TABLE_ARENA; + return !stmt_arena->is_conventional(); } void change_item_tree(Item **place, Item *new_value) @@ -4588,18 +4617,18 @@ public: mdl_context.release_transactional_locks(this); } int decide_logging_format(TABLE_LIST *tables); + /* - In Some cases when decide_logging_format is called it does not have all - information to decide the logging format. So that cases we call decide_logging_format_2 - at later stages in execution. - One example would be binlog format for IODKU but column with unique key is not inserted. - We dont have inserted columns info when we call decide_logging_format so on later stage we call - decide_logging_format_low + In Some cases when decide_logging_format is called it does not have + all information to decide the logging format. So that cases we call + decide_logging_format_2 at later stages in execution. - @returns 0 if no format is changed - 1 if there is change in binlog format + One example would be binlog format for insert on duplicate key + (IODKU) but column with unique key is not inserted. We do not have + inserted columns info when we call decide_logging_format so on + later stage we call reconsider_logging_format_for_iodup() */ - int decide_logging_format_low(TABLE *table); + void reconsider_logging_format_for_iodup(TABLE *table); enum need_invoker { INVOKER_NONE=0, INVOKER_USER, INVOKER_ROLE}; void binlog_invoker(bool role) { m_binlog_invoker= role ? INVOKER_ROLE : INVOKER_USER; } diff --git a/sql/sql_expression_cache.cc b/sql/sql_expression_cache.cc index 351ec258ddb..bc44ecd79fa 100644 --- a/sql/sql_expression_cache.cc +++ b/sql/sql_expression_cache.cc @@ -63,7 +63,7 @@ void Expression_cache_tmptable::disable_cache() cache_table= NULL; update_tracker(); if (tracker) - tracker->cache= NULL; + tracker->detach_from_cache(); } @@ -189,6 +189,8 @@ Expression_cache_tmptable::~Expression_cache_tmptable() else { update_tracker(); + if (tracker) + tracker->detach_from_cache(); tracker= NULL; } } diff --git a/sql/sql_expression_cache.h b/sql/sql_expression_cache.h index 61e0c4c69b3..d4bb252dccb 100644 --- a/sql/sql_expression_cache.h +++ b/sql/sql_expression_cache.h @@ -83,7 +83,11 @@ public: cache(c), hit(0), miss(0), state(UNINITED) {} +private: + // This can be NULL if the cache is already deleted Expression_cache *cache; + +public: ulong hit, miss; enum expr_cache_state state; @@ -91,6 +95,7 @@ public: void set(ulong h, ulong m, enum expr_cache_state s) {hit= h; miss= m; state= s;} + void detach_from_cache() { cache= NULL; } void fetch_current_stats() { if (cache) diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 073d523b876..eb0dc1c2b3f 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -699,8 +699,10 @@ mysql_ha_fix_cond_and_key(SQL_HANDLER *handler, if (!in_prepare) { MY_BITMAP *old_map= dbug_tmp_use_all_columns(table, &table->write_set); - (void) item->save_in_field(key_part->field, 1); + int res= item->save_in_field(key_part->field, 1); dbug_tmp_restore_column_map(&table->write_set, old_map); + if (res) + return 1; } key_len+= key_part->store_length; keypart_map= (keypart_map << 1) | 1; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 460fbba4ac5..7e848b3906e 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2000, 2016, Oracle and/or its affiliates. - Copyright (c) 2010, 2021, MariaDB Corporation + Copyright (c) 2010, 2022, 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 @@ -951,6 +951,12 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, } THD_STAGE_INFO(thd, stage_update); + + if (duplic == DUP_UPDATE) + { + restore_record(table,s->default_values); // Get empty record + thd->reconsider_logging_format_for_iodup(table); + } do { DBUG_PRINT("info", ("iteration %llu", iteration)); @@ -1063,7 +1069,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, break; } - thd->decide_logging_format_low(table); #ifndef EMBEDDED_LIBRARY if (lock_type == TL_WRITE_DELAYED) { diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index e622d5fb863..290dcdb33b9 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2019, Oracle and/or its affiliates. - Copyright (c) 2009, 2021, MariaDB Corporation. + Copyright (c) 2009, 2022, 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 @@ -209,7 +209,6 @@ init_lex_with_single_table(THD *thd, TABLE *table, LEX *lex) return TRUE; context->resolve_in_table_list_only(table_list); lex->use_only_table_context= TRUE; - lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VCOL_EXPR; select_lex->cur_pos_in_select_list= UNDEF_POS; table->map= 1; //To ensure correct calculation of const item table_list->table= table; @@ -4608,17 +4607,21 @@ void SELECT_LEX::update_used_tables() while ((tl= ti++)) { TABLE_LIST *embedding= tl; - do + if (!is_eliminated_table(join->eliminated_tables, tl)) { - bool maybe_null; - if ((maybe_null= MY_TEST(embedding->outer_join))) + do { - tl->table->maybe_null= maybe_null; - break; + bool maybe_null; + if ((maybe_null= MY_TEST(embedding->outer_join))) + { + tl->table->maybe_null= maybe_null; + break; + } } + while ((embedding= embedding->embedding)); } - while ((embedding= embedding->embedding)); - if (tl->on_expr) + + if (tl->on_expr && !is_eliminated_table(join->eliminated_tables, tl)) { tl->on_expr->update_used_tables(); tl->on_expr->walk(&Item::eval_not_null_tables, 0, NULL); @@ -4642,8 +4645,11 @@ void SELECT_LEX::update_used_tables() if (embedding->on_expr && embedding->nested_join->join_list.head() == tl) { - embedding->on_expr->update_used_tables(); - embedding->on_expr->walk(&Item::eval_not_null_tables, 0, NULL); + if (!is_eliminated_table(join->eliminated_tables, embedding)) + { + embedding->on_expr->update_used_tables(); + embedding->on_expr->walk(&Item::eval_not_null_tables, 0, NULL); + } } tl= embedding; embedding= tl->embedding; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 6a3a01f69d6..3db50222a27 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -4436,6 +4436,29 @@ public: return create_info.vers_info; } + /* The list of history-generating DML commands */ + bool vers_history_generating() const + { + switch (sql_command) + { + case SQLCOM_DELETE: + return !vers_conditions.delete_history; + case SQLCOM_UPDATE: + case SQLCOM_UPDATE_MULTI: + case SQLCOM_DELETE_MULTI: + case SQLCOM_REPLACE: + case SQLCOM_REPLACE_SELECT: + return true; + case SQLCOM_INSERT: + case SQLCOM_INSERT_SELECT: + return duplicates == DUP_UPDATE; + case SQLCOM_LOAD: + return duplicates == DUP_REPLACE; + default: + return false; + } + } + int add_period(Lex_ident name, Lex_ident_sys_st start, Lex_ident_sys_st end) { if (lex_string_cmp(system_charset_info, &start, &end) == 0) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 6a1b849b912..b5e6b6540c5 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -8852,6 +8852,7 @@ bool st_select_lex::add_window_def(THD *thd, fields_in_window_functions+= win_part_list_ptr->elements + win_order_list_ptr->elements; } + win_def->win_spec_number= window_specs.elements; return (win_def == NULL || window_specs.push_back(win_def)); } @@ -8879,6 +8880,7 @@ bool st_select_lex::add_window_spec(THD *thd, win_order_list_ptr->elements; } thd->lex->win_spec= win_spec; + win_spec->win_spec_number= window_specs.elements; return (win_spec == NULL || window_specs.push_back(win_spec)); } diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 87b4d241028..2790f69e41e 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -355,7 +355,8 @@ bool check_valid_path(const char *path, size_t len) static void fix_dl_name(MEM_ROOT *root, LEX_CSTRING *dl) { const size_t so_ext_len= sizeof(SO_EXT) - 1; - if (my_strcasecmp(&my_charset_latin1, dl->str + dl->length - so_ext_len, + if (dl->length < so_ext_len || + my_strcasecmp(&my_charset_latin1, dl->str + dl->length - so_ext_len, SO_EXT)) { char *s= (char*)alloc_root(root, dl->length + so_ext_len + 1); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 735e00dd091..1b8147a6cfb 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -18087,7 +18087,7 @@ Field *Item_default_value::create_tmp_field_ex(TABLE *table, Tmp_field_src *src, const Tmp_field_param *param) { - if (field->default_value && (field->flags & BLOB_FLAG)) + if (field->default_value || (field->flags & BLOB_FLAG)) { /* We have to use a copy function when using a blob with default value @@ -18488,6 +18488,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, table->copy_blobs= 1; table->in_use= thd; table->no_rows_with_nulls= param->force_not_null_cols; + table->expr_arena= thd; table->update_handler= NULL; table->check_unique_buf= NULL; @@ -25483,15 +25484,17 @@ change_to_use_tmp_fields(THD *thd, Ref_ptr_array ref_pointer_array, for (uint i= 0; (item= it++); i++) { Field *field; - if ((item->with_sum_func() && item->type() != Item::SUM_FUNC_ITEM) || + enum Item::Type item_type= item->type(); + if ((item->with_sum_func() && item_type != Item::SUM_FUNC_ITEM) || item->with_window_func) item_field= item; - else if (item->type() == Item::FIELD_ITEM) + else if (item_type == Item::FIELD_ITEM || + item_type == Item::DEFAULT_VALUE_ITEM) { if (!(item_field= item->get_tmp_table_item(thd))) DBUG_RETURN(true); } - else if (item->type() == Item::FUNC_ITEM && + else if (item_type == Item::FUNC_ITEM && ((Item_func*)item)->functype() == Item_func::SUSERVAR_FUNC) { field= item->get_tmp_table_field(); @@ -27369,7 +27372,7 @@ static void print_table_array(THD *thd, too) */ -static bool is_eliminated_table(table_map eliminated_tables, TABLE_LIST *tbl) +bool is_eliminated_table(table_map eliminated_tables, TABLE_LIST *tbl) { return eliminated_tables && ((tbl->table && (tbl->table->map & eliminated_tables)) || diff --git a/sql/sql_select.h b/sql/sql_select.h index 28bf33997a0..9a6237f00a6 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -2,7 +2,7 @@ #define SQL_SELECT_INCLUDED /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. - Copyright (c) 2008, 2020, MariaDB Corporation. + Copyright (c) 2008, 2022, 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 @@ -2540,6 +2540,8 @@ int create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort); JOIN_TAB *first_explain_order_tab(JOIN* join); JOIN_TAB *next_explain_order_tab(JOIN* join, JOIN_TAB* tab); +bool is_eliminated_table(table_map eliminated_tables, TABLE_LIST *tbl); + bool check_simple_equality(THD *thd, const Item::Context &ctx, Item *left_item, Item *right_item, COND_EQUAL *cond_equal); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index d9d5c5d135c..eb3ee3d5f74 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -5181,6 +5181,7 @@ public: int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) { + DBUG_ENTER("get_all_tables"); LEX *lex= thd->lex; TABLE *table= tables->table; TABLE_LIST table_acl_check; @@ -5198,7 +5199,29 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) uint table_open_method= tables->table_open_method; bool can_deadlock; MEM_ROOT tmp_mem_root; - DBUG_ENTER("get_all_tables"); + /* + We're going to open FRM files for tables. + In case of VIEWs that contain stored function calls, + these stored functions will be parsed and put to the SP cache. + + Suppose we have a view containing a stored function call: + CREATE VIEW v1 AS SELECT f1() AS c1; + and now we're running: + SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=f1(); + If a parallel thread invalidates the cache, + e.g. by creating or dropping some stored routine, + the SELECT query will re-parse f1() when processing "v1" + and replace the outdated cached version of f1() to a new one. + But the old version of f1() is referenced from the m_sp member + of the Item_func_sp instances used in the WHERE condition. + We cannot destroy it. To avoid such clashes, let's remember + all old routines into a temporary SP cache collection + and process tables with a new empty temporary SP cache collection. + Then restore to the old SP cache collection at the end. + */ + Sp_caches old_sp_caches; + + old_sp_caches.sp_caches_swap(*thd); bzero(&tmp_mem_root, sizeof(tmp_mem_root)); @@ -5374,6 +5397,13 @@ err: thd->restore_backup_open_tables_state(&open_tables_state_backup); free_root(&tmp_mem_root, 0); + /* + Now restore to the saved SP cache collection + and clear the temporary SP cache collection. + */ + old_sp_caches.sp_caches_swap(*thd); + old_sp_caches.sp_caches_clear(); + DBUG_RETURN(error); } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index d58dbc2b0d6..9414735fa4e 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -8100,6 +8100,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, { /* New column definitions are added here */ List<Create_field> new_create_list; + /* System-invisible fields must be added last */ + List<Create_field> new_create_tail; /* New key definitions are added here */ List<Key> new_key_list; List_iterator<Alter_drop> drop_it(alter_info->drop_list); @@ -8325,7 +8327,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, dropped_sys_vers_fields|= field->flags; drop_it.remove(); } - else + else if (field->invisible < INVISIBLE_SYSTEM) { /* This field was not dropped and not changed, add it to the list @@ -8350,6 +8352,12 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, alter_it.remove(); } } + else + { + DBUG_ASSERT(field->invisible == INVISIBLE_SYSTEM); + def= new (thd->mem_root) Create_field(thd, field, field); + new_create_tail.push_back(def, thd->mem_root); + } } dropped_sys_vers_fields &= VERS_SYSTEM_FIELD; if ((dropped_sys_vers_fields || @@ -8482,6 +8490,9 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, alter_it.remove(); } } + + new_create_list.append(&new_create_tail); + if (unlikely(alter_info->alter_list.elements)) { my_error(ER_BAD_FIELD_ERROR, MYF(0), diff --git a/sql/sql_type.cc b/sql/sql_type.cc index e0a6169df3d..28fd2418e3b 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -2057,7 +2057,6 @@ Type_handler_decimal_result::make_num_distinct_aggregator_field( const Item *item) const { - DBUG_ASSERT(item->decimals <= DECIMAL_MAX_SCALE); return new (mem_root) Field_new_decimal(NULL, item->max_length, (uchar *) (item->maybe_null ? "" : 0), diff --git a/sql/sql_window.cc b/sql/sql_window.cc index 876ddef8f65..bf36b592d96 100644 --- a/sql/sql_window.cc +++ b/sql/sql_window.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, 2017 MariaDB + Copyright (c) 2016, 2022 MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -424,16 +424,49 @@ ORDER *st_select_lex::find_common_window_func_partition_fields(THD *thd) #define CMP_GT 2 // Greater then static -int compare_order_elements(ORDER *ord1, ORDER *ord2) +int compare_order_elements(ORDER *ord1, int weight1, + ORDER *ord2, int weight2) { if (*ord1->item == *ord2->item && ord1->direction == ord2->direction) return CMP_EQ; Item *item1= (*ord1->item)->real_item(); Item *item2= (*ord2->item)->real_item(); - DBUG_ASSERT(item1->type() == Item::FIELD_ITEM && - item2->type() == Item::FIELD_ITEM); - int cmp= ((Item_field *) item1)->field->field_index - - ((Item_field *) item2)->field->field_index; + + bool item1_field= (item1->type() == Item::FIELD_ITEM); + bool item2_field= (item2->type() == Item::FIELD_ITEM); + + ptrdiff_t cmp; + if (item1_field && item2_field) + { + DBUG_ASSERT(((Item_field *) item1)->field->table == + ((Item_field *) item2)->field->table); + cmp= ((Item_field *) item1)->field->field_index - + ((Item_field *) item2)->field->field_index; + } + else if (item1_field && !item2_field) + return CMP_LT; + else if (!item1_field && item2_field) + return CMP_LT; + else + { + /* + Ok, item1_field==NULL and item2_field==NULL. + We're not able to compare Item expressions. Order them according to + their passed "weight" (which comes from Window_spec::win_spec_number): + */ + if (weight1 != weight2) + cmp= weight1 - weight2; + else + { + /* + The weight is the same. That is, the elements come from the same + window specification... This shouldn't happen. + */ + DBUG_ASSERT(0); + cmp= item1 - item2; + } + } + if (cmp == 0) { if (ord1->direction == ord2->direction) @@ -446,7 +479,9 @@ int compare_order_elements(ORDER *ord1, ORDER *ord2) static int compare_order_lists(SQL_I_List<ORDER> *part_list1, - SQL_I_List<ORDER> *part_list2) + int spec_number1, + SQL_I_List<ORDER> *part_list2, + int spec_number2) { if (part_list1 == part_list2) return CMP_EQ; @@ -471,7 +506,8 @@ int compare_order_lists(SQL_I_List<ORDER> *part_list1, if (!elem1 || !elem2) break; - if ((cmp= compare_order_elements(elem1, elem2))) + if ((cmp= compare_order_elements(elem1, spec_number1, + elem2, spec_number2))) return cmp; } if (elem1) @@ -566,7 +602,9 @@ int compare_window_spec_joined_lists(Window_spec *win_spec1, win_spec1->join_partition_and_order_lists(); win_spec2->join_partition_and_order_lists(); int cmp= compare_order_lists(win_spec1->partition_list, - win_spec2->partition_list); + win_spec1->win_spec_number, + win_spec2->partition_list, + win_spec2->win_spec_number); win_spec1->disjoin_partition_and_order_lists(); win_spec2->disjoin_partition_and_order_lists(); return cmp; @@ -584,7 +622,9 @@ int compare_window_funcs_by_window_specs(Item_window_func *win_func1, if (win_spec1 == win_spec2) return CMP_EQ; cmp= compare_order_lists(win_spec1->partition_list, - win_spec2->partition_list); + win_spec1->win_spec_number, + win_spec2->partition_list, + win_spec2->win_spec_number); if (cmp == CMP_EQ) { /* @@ -603,7 +643,9 @@ int compare_window_funcs_by_window_specs(Item_window_func *win_func1, } cmp= compare_order_lists(win_spec1->order_list, - win_spec2->order_list); + win_spec1->win_spec_number, + win_spec2->order_list, + win_spec2->win_spec_number); if (cmp != CMP_EQ) return cmp; @@ -700,7 +742,9 @@ void order_window_funcs_by_window_specs(List<Item_window_func> *win_func_list) int cmp; if (win_spec_prev->partition_list == win_spec_curr->partition_list) cmp= compare_order_lists(win_spec_prev->order_list, - win_spec_curr->order_list); + win_spec_prev->win_spec_number, + win_spec_curr->order_list, + win_spec_curr->win_spec_number); else cmp= compare_window_spec_joined_lists(win_spec_prev, win_spec_curr); if (!(CMP_LT_C <= cmp && cmp <= CMP_GT_C)) diff --git a/sql/sql_window.h b/sql/sql_window.h index 5e76a33dcd0..66ea8c7dd4d 100644 --- a/sql/sql_window.h +++ b/sql/sql_window.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, 2017 MariaDB + Copyright (c) 2016, 2022 MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -120,10 +120,15 @@ class Window_spec : public Sql_alloc Window_spec *referenced_win_spec; - Window_spec(LEX_CSTRING *win_ref, - SQL_I_List<ORDER> *part_list, - SQL_I_List<ORDER> *ord_list, - Window_frame *win_frame) + /* + Window_spec objects are numbered by the number of their appearance in the + query. This is used by compare_order_elements() to provide a predictable + ordering of PARTITION/ORDER BY clauses. + */ + int win_spec_number; + + Window_spec(LEX_CSTRING *win_ref, SQL_I_List<ORDER> *part_list, + SQL_I_List<ORDER> *ord_list, Window_frame *win_frame) : window_names_are_checked(false), window_ref(win_ref), partition_list(part_list), save_partition_list(NULL), order_list(ord_list), save_order_list(NULL), diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index cb0d8fff50a..e2e554f6d82 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1,6 +1,6 @@ /* Copyright (c) 2000, 2015, Oracle and/or its affiliates. - Copyright (c) 2010, 2021, MariaDB + Copyright (c) 2010, 2022, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -834,7 +834,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); /* We should not introduce any further shift/reduce conflicts. */ -%expect 68 +%expect 72 /* Comments for TOKENS. @@ -947,6 +947,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %token FIRST_VALUE_SYM /* SQL-2011 */ %token FLOAT_NUM %token FLOAT_SYM /* SQL-2003-R */ +%token FORCE_LOOKAHEAD /* INTERNAL never returned by the lexer */ %token FOREIGN /* SQL-2003-R */ %token FOR_SYM /* SQL-2003-R */ %token FOR_SYSTEM_TIME_SYM /* INTERNAL */ @@ -3057,6 +3058,9 @@ sequence_def: } ; +/* this rule is used to force look-ahead in the parser */ +force_lookahead: {} | FORCE_LOOKAHEAD {} ; + server_def: SERVER_SYM opt_if_not_exists ident_or_text { @@ -3258,7 +3262,7 @@ ev_sql_stmt: lex->sphead->set_body_start(thd, lip->get_cpp_ptr()); } - sp_proc_stmt + sp_proc_stmt force_lookahead { /* return back to the original memory root ASAP */ if (Lex->sp_body_finalize_event(thd)) @@ -18062,8 +18066,8 @@ trigger_tail: lex->sphead->set_body_start(thd, lip->get_cpp_tok_start()); } - sp_proc_stmt /* $19 */ - { /* $20 */ + sp_proc_stmt /* $19 */ force_lookahead /* $20 */ + { /* $21 */ LEX *lex= Lex; lex->sql_command= SQLCOM_CREATE_TRIGGER; @@ -18113,7 +18117,7 @@ sf_c_chistics_and_body: lex->sphead->set_c_chistics(lex->sp_chistics); lex->sphead->set_body_start(thd, YYLIP->get_cpp_tok_start()); } - sp_proc_stmt_in_returns_clause + sp_proc_stmt_in_returns_clause force_lookahead { if (unlikely(Lex->sp_body_finalize_function(thd))) MYSQL_YYABORT; @@ -18135,7 +18139,7 @@ sp_tail: Lex->sphead->set_c_chistics(Lex->sp_chistics); Lex->sphead->set_body_start(thd, YYLIP->get_cpp_tok_start()); } - sp_proc_stmt + sp_proc_stmt force_lookahead { if (unlikely(Lex->sp_body_finalize_procedure(thd))) MYSQL_YYABORT; diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 2b22d585a94..c079598a169 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -310,7 +310,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); /* We should not introduce any further shift/reduce conflicts. */ -%expect 70 +%expect 73 /* Comments for TOKENS. @@ -423,6 +423,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %token FIRST_VALUE_SYM /* SQL-2011 */ %token FLOAT_NUM %token FLOAT_SYM /* SQL-2003-R */ +%token FORCE_LOOKAHEAD /* INTERNAL never returned by the lexer */ %token FOREIGN /* SQL-2003-R */ %token FOR_SYM /* SQL-2003-R */ %token FOR_SYSTEM_TIME_SYM /* INTERNAL */ @@ -2832,6 +2833,9 @@ sequence_def: } ; +/* this rule is used to force look-ahead in the parser */ +force_lookahead: {} | FORCE_LOOKAHEAD {} ; + server_def: SERVER_SYM opt_if_not_exists ident_or_text { @@ -3033,7 +3037,7 @@ ev_sql_stmt: lex->sphead->set_body_start(thd, lip->get_cpp_ptr()); } - sp_proc_stmt + sp_proc_stmt force_lookahead { /* return back to the original memory root ASAP */ if (Lex->sp_body_finalize_event(thd)) @@ -18286,8 +18290,8 @@ trigger_tail: lex->sphead->set_body_start(thd, lip->get_cpp_tok_start()); } - sp_proc_stmt /* $19 */ - { /* $20 */ + sp_proc_stmt /* $19 */ force_lookahead /* $20 */ + { /* $21 */ LEX *lex= Lex; lex->sql_command= SQLCOM_CREATE_TRIGGER; @@ -18336,7 +18340,7 @@ sf_c_chistics_and_body_standalone: lex->sphead->set_body_start(thd, YYLIP->get_cpp_tok_start()); } sp_tail_is - sp_body + sp_body force_lookahead { if (unlikely(Lex->sp_body_finalize_function(thd))) MYSQL_YYABORT; diff --git a/sql/table.cc b/sql/table.cc index 6aa2dc872ad..00b498fdd77 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -50,17 +50,6 @@ #define MYSQL57_GENERATED_FIELD 128 #define MYSQL57_GCOL_HEADER_SIZE 4 -class Table_arena: public Query_arena -{ -public: - Table_arena(MEM_ROOT *mem_root, enum enum_state state_arg) : - Query_arena(mem_root, state_arg){} - virtual Type type() const - { - return TABLE_ARENA; - } -}; - struct extra2_fields { LEX_CUSTRING version; @@ -1139,8 +1128,8 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table, We need to use CONVENTIONAL_EXECUTION here to ensure that any new items created by fix_fields() are not reverted. */ - table->expr_arena= new (alloc_root(mem_root, sizeof(Table_arena))) - Table_arena(mem_root, + table->expr_arena= new (alloc_root(mem_root, sizeof(Query_arena))) + Query_arena(mem_root, Query_arena::STMT_CONVENTIONAL_EXECUTION); if (!table->expr_arena) DBUG_RETURN(1); @@ -3352,8 +3341,7 @@ class Vcol_expr_context bool inited; THD *thd; TABLE *table; - LEX *old_lex; - LEX lex; + Query_arena backup_arena; table_map old_map; Security_context *save_security_ctx; sql_mode_t save_sql_mode; @@ -3363,7 +3351,6 @@ public: inited(false), thd(_thd), table(_table), - old_lex(thd->lex), old_map(table->map), save_security_ctx(thd->security_ctx), save_sql_mode(thd->variables.sql_mode) {} @@ -3375,18 +3362,6 @@ public: bool Vcol_expr_context::init() { - /* - As this is vcol expression we must narrow down name resolution to - single table. - */ - if (init_lex_with_single_table(thd, table, &lex)) - { - my_error(ER_OUT_OF_RESOURCES, MYF(0)); - table->map= old_map; - return true; - } - - lex.sql_command= old_lex->sql_command; thd->variables.sql_mode= 0; TABLE_LIST const *tl= table->pos_in_table_list; @@ -3395,6 +3370,8 @@ bool Vcol_expr_context::init() if (table->pos_in_table_list->security_ctx) thd->security_ctx= tl->security_ctx; + thd->set_n_backup_active_arena(table->expr_arena, &backup_arena); + inited= true; return false; } @@ -3403,9 +3380,9 @@ Vcol_expr_context::~Vcol_expr_context() { if (!inited) return; - end_lex_with_single_table(thd, table, old_lex); table->map= old_map; thd->security_ctx= save_security_ctx; + thd->restore_active_arena(table->expr_arena, &backup_arena); thd->variables.sql_mode= save_sql_mode; } @@ -3756,6 +3733,7 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, outparam->s= share; outparam->db_stat= db_stat; outparam->write_row_record= NULL; + outparam->status= STATUS_NO_RECORD; if (share->incompatible_version && !(ha_open_flags & (HA_OPEN_FOR_ALTER | HA_OPEN_FOR_REPAIR))) @@ -6642,7 +6620,7 @@ Item *Field_iterator_view::create_item(THD *thd) Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref, LEX_CSTRING *name) { - bool save_wrapper= thd->lex->first_select_lex()->no_wrap_view_item; + bool save_wrapper= thd->lex->current_select->no_wrap_view_item; Item *field= *field_ref; DBUG_ENTER("create_view_field"); diff --git a/sql/tztime.cc b/sql/tztime.cc index 80035a5b906..7319084c28b 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -2747,9 +2747,9 @@ main(int argc, char **argv) sql_log_bin and wsrep_on to avoid Galera replicating below TRUNCATE TABLE clauses. This will allow user to set different time zones to nodes in Galera cluster. */ - printf("set @prep1=if((select count(*) from information_schema.global_variables where variable_name='wsrep_on' and variable_value='ON'), 'SET SESSION SQL_LOG_BIN=?, WSREP_ON=OFF;', 'do ?');\n" - "prepare set_wsrep_write_binlog from @prep1;\n" - "set @toggle=0; execute set_wsrep_write_binlog using @toggle;\n" + printf("set @prep1=if((select count(*) from information_schema.global_variables where variable_name='wsrep_on' and variable_value='ON'), 'SET SESSION WSREP_ON=OFF', 'do 0');\n" + "SET SESSION SQL_LOG_BIN=0;\n" + "execute immediate @prep1;\n" "%s%s", trunc_tables, lock_tables); else // Alter time zone tables to InnoDB if wsrep_on is enabled |