diff options
Diffstat (limited to 'sql')
132 files changed, 4262 insertions, 2122 deletions
diff --git a/sql/compat56.h b/sql/compat56.h index 347d6145048..65cd36dacfd 100644 --- a/sql/compat56.h +++ b/sql/compat56.h @@ -30,8 +30,8 @@ #define MY_PACKED_TIME_GET_INT_PART(x) ((x) >> 24) #define MY_PACKED_TIME_GET_FRAC_PART(x) ((x) % (1LL << 24)) -#define MY_PACKED_TIME_MAKE(i, f) ((((longlong) (i)) << 24) + (f)) -#define MY_PACKED_TIME_MAKE_INT(i) ((((longlong) (i)) << 24)) +#define MY_PACKED_TIME_MAKE(i, f) ((((ulonglong) (i)) << 24) + (f)) +#define MY_PACKED_TIME_MAKE_INT(i) ((((ulonglong) (i)) << 24)) longlong TIME_to_longlong_datetime_packed(const MYSQL_TIME *); longlong TIME_to_longlong_time_packed(const MYSQL_TIME *); diff --git a/sql/contributors.h b/sql/contributors.h index 34f06087c8c..e16448ee985 100644 --- a/sql/contributors.h +++ b/sql/contributors.h @@ -37,22 +37,17 @@ struct show_table_contributors_st { struct show_table_contributors_st show_table_contributors[]= { /* MariaDB foundation sponsors, in contribution, size , time order */ - {"Booking.com", "https://www.booking.com", "Founding member, Platinum Sponsor of the MariaDB Foundation"}, {"Alibaba Cloud", "https://www.alibabacloud.com/", "Platinum Sponsor of the MariaDB Foundation"}, {"Tencent Cloud", "https://cloud.tencent.com", "Platinum Sponsor of the MariaDB Foundation"}, {"Microsoft", "https://microsoft.com/", "Platinum Sponsor of the MariaDB Foundation"}, {"MariaDB Corporation", "https://mariadb.com", "Founding member, Platinum Sponsor of the MariaDB Foundation"}, + {"ServiceNow", "https://servicenow.com", "Platinum Sponsor of the MariaDB Foundation"}, {"Visma", "https://visma.com", "Gold Sponsor of the MariaDB Foundation"}, {"DBS", "https://dbs.com", "Gold Sponsor of the MariaDB Foundation"}, {"IBM", "https://www.ibm.com", "Gold Sponsor of the MariaDB Foundation"}, - {"Tencent Games", "http://game.qq.com/", "Gold Sponsor of the MariaDB Foundation"}, - {"Nexedi", "https://www.nexedi.com", "Silver Sponsor of the MariaDB Foundation"}, - {"Acronis", "https://www.acronis.com", "Silver Sponsor of the MariaDB Foundation"}, - {"Verkkokauppa.com", "https://www.verkkokauppa.com", "Bronze Sponsor of the MariaDB Foundation"}, - {"Virtuozzo", "https://virtuozzo.com", "Bronze Sponsor of the MariaDB Foundation"}, - {"Tencent Game DBA", "http://tencentdba.com/about", "Bronze Sponsor of the MariaDB Foundation"}, - {"Tencent TDSQL", "http://tdsql.org", "Bronze Sponsor of the MariaDB Foundation"}, - {"Percona", "https://www.percona.com/", "Bronze Sponsor of the MariaDB Foundation"}, + {"Automattic", "https://automattic.com", "Silver Sponsor of the MariaDB Foundation"}, + {"Percona", "https://www.percona.com/", "Sponsor of the MariaDB Foundation"}, + {"Galera Cluster", "https://galeracluster.com", "Sponsor of the MariaDB Foundation"}, /* Sponsors of important features */ {"Google", "USA", "Sponsoring encryption, parallel replication and GTID"}, diff --git a/sql/create_options.cc b/sql/create_options.cc index a8d997efaf4..066adcd92e3 100644 --- a/sql/create_options.cc +++ b/sql/create_options.cc @@ -98,14 +98,13 @@ static bool report_unknown_option(THD *thd, engine_option_value *val, { DBUG_ENTER("report_unknown_option"); - if (val->parsed || suppress_warning) + if (val->parsed || suppress_warning || thd->slave_thread) { DBUG_PRINT("info", ("parsed => exiting")); DBUG_RETURN(FALSE); } - if (!(thd->variables.sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS) && - !thd->slave_thread) + if (!(thd->variables.sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS)) { my_error(ER_UNKNOWN_OPTION, MYF(0), val->name.str); DBUG_RETURN(TRUE); diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 70faeee6039..bc6d4587064 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -180,6 +180,7 @@ Event_queue_element_for_exec::init(const LEX_CSTRING *db, const LEX_CSTRING *n) if (!(name.str= my_strndup(n->str, name.length= n->length, MYF(MY_WME)))) { my_free(const_cast<char*>(dbname.str)); + dbname.str= NULL; return TRUE; } return FALSE; diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h index e5e3e4eb087..eb4f8590512 100644 --- a/sql/event_data_objects.h +++ b/sql/event_data_objects.h @@ -33,7 +33,7 @@ struct TABLE; class Event_queue_element_for_exec { public: - Event_queue_element_for_exec(){}; + Event_queue_element_for_exec() : dbname{nullptr, 0}, name{nullptr, 0} {} ~Event_queue_element_for_exec(); bool diff --git a/sql/event_queue.cc b/sql/event_queue.cc index 91c243b3f70..96a5e93cfe6 100644 --- a/sql/event_queue.cc +++ b/sql/event_queue.cc @@ -639,6 +639,7 @@ Event_queue::get_top_for_execution_if_time(THD *thd, if (!(*event_name= new Event_queue_element_for_exec()) || (*event_name)->init(&top->dbname, &top->name)) { + delete *event_name; ret= TRUE; break; } diff --git a/sql/events.cc b/sql/events.cc index 195c0fa09e2..5e15b92dc49 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -658,8 +658,16 @@ Events::drop_schema_events(THD *thd, const char *db) */ if (event_queue) event_queue->drop_schema_events(thd, &db_lex); - db_repository->drop_schema_events(thd, &db_lex); - + if (db_repository) + db_repository->drop_schema_events(thd, &db_lex); + else + { + if ((db_repository= new Event_db_repository)) + { + db_repository->drop_schema_events(thd, &db_lex); + delete db_repository; + } + } DBUG_VOID_RETURN; } diff --git a/sql/field.cc b/sql/field.cc index 5a694b50fe0..89c51288de8 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -8098,11 +8098,10 @@ uint Field_varstring::get_key_image(uchar *buff, uint length, { String val; uint local_char_length; - my_bitmap_map *old_map; - old_map= dbug_tmp_use_all_columns(table, table->read_set); + MY_BITMAP *old_map= dbug_tmp_use_all_columns(table, &table->read_set); val_str(&val, &val); - dbug_tmp_restore_column_map(table->read_set, old_map); + dbug_tmp_restore_column_map(&table->read_set, old_map); local_char_length= val.charpos(length / field_charset->mbmaxlen); if (local_char_length < val.length()) @@ -8494,7 +8493,7 @@ int Field_blob::store(const char *from,size_t length,CHARSET_INFO *cs) DBUG_ASSERT(length <= max_data_length()); new_length= length; - copy_length= (size_t)MY_MIN(UINT_MAX,table->in_use->variables.group_concat_max_len); + copy_length= table->in_use->variables.group_concat_max_len; if (new_length > copy_length) { new_length= Well_formed_prefix(cs, @@ -11077,16 +11076,26 @@ Column_definition::Column_definition(THD *thd, Field *old_field, CREATE TABLE t1 (a INT) AS SELECT a FROM t2; See Type_handler::Column_definition_redefine_stage1() for data type specific code. + + @param this - The field definition corresponding to the expression + in the "AS SELECT.." part. + + @param dup_field - The field definition from the "CREATE TABLE (...)" part. + It has already underwent prepare_stage1(), so + must be fully initialized: + -- dup_field->charset is set and BINARY + sorting style is applied, see find_bin_collation(). + + @param file - The table handler */ void Column_definition::redefine_stage1_common(const Column_definition *dup_field, - const handler *file, - const Schema_specification_st *schema) + const handler *file) { set_handler(dup_field->type_handler()); default_value= dup_field->default_value; - charset= dup_field->charset ? dup_field->charset : - schema->default_table_charset; + DBUG_ASSERT(dup_field->charset); // Set by prepare_stage1() + charset= dup_field->charset; length= dup_field->char_length; pack_length= dup_field->pack_length; key_length= dup_field->key_length; @@ -11396,7 +11405,7 @@ key_map Field::get_possible_keys() bool Field::validate_value_in_record_with_warn(THD *thd, const uchar *record) { - my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); + MY_BITMAP *old_map= dbug_tmp_use_all_columns(table, &table->read_set); bool rc; if ((rc= validate_value_in_record(thd, record))) { @@ -11408,7 +11417,7 @@ bool Field::validate_value_in_record_with_warn(THD *thd, const uchar *record) ER_THD(thd, ER_INVALID_DEFAULT_VALUE_FOR_FIELD), ErrConvString(&tmp).ptr(), field_name.str); } - dbug_tmp_restore_column_map(table->read_set, old_map); + dbug_tmp_restore_column_map(&table->read_set, old_map); return rc; } diff --git a/sql/field.h b/sql/field.h index a512d74b444..421d6bddb2d 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1394,8 +1394,6 @@ public: virtual uint max_packed_col_length(uint max_length) { return max_length;} - virtual bool is_packable() const { return false; } - uint offset(const uchar *record) const { return (uint) (ptr - record); @@ -1872,7 +1870,7 @@ public: uchar null_bit_arg, utype unireg_check_arg, const LEX_CSTRING *field_name_arg, const DTCollation &collation); - uint decimals() const { return NOT_FIXED_DEC; } + uint decimals() const { return is_created_from_null_item ? 0 : NOT_FIXED_DEC; } int save_in_field(Field *to) { return save_in_field_str(to); } bool memcpy_field_possible(const Field *from) const { @@ -1988,7 +1986,6 @@ public: bool can_optimize_range(const Item_bool_func *cond, const Item *item, bool is_eq_func) const; - bool is_packable() const { return true; } }; /* base class for float and double and decimal (old one) */ @@ -4537,7 +4534,13 @@ public: void move_field_offset(my_ptrdiff_t ptr_diff) { Field::move_field_offset(ptr_diff); - bit_ptr= ADD_TO_PTR(bit_ptr, ptr_diff, uchar*); + + /* + clang does not like when things are added to a null pointer, even if + it is never referenced. + */ + if (bit_ptr) + bit_ptr= ADD_TO_PTR(bit_ptr, ptr_diff, uchar*); } void hash(ulong *nr, ulong *nr2); @@ -4634,6 +4637,11 @@ public: void frm_pack_charset(uchar *buff) const; void frm_unpack_basic(const uchar *buff); bool frm_unpack_charset(TABLE_SHARE *share, const uchar *buff); + CHARSET_INFO *explicit_or_derived_charset(const Column_derived_attributes + *derived_attr) const + { + return charset ? charset : derived_attr->charset(); + } }; @@ -4768,6 +4776,15 @@ public: void create_length_to_internal_length_bit(); void create_length_to_internal_length_newdecimal(); + /* + Prepare the "charset" member for string data types, + such as CHAR, VARCHAR, TEXT, ENUM, SET: + - derive the charset if not specified explicitly + - find a _bin collation if the BINARY comparison style was specified, e.g.: + CREATE TABLE t1 (a VARCHAR(10) BINARY) CHARSET utf8; + */ + bool prepare_charset_for_string(const Column_derived_attributes *dattr); + /** Prepare a SET/ENUM field. Create "interval" from "interval_list" if needed, and adjust "length". @@ -4803,7 +4820,13 @@ public: bool sp_prepare_create_field(THD *thd, MEM_ROOT *mem_root); bool prepare_stage1(THD *thd, MEM_ROOT *mem_root, - handler *file, ulonglong table_flags); + handler *file, ulonglong table_flags, + const Column_derived_attributes *derived_attr); + void prepare_stage1_simple(CHARSET_INFO *cs) + { + charset= cs; + create_length_to_internal_length_simple(); + } bool prepare_stage1_typelib(THD *thd, MEM_ROOT *mem_root, handler *file, ulonglong table_flags); bool prepare_stage1_string(THD *thd, MEM_ROOT *mem_root, @@ -4811,15 +4834,19 @@ public: bool prepare_stage1_bit(THD *thd, MEM_ROOT *mem_root, handler *file, ulonglong table_flags); + bool bulk_alter(const Column_derived_attributes *derived_attr, + const Column_bulk_alter_attributes *bulk_attr) + { + return type_handler()->Column_definition_bulk_alter(this, + derived_attr, + bulk_attr); + } void redefine_stage1_common(const Column_definition *dup_field, - const handler *file, - const Schema_specification_st *schema); - bool redefine_stage1(const Column_definition *dup_field, const handler *file, - const Schema_specification_st *schema) + const handler *file); + bool redefine_stage1(const Column_definition *dup_field, const handler *file) { const Type_handler *handler= dup_field->type_handler(); - return handler->Column_definition_redefine_stage1(this, dup_field, - file, schema); + return handler->Column_definition_redefine_stage1(this, dup_field, file); } bool prepare_stage2(handler *handler, ulonglong table_flags); bool prepare_stage2_blob(handler *handler, diff --git a/sql/filesort.cc b/sql/filesort.cc index ad4cb2b6e6b..aa25474be1a 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -1978,14 +1978,7 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length, if (sortorder->field) { CHARSET_INFO *cs= sortorder->field->sort_charset(); - sortorder->type= sortorder->field->is_packable() ? - SORT_FIELD_ATTR::VARIABLE_SIZE : - SORT_FIELD_ATTR::FIXED_SIZE; - sortorder->length= sortorder->field->sort_length(); - if (sortorder->is_variable_sized()) - set_if_smaller(sortorder->length, thd->variables.max_sort_length); - if (use_strnxfrm((cs=sortorder->field->sort_charset()))) { *multi_byte_charset= true; @@ -1996,10 +1989,6 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length, } else { - sortorder->type= sortorder->item->type_handler()->is_packable() ? - SORT_FIELD_ATTR::VARIABLE_SIZE : - SORT_FIELD_ATTR::FIXED_SIZE; - sortorder->item->type_handler()->sortlength(thd, sortorder->item, sortorder); if (use_strnxfrm(sortorder->item->collation.collation)) @@ -2009,8 +1998,7 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length, if (sortorder->item->maybe_null) length++; // Place for NULL marker } - if (sortorder->is_variable_sized()) - set_if_smaller(sortorder->length, thd->variables.max_sort_length); + set_if_smaller(sortorder->length, thd->variables.max_sort_length); length+=sortorder->length; } sortorder->field= (Field*) 0; // end marker diff --git a/sql/gcalc_tools.h b/sql/gcalc_tools.h index 77da791f0b9..e625b355d95 100644 --- a/sql/gcalc_tools.h +++ b/sql/gcalc_tools.h @@ -184,7 +184,11 @@ class Gcalc_result_receiver double first_x, first_y, prev_x, prev_y; double shape_area; public: - Gcalc_result_receiver() : collection_result(FALSE), n_shapes(0), n_holes(0) +Gcalc_result_receiver() : + n_points(0), + common_shapetype(Gcalc_function::shape_point), + collection_result(FALSE), n_shapes(0), n_holes(0), + cur_shape(Gcalc_function::shape_point), shape_pos(0) {} int start_shape(Gcalc_function::shape_type shape); int add_point(double x, double y); diff --git a/sql/gen_win_tzname_data.ps1 b/sql/gen_win_tzname_data.ps1 index c0a37d21895..474ab889d25 100644 --- a/sql/gen_win_tzname_data.ps1 +++ b/sql/gen_win_tzname_data.ps1 @@ -4,6 +4,7 @@ write-output "/* This file was generated using gen_win_tzname_data.ps1 */" $xdoc = new-object System.Xml.XmlDocument +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $xdoc.load("https://raw.githubusercontent.com/unicode-org/cldr/master/common/supplemental/windowsZones.xml") $nodes = $xdoc.SelectNodes("//mapZone[@territory='001']") # use default territory (001) foreach ($node in $nodes) { diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 609e2135a99..24e08501bed 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2005, 2019, Oracle and/or its affiliates. - Copyright (c) 2009, 2020, MariaDB + Copyright (c) 2009, 2021, 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 @@ -4282,7 +4282,7 @@ int ha_partition::write_row(const uchar * buf) int error; longlong func_value; bool have_auto_increment= table->next_number_field && buf == table->record[0]; - my_bitmap_map *old_map; + MY_BITMAP *old_map; THD *thd= ha_thd(); sql_mode_t saved_sql_mode= thd->variables.sql_mode; bool saved_auto_inc_field_not_null= table->auto_increment_field_not_null; @@ -4324,9 +4324,9 @@ int ha_partition::write_row(const uchar * buf) } } - old_map= dbug_tmp_use_all_columns(table, table->read_set); + old_map= dbug_tmp_use_all_columns(table, &table->read_set); error= m_part_info->get_partition_id(m_part_info, &part_id, &func_value); - dbug_tmp_restore_column_map(table->read_set, old_map); + dbug_tmp_restore_column_map(&table->read_set, old_map); if (unlikely(error)) { m_part_info->err_value= func_value; @@ -6461,6 +6461,7 @@ int ha_partition::multi_range_read_init(RANGE_SEQ_IF *seq, DBUG_ENTER("ha_partition::multi_range_read_init"); DBUG_PRINT("enter", ("partition this: %p", this)); + eq_range= 0; m_seq_if= seq; m_seq= seq->init(seq_init_param, n_ranges, mrr_mode); if (unlikely((error= multi_range_key_create_key(seq, m_seq)))) @@ -9060,7 +9061,6 @@ int ha_partition::extra(enum ha_extra_function operation) case HA_EXTRA_STARTING_ORDERED_INDEX_SCAN: case HA_EXTRA_BEGIN_ALTER_COPY: case HA_EXTRA_END_ALTER_COPY: - case HA_EXTRA_FAKE_START_STMT: DBUG_RETURN(loop_partitions(extra_cb, &operation)); default: { @@ -11326,13 +11326,12 @@ int ha_partition::bulk_update_row(const uchar *old_data, const uchar *new_data, int error= 0; uint32 part_id; longlong func_value; - my_bitmap_map *old_map; DBUG_ENTER("ha_partition::bulk_update_row"); - old_map= dbug_tmp_use_all_columns(table, table->read_set); + MY_BITMAP *old_map= dbug_tmp_use_all_columns(table, &table->read_set); error= m_part_info->get_partition_id(m_part_info, &part_id, &func_value); - dbug_tmp_restore_column_map(table->read_set, old_map); + dbug_tmp_restore_column_map(&table->read_set, old_map); if (unlikely(error)) { m_part_info->err_value= func_value; diff --git a/sql/handler.cc b/sql/handler.cc index ed12830ce20..ac5b43249db 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -859,6 +859,7 @@ static my_bool kill_handlerton(THD *thd, plugin_ref plugin, { handlerton *hton= plugin_hton(plugin); + mysql_mutex_assert_owner(&thd->LOCK_thd_data); if (hton->state == SHOW_OPTION_YES && hton->kill_query && thd_get_ha_data(thd, hton)) hton->kill_query(hton, thd, *(enum thd_kill_levels *) level); @@ -3322,6 +3323,13 @@ int handler::update_auto_increment() (table->auto_increment_field_not_null && thd->variables.sql_mode & MODE_NO_AUTO_VALUE_ON_ZERO)) { + + /* + There could be an error reported because value was truncated + when strict mode is enabled. + */ + if (thd->is_error()) + DBUG_RETURN(HA_ERR_AUTOINC_ERANGE); /* Update next_insert_id if we had already generated a value in this statement (case of INSERT VALUES(null),(3763),(null): @@ -3335,25 +3343,27 @@ int handler::update_auto_increment() DBUG_RETURN(0); } - // ALTER TABLE ... ADD COLUMN ... AUTO_INCREMENT - if (thd->lex->sql_command == SQLCOM_ALTER_TABLE) + if (table->versioned()) { - if (table->versioned()) + Field *end= table->vers_end_field(); + DBUG_ASSERT(end); + bitmap_set_bit(table->read_set, end->field_index); + if (!end->is_max()) { - Field *end= table->vers_end_field(); - DBUG_ASSERT(end); - bitmap_set_bit(table->read_set, end->field_index); - if (!end->is_max()) + if (thd->lex->sql_command == SQLCOM_ALTER_TABLE) { if (!table->next_number_field->real_maybe_null()) DBUG_RETURN(HA_ERR_UNSUPPORTED); table->next_number_field->set_null(); - DBUG_RETURN(0); } + DBUG_RETURN(0); } - table->next_number_field->set_notnull(); } + // ALTER TABLE ... ADD COLUMN ... AUTO_INCREMENT + if (thd->lex->sql_command == SQLCOM_ALTER_TABLE) + table->next_number_field->set_notnull(); + if ((nr= next_insert_id) >= auto_inc_interval_for_cur_row.maximum()) { /* next_insert_id is beyond what is reserved, so we reserve more. */ @@ -5204,6 +5214,7 @@ int ha_create_table(THD *thd, const char *path, char name_buff[FN_REFLEN]; const char *name; TABLE_SHARE share; + Abort_on_warning_instant_set old_abort_on_warning(thd, 0); bool temp_table __attribute__((unused)) = create_info->options & (HA_LEX_CREATE_TMP_TABLE | HA_CREATE_TMP_ALTER); DBUG_ENTER("ha_create_table"); @@ -7605,6 +7616,11 @@ bool Vers_parse_info::fix_alter_info(THD *thd, Alter_info *alter_info, { if (f->flags & VERS_SYSTEM_FIELD) { + if (!table->versioned()) + { + my_error(ER_VERS_NOT_VERSIONED, MYF(0), table->s->table_name.str); + return true; + } my_error(ER_VERS_DUPLICATE_ROW_START_END, MYF(0), f->flags & VERS_SYS_START_FLAG ? "START" : "END", f->field_name.str); return true; diff --git a/sql/handler.h b/sql/handler.h index 0c8be2154a9..891187db171 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -2,7 +2,7 @@ #define HANDLER_INCLUDED /* Copyright (c) 2000, 2019, Oracle and/or its affiliates. - Copyright (c) 2009, 2020, MariaDB + Copyright (c) 2009, 2021, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -1507,7 +1507,7 @@ struct handlerton enum handler_create_iterator_result (*create_iterator)(handlerton *hton, enum handler_iterator_type type, struct handler_iterator *fill_this_in); - int (*abort_transaction)(handlerton *hton, THD *bf_thd, + void (*abort_transaction)(handlerton *hton, THD *bf_thd, THD *victim_thd, my_bool signal); int (*set_checkpoint)(handlerton *hton, const XID* xid); int (*get_checkpoint)(handlerton *hton, XID* xid); @@ -1725,6 +1725,12 @@ handlerton *ha_default_tmp_handlerton(THD *thd); /* can be replicated by wsrep replication provider plugin */ #define HTON_WSREP_REPLICATION (1 << 13) +/* + Table requires and close and reopen after truncate + If the handler has HTON_CAN_RECREATE, this flag is not used +*/ +#define HTON_REQUIRES_CLOSE_AFTER_TRUNCATE (1 << 18) + class Ha_trx_info; struct THD_TRANS @@ -2086,7 +2092,7 @@ public: struct Table_scope_and_contents_source_pod_st // For trivial members { - CHARSET_INFO *table_charset; + CHARSET_INFO *alter_table_convert_to_charset; LEX_CUSTRING tabledef_version; LEX_CSTRING connect_string; LEX_CSTRING comment; @@ -2231,7 +2237,7 @@ struct HA_CREATE_INFO: public Table_scope_and_contents_source_st, DBUG_ASSERT(cs); if (check_conflicting_charset_declarations(cs)) return true; - table_charset= default_table_charset= cs; + alter_table_convert_to_charset= default_table_charset= cs; used_fields|= (HA_CREATE_USED_CHARSET | HA_CREATE_USED_DEFAULT_CHARSET); return false; } @@ -3191,7 +3197,7 @@ public: { cached_table_flags= table_flags(); } - /* ha_ methods: pubilc wrappers for private virtual API */ + /* ha_ methods: public wrappers for private virtual API */ int ha_open(TABLE *table, const char *name, int mode, uint test_if_locked, MEM_ROOT *mem_root= 0, List<String> *partitions_to_open=NULL); diff --git a/sql/item.cc b/sql/item.cc index f700bcfe680..6c52ade0d9f 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2000, 2018, Oracle and/or its affiliates. - Copyright (c) 2010, 2020, MariaDB Corporation. + Copyright (c) 2010, 2021, 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 @@ -1423,9 +1423,9 @@ int Item::save_in_field_no_warnings(Field *field, bool no_conversions) Sql_mode_save sql_mode(thd); thd->variables.sql_mode&= ~(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE); thd->variables.sql_mode|= MODE_INVALID_DATES; - my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set); + MY_BITMAP *old_map= dbug_tmp_use_all_columns(table, &table->write_set); res= save_in_field(field, no_conversions); - dbug_tmp_restore_column_map(table->write_set, old_map); + dbug_tmp_restore_column_map(&table->write_set, old_map); return res; } @@ -2599,9 +2599,7 @@ Item_sp::Item_sp(THD *thd, Name_resolution_context *context_arg, dummy_table= (TABLE*) thd->calloc(sizeof(TABLE) + sizeof(TABLE_SHARE) + sizeof(Query_arena)); dummy_table->s= (TABLE_SHARE*) (dummy_table + 1); - /* TODO(cvicentiu) Move this sp_query_arena in the class as a direct member. - Currently it can not be done due to header include dependencies. */ - sp_query_arena= (Query_arena *) (dummy_table->s + 1); + sp_query_arena= new(dummy_table->s + 1) Query_arena(); memset(&sp_mem_root, 0, sizeof(sp_mem_root)); } @@ -2612,7 +2610,7 @@ Item_sp::Item_sp(THD *thd, Item_sp *item): dummy_table= (TABLE*) thd->calloc(sizeof(TABLE)+ sizeof(TABLE_SHARE) + sizeof(Query_arena)); dummy_table->s= (TABLE_SHARE*) (dummy_table+1); - sp_query_arena= (Query_arena *) (dummy_table->s + 1); + sp_query_arena= new(dummy_table->s + 1) Query_arena(); memset(&sp_mem_root, 0, sizeof(sp_mem_root)); } @@ -4978,13 +4976,19 @@ bool Item_ref_null_helper::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuz @param resolved_item item which was resolved in outer SELECT(for warning) @param mark_item item which should be marked (can be differ in case of substitution) + @param suppress_warning_output flag specifying whether to suppress output of + a warning message */ static bool mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, Item_ident *resolved_item, - Item_ident *mark_item) + Item_ident *mark_item, + bool suppress_warning_output) { DBUG_ENTER("mark_as_dependent"); + DBUG_PRINT("info", ("current select: %d (%p) last: %d (%p)", + current->select_number, current, + (last ? last->select_number : 0), last)); /* store pointer on SELECT_LEX from which item is dependent */ if (mark_item && mark_item->can_be_depended) @@ -4995,7 +4999,7 @@ static bool mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, if (current->mark_as_dependent(thd, last, /** resolved_item psergey-thu **/ mark_item)) DBUG_RETURN(TRUE); - if (thd->lex->describe & DESCRIBE_EXTENDED) + if ((thd->lex->describe & DESCRIBE_EXTENDED) && !suppress_warning_output) { const char *db_name= (resolved_item->db_name ? resolved_item->db_name : ""); @@ -5024,6 +5028,8 @@ static bool mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, @param found_item Item which was found during resolving (if resolved identifier belongs to VIEW) @param resolved_item Identifier which was resolved + @param suppress_warning_output flag specifying whether to suppress output of + a warning message @note We have to mark all items between current_sel (including) and @@ -5037,7 +5043,8 @@ void mark_select_range_as_dependent(THD *thd, SELECT_LEX *last_select, SELECT_LEX *current_sel, Field *found_field, Item *found_item, - Item_ident *resolved_item) + Item_ident *resolved_item, + bool suppress_warning_output) { /* Go from current SELECT to SELECT where field was resolved (it @@ -5072,7 +5079,7 @@ void mark_select_range_as_dependent(THD *thd, found_field->table->map; prev_subselect_item->const_item_cache= 0; mark_as_dependent(thd, last_select, current_sel, resolved_item, - dependent); + dependent, suppress_warning_output); } } @@ -5422,8 +5429,9 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) Name_resolution_context *outer_context= 0; SELECT_LEX *select= 0; /* Currently derived tables cannot be correlated */ - if (current_sel->master_unit()->first_select()->get_linkage() != - DERIVED_TABLE_TYPE) + if ((current_sel->master_unit()->first_select()->get_linkage() != + DERIVED_TABLE_TYPE) && + current_sel->master_unit()->outer_select()) outer_context= context->outer_context; /* @@ -5538,7 +5546,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) context->select_lex, this, ((ref_type == REF_ITEM || ref_type == FIELD_ITEM) ? - (Item_ident*) (*reference) : 0)); + (Item_ident*) (*reference) : 0), false); return 0; } } @@ -5550,7 +5558,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) context->select_lex, this, ((ref_type == REF_ITEM || ref_type == FIELD_ITEM) ? (Item_ident*) (*reference) : - 0)); + 0), false); if (thd->lex->in_sum_func && thd->lex->in_sum_func->nest_level >= select->nest_level) { @@ -5664,7 +5672,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) set_max_sum_func_level(thd, select); mark_as_dependent(thd, last_checked_context->select_lex, context->select_lex, rf, - rf); + rf, false); return 0; } @@ -5677,7 +5685,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) set_max_sum_func_level(thd, select); mark_as_dependent(thd, last_checked_context->select_lex, context->select_lex, - this, (Item_ident*)*reference); + this, (Item_ident*)*reference, false); if (last_checked_context->select_lex->having_fix_field) { Item_ref *rf; @@ -5753,7 +5761,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) Field *from_field= (Field *)not_found_field; bool outer_fixed= false; SELECT_LEX *select= thd->lex->current_select; - + if (select && select->in_tvc) { my_error(ER_FIELD_REFERENCE_IN_TVC, MYF(0), full_name()); @@ -6198,12 +6206,14 @@ Item *Item_field::replace_equal_field(THD *thd, uchar *arg) item_equal->compare_type_handler()->cmp_type()); return const_item2; } - Item_field *subst= - (Item_field *)(item_equal->get_first(param->context_tab, this)); + Item_ident *subst= + (Item_ident *) (item_equal->get_first(param->context_tab, this)); if (subst) - subst= (Item_field *) (subst->real_item()); - if (subst && !field->eq(subst->field)) - return subst; + { + Item_field *subst2= (Item_field *) (subst->real_item()); + if (subst2 && !field->eq(subst2->field)) + return subst2; + } } return this; } @@ -6652,7 +6662,7 @@ Item *Item_string::make_odbc_literal(THD *thd, const LEX_CSTRING *typestr) } -static int save_int_value_in_field (Field *field, longlong nr, +static int save_int_value_in_field (Field *field, longlong nr, bool null_value, bool unsigned_flag) { if (null_value) @@ -7648,7 +7658,7 @@ public: if (tbl->table == item->field->table) { if (sel != current_select) - mark_as_dependent(thd, sel, current_select, item, item); + mark_as_dependent(thd, sel, current_select, item, item, false); return; } } @@ -7844,7 +7854,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) ((refer_type == REF_ITEM || refer_type == FIELD_ITEM) ? (Item_ident*) (*reference) : - 0)); + 0), false); /* view reference found, we substituted it instead of this Item, so can quit @@ -7894,7 +7904,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) goto error; thd->change_item_tree(reference, fld); mark_as_dependent(thd, last_checked_context->select_lex, - current_sel, fld, fld); + current_sel, fld, fld, false); /* A reference is resolved to a nest level that's outer or the same as the nest level of the enclosing set function : adjust the value of @@ -7917,7 +7927,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) /* Should be checked in resolve_ref_in_select_and_group(). */ DBUG_ASSERT(*ref && (*ref)->is_fixed()); mark_as_dependent(thd, last_checked_context->select_lex, - context->select_lex, this, this); + context->select_lex, this, this, false); /* A reference is resolved to a nest level that's outer or the same as the nest level of the enclosing set function : adjust the value of @@ -8427,6 +8437,22 @@ bool Item_direct_ref::val_native(THD *thd, Native *to) } +longlong Item_direct_ref::val_time_packed(THD *thd) +{ + longlong tmp = (*ref)->val_time_packed(thd); + null_value= (*ref)->null_value; + return tmp; +} + + +longlong Item_direct_ref::val_datetime_packed(THD *thd) +{ + longlong tmp = (*ref)->val_datetime_packed(thd); + null_value= (*ref)->null_value; + return tmp; +} + + Item_cache_wrapper::~Item_cache_wrapper() { DBUG_ASSERT(expr_cache == 0); @@ -9279,8 +9305,9 @@ bool Item_default_value::fix_fields(THD *thd, Item **items) memcpy((void *)def_field, (void *)field_arg->field, field_arg->field->size_of()); def_field->reset_fields(); - // If non-constant default value expression - if (def_field->default_value && def_field->default_value->flags) + // If non-constant default value expression or a blob + if (def_field->default_value && + (def_field->default_value->flags || (def_field->flags & BLOB_FLAG))) { uchar *newptr= (uchar*) thd->alloc(1+def_field->pack_length()); if (!newptr) @@ -9383,11 +9410,60 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions) return Item_field::save_in_field(field_arg, no_conversions); } +double Item_default_value::val_result() +{ + calculate(); + return Item_field::val_result(); +} + +longlong Item_default_value::val_int_result() +{ + calculate(); + return Item_field::val_int_result(); +} + +String *Item_default_value::str_result(String* tmp) +{ + calculate(); + return Item_field::str_result(tmp); +} + +bool Item_default_value::val_bool_result() +{ + calculate(); + return Item_field::val_bool_result(); +} + +bool Item_default_value::is_null_result() +{ + calculate(); + return Item_field::is_null_result(); +} + +my_decimal *Item_default_value::val_decimal_result(my_decimal *decimal_value) +{ + calculate(); + return Item_field::val_decimal_result(decimal_value); +} + +bool Item_default_value::get_date_result(THD *thd, MYSQL_TIME *ltime, + date_mode_t fuzzydate) +{ + calculate(); + return Item_field::get_date_result(thd, ltime, fuzzydate); +} + +bool Item_default_value::val_native_result(THD *thd, Native *to) +{ + calculate(); + return Item_field::val_native_result(thd, to); +} + table_map Item_default_value::used_tables() const { if (!field || !field->default_value) return static_cast<table_map>(0); - if (!field->default_value->expr) // not fully parsed field + if (!field->default_value->expr) // not fully parsed field return static_cast<table_map>(RAND_TABLE_BIT); return field->default_value->expr->used_tables(); } @@ -9763,7 +9839,7 @@ bool Item_cache_int::cache_value() return FALSE; value_cached= TRUE; value= example->val_int_result(); - null_value= example->null_value; + null_value_inside= null_value= example->null_value; unsigned_flag= example->unsigned_flag; return TRUE; } @@ -9840,7 +9916,7 @@ bool Item_cache_temporal::cache_value() return false; value_cached= true; value= example->val_datetime_packed_result(current_thd); - null_value= example->null_value; + null_value_inside= null_value= example->null_value; return true; } @@ -9851,7 +9927,7 @@ bool Item_cache_time::cache_value() return false; value_cached= true; value= example->val_time_packed_result(current_thd); - null_value= example->null_value; + null_value_inside= null_value= example->null_value; return true; } @@ -9993,7 +10069,7 @@ bool Item_cache_real::cache_value() return FALSE; value_cached= TRUE; value= example->val_result(); - null_value= example->null_value; + null_value_inside= null_value= example->null_value; return TRUE; } @@ -10060,7 +10136,8 @@ bool Item_cache_decimal::cache_value() return FALSE; value_cached= TRUE; my_decimal *val= example->val_decimal_result(&decimal_value); - if (!(null_value= example->null_value) && val != &decimal_value) + if (!(null_value_inside= null_value= example->null_value) && + val != &decimal_value) my_decimal2decimal(val, &decimal_value); return TRUE; } @@ -10109,11 +10186,14 @@ Item *Item_cache_decimal::convert_to_basic_const_item(THD *thd) bool Item_cache_str::cache_value() { if (!example) + { + DBUG_ASSERT(value_cached == FALSE); return FALSE; + } value_cached= TRUE; value_buff.set(buffer, sizeof(buffer), example->collation.collation); value= example->str_result(&value_buff); - if ((null_value= example->null_value)) + if ((null_value= null_value_inside= example->null_value)) value= 0; else if (value != &value_buff) { @@ -10208,6 +10288,8 @@ Item *Item_cache_str::convert_to_basic_const_item(THD *thd) bool Item_cache_row::setup(THD *thd, Item *item) { example= item; + null_value= true; + if (!values && allocate(thd, item->cols())) return 1; for (uint i= 0; i < item_count; i++) @@ -10240,12 +10322,19 @@ bool Item_cache_row::cache_value() if (!example) return FALSE; value_cached= TRUE; - null_value= 0; + null_value= TRUE; + null_value_inside= false; example->bring_value(); + + /* + For Item_cache_row null_value is set to TRUE only when ALL the values + inside the cache are NULL + */ for (uint i= 0; i < item_count; i++) { values[i]->cache_value(); - null_value|= values[i]->null_value; + null_value&= values[i]->null_value; + null_value_inside|= values[i]->null_value; } return TRUE; } diff --git a/sql/item.h b/sql/item.h index e2e18ce9b86..dbd2cb0b1df 100644 --- a/sql/item.h +++ b/sql/item.h @@ -2,7 +2,7 @@ #define SQL_ITEM_INCLUDED /* Copyright (c) 2000, 2017, Oracle and/or its affiliates. - Copyright (c) 2009, 2020, MariaDB Corporation. + Copyright (c) 2009, 2021, 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 @@ -1938,6 +1938,15 @@ public: return 0; } + /** + Check db/table_name if they defined in item and match arg values + + @param arg Pointer to Check_table_name_prm structure + + @retval true Match failed + @retval false Match succeeded + */ + virtual bool check_table_name_processor(void *arg) { return false; } /* TRUE if the expression depends only on the table indicated by tab_map or can be converted to such an exression using equalities. @@ -2096,6 +2105,15 @@ public: bool collect; }; + struct Check_table_name_prm + { + LEX_CSTRING db; + LEX_CSTRING table_name; + String field; + Check_table_name_prm(LEX_CSTRING _db, LEX_CSTRING _table_name) : + db(_db), table_name(_table_name) {} + }; + /* For SP local variable returns pointer to Item representing its current value and pointer to current Item otherwise. @@ -3500,6 +3518,24 @@ public: } return 0; } + bool check_table_name_processor(void *arg) + { + Check_table_name_prm &p= *(Check_table_name_prm *) arg; + if (p.table_name.length && table_name) + { + DBUG_ASSERT(p.db.length); + if ((db_name && + my_strcasecmp(table_alias_charset, p.db.str, db_name)) || + my_strcasecmp(table_alias_charset, p.table_name.str, table_name)) + { + print(&p.field, (enum_query_type) (QT_ITEM_ORIGINAL_FUNC_NULLIF | + QT_NO_DATA_EXPANSION | + QT_TO_SYSTEM_CHARSET)); + return true; + } + } + return false; + } void cleanup(); Item_equal *get_item_equal() { return item_equal; } void set_item_equal(Item_equal *item_eq) { item_equal= item_eq; } @@ -5569,14 +5605,17 @@ public: return Item_ref::fix_fields(thd, it); } void save_val(Field *to); + /* Below we should have all val() methods as in Item_ref */ double val_real(); longlong val_int(); - String *val_str(String* tmp); - bool val_native(THD *thd, Native *to); my_decimal *val_decimal(my_decimal *); bool val_bool(); + String *val_str(String* tmp); + bool val_native(THD *thd, Native *to); bool is_null(); bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + longlong val_datetime_packed(THD *); + longlong val_time_packed(THD *); virtual Ref_Type ref_type() { return DIRECT_REF; } Item *get_copy(THD *thd) { return get_item_copy<Item_direct_ref>(thd, this); } @@ -5905,6 +5944,20 @@ public: } return Item_direct_ref::get_date(thd, ltime, fuzzydate); } + longlong val_time_packed(THD *thd) + { + if (check_null_ref()) + return 0; + else + return Item_direct_ref::val_time_packed(thd); + } + longlong val_datetime_packed(THD *thd) + { + if (check_null_ref()) + return 0; + else + return Item_direct_ref::val_datetime_packed(thd); + } bool send(Protocol *protocol, st_value *buffer); void save_org_in_field(Field *field, fast_field_copier data __attribute__ ((__unused__))) @@ -6405,6 +6458,17 @@ public: my_decimal *val_decimal(my_decimal *decimal_value); 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); + + /* Result variants */ + double val_result(); + longlong val_int_result(); + String *str_result(String* tmp); + my_decimal *val_decimal_result(my_decimal *val); + bool val_bool_result(); + bool is_null_result(); + bool get_date_result(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + bool send(Protocol *protocol, st_value *buffer); int save_in_field(Field *field_arg, bool no_conversions); bool save_in_param(THD *thd, Item_param *param) @@ -6433,6 +6497,8 @@ public: } Item *transform(THD *thd, Item_transformer transformer, uchar *args); + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param); }; @@ -6721,6 +6787,14 @@ protected: table_map used_table_map; public: + /* + This is set if at least one of the values of a sub query is NULL + Item_cache_row returns this with null_inside(). + For not row items, it's set to the value of null_value + It is set after cache_value() is called. + */ + bool null_value_inside; + Item_cache(THD *thd): Item(thd), Type_handler_hybrid_field_type(&type_handler_string), @@ -6730,6 +6804,7 @@ public: { maybe_null= 1; null_value= 1; + null_value_inside= true; } protected: Item_cache(THD *thd, const Type_handler *handler): @@ -6741,6 +6816,7 @@ protected: { maybe_null= 1; null_value= 1; + null_value_inside= true; } public: @@ -7360,7 +7436,8 @@ void mark_select_range_as_dependent(THD *thd, st_select_lex *last_select, st_select_lex *current_sel, Field *found_field, Item *found_item, - Item_ident *resolved_item); + Item_ident *resolved_item, + bool suppress_warning_output); extern Cached_item *new_Cached_item(THD *thd, Item *item, bool pass_through_ref); diff --git a/sql/item_buff.cc b/sql/item_buff.cc index 9c96fdb1a9a..05cef6871be 100644 --- a/sql/item_buff.cc +++ b/sql/item_buff.cc @@ -47,9 +47,9 @@ Cached_item *new_Cached_item(THD *thd, Item *item, bool pass_through_ref) } switch (item->result_type()) { case STRING_RESULT: - return new Cached_item_str(thd, (Item_field *) item); + return new Cached_item_str(thd, item); case INT_RESULT: - return new Cached_item_int((Item_field *) item); + return new Cached_item_int(item); case REAL_RESULT: return new Cached_item_real(item); case DECIMAL_RESULT: diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index c43dbcaab78..687aa7e192b 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -321,13 +321,13 @@ static bool convert_const_to_int(THD *thd, Item_field *field_item, TABLE *table= field->table; Sql_mode_save sql_mode(thd); Check_level_instant_set check_level_save(thd, CHECK_FIELD_IGNORE); - my_bitmap_map *old_maps[2] = { NULL, NULL }; + MY_BITMAP *old_maps[2] = { NULL, NULL }; ulonglong UNINIT_VAR(orig_field_val); /* original field value if valid */ /* table->read_set may not be set if we come here from a CREATE TABLE */ if (table && table->read_set) dbug_tmp_use_all_columns(table, old_maps, - table->read_set, table->write_set); + &table->read_set, &table->write_set); /* For comparison purposes allow invalid dates like 2000-01-32 */ thd->variables.sql_mode= (thd->variables.sql_mode & ~MODE_NO_ZERO_DATE) | MODE_INVALID_DATES; @@ -368,7 +368,7 @@ static bool convert_const_to_int(THD *thd, Item_field *field_item, DBUG_ASSERT(!result); } if (table && table->read_set) - dbug_tmp_restore_column_maps(table->read_set, table->write_set, old_maps); + dbug_tmp_restore_column_maps(&table->read_set, &table->write_set, old_maps); } return result; } @@ -1198,9 +1198,9 @@ longlong Item_func_truth::val_int() bool Item_in_optimizer::is_top_level_item() { - if (invisible_mode()) - return FALSE; - return ((Item_in_subselect *)args[1])->is_top_level_item(); + if (!invisible_mode()) + return ((Item_in_subselect *)args[1])->is_top_level_item(); + return false; } @@ -1565,7 +1565,7 @@ longlong Item_in_optimizer::val_int() DBUG_RETURN(res); } - if (cache->null_value) + if (cache->null_value_inside) { DBUG_PRINT("info", ("Left NULL...")); /* @@ -3196,7 +3196,7 @@ bool Item_func_decode_oracle::fix_length_and_dec() /* Aggregate all THEN and ELSE expression types and collations when string result - + @param THD - current thd @param start - an element in args to start aggregating from */ @@ -5473,6 +5473,7 @@ void Item_func_like::print(String *str, enum_query_type query_type) longlong Item_func_like::val_int() { DBUG_ASSERT(fixed == 1); + DBUG_ASSERT(escape != -1); String* res= args[0]->val_str(&cmp_value1); if (args[0]->null_value) { @@ -5559,15 +5560,29 @@ bool fix_escape_item(THD *thd, Item *escape_item, String *tmp_str, bool escape_used_in_parsing, CHARSET_INFO *cmp_cs, int *escape) { - if (!escape_item->const_during_execution()) + /* + ESCAPE clause accepts only constant arguments and Item_param. + + Subqueries during context_analysis_only might decide they're + const_during_execution, but not quite const yet, not evaluate-able. + This is fine, as most of context_analysis_only modes will never + reach val_int(), so we won't need the value. + CONTEXT_ANALYSIS_ONLY_DERIVED being a notable exception here. + */ + if (!escape_item->const_during_execution() || + (!escape_item->const_item() && + !(thd->lex->context_analysis_only & ~CONTEXT_ANALYSIS_ONLY_DERIVED))) { my_error(ER_WRONG_ARGUMENTS,MYF(0),"ESCAPE"); return TRUE; } - + + IF_DBUG(*escape= -1,); + if (escape_item->const_item()) { /* If we are on execution stage */ + /* XXX is it safe to evaluate is_expensive() items here? */ String *escape_str= escape_item->val_str(tmp_str); if (escape_str) { @@ -5642,13 +5657,17 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref) if (!res2) return FALSE; // Null argument - const size_t len = res2->length(); - const char* first = res2->ptr(); - const char* last = first + len - 1; + const size_t len= res2->length(); + /* len must be > 2 ('%pattern%') heuristic: only do TurboBM for pattern_len > 2 */ + if (len <= 2) + return FALSE; + + const char* first= res2->ptr(); + const char* last= first + len - 1; if (len > MIN_TURBOBM_PATTERN_LEN + 2 && *first == wild_many && diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index ab8e15372ad..d77da3fa48c 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -832,11 +832,7 @@ class Item_func_ne :public Item_bool_rowready_func2 { protected: SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, - Field *field, Item *value) - { - DBUG_ENTER("Item_func_ne::get_func_mm_tree"); - DBUG_RETURN(get_ne_mm_tree(param, field, value, value)); - } + Field *field, Item *value); public: Item_func_ne(THD *thd, Item *a, Item *b): Item_bool_rowready_func2(thd, a, b) {} @@ -2787,6 +2783,13 @@ public: return this; } + bool walk(Item_processor processor, bool walk_subquery, void *arg) + { + return walk_args(processor, walk_subquery, arg) + || escape_item->walk(processor, walk_subquery, arg) + || (this->*processor)(arg); + } + bool find_selective_predicates_list_processor(void *arg); Item *get_copy(THD *thd) diff --git a/sql/item_create.cc b/sql/item_create.cc index 5f6e7b29d5c..a80781259ca 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. - Copyright (c) 2008, 2020, MariaDB Corporation. + Copyright (c) 2008, 2021, 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 @@ -109,7 +109,6 @@ public: @return An item representing the function call */ virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2) = 0; - protected: /** Constructor. */ Create_func_arg2() {} @@ -136,7 +135,6 @@ public: @return An item representing the function call */ virtual Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3) = 0; - protected: /** Constructor. */ Create_func_arg3() {} @@ -908,6 +906,19 @@ class Create_func_distance : public Create_func_arg2 Create_func_distance() {} virtual ~Create_func_distance() {} }; + + +class Create_func_distance_sphere: public Create_native_func +{ + public: + Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); + static Create_func_distance_sphere s_singleton; + + protected: + Create_func_distance_sphere() {} + virtual ~Create_func_distance_sphere() {} +}; + #endif @@ -4878,6 +4889,26 @@ Create_func_glength::create_1_arg(THD *thd, Item *arg1) { return new (thd->mem_root) Item_func_glength(thd, arg1); } + + +Create_func_distance_sphere Create_func_distance_sphere::s_singleton; + +Item* +Create_func_distance_sphere::create_native(THD *thd, LEX_CSTRING *name, + List<Item> *item_list) +{ + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + if (arg_count < 2) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); + return NULL; + } + return new (thd->mem_root) Item_func_sphere_distance(thd, *item_list); +} #endif @@ -7457,6 +7488,7 @@ static Native_func_registry func_array[] = { { STRING_WITH_LEN("ST_WITHIN") }, GEOM_BUILDER(Create_func_within)}, { { STRING_WITH_LEN("ST_X") }, GEOM_BUILDER(Create_func_x)}, { { STRING_WITH_LEN("ST_Y") }, GEOM_BUILDER(Create_func_y)}, + { { C_STRING_WITH_LEN("ST_DISTANCE_SPHERE") }, GEOM_BUILDER(Create_func_distance_sphere)}, { { STRING_WITH_LEN("SUBSTR_ORACLE") }, BUILDER(Create_func_substr_oracle)}, { { STRING_WITH_LEN("SUBSTRING_INDEX") }, BUILDER(Create_func_substr_index)}, diff --git a/sql/item_func.cc b/sql/item_func.cc index 837156ce39a..d50fb22a154 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -1095,17 +1095,20 @@ double Item_func_plus::real_op() return check_float_overflow(value); } +#if defined(__powerpc64__) && GCC_VERSION >= 6003 && GCC_VERSION <= 10002 +#pragma GCC push_options +#pragma GCC optimize ("no-expensive-optimizations") +#endif longlong Item_func_plus::int_op() { longlong val0= args[0]->val_int(); longlong val1= args[1]->val_int(); - longlong res= val0 + val1; bool res_unsigned= FALSE; + longlong res; if ((null_value= args[0]->null_value || args[1]->null_value)) return 0; - /* First check whether the result can be represented as a (bool unsigned_flag, longlong value) pair, then check if it is compatible @@ -1146,16 +1149,29 @@ longlong Item_func_plus::int_op() { if (val0 >=0 && val1 >= 0) res_unsigned= TRUE; - else if (val0 < 0 && val1 < 0 && res >= 0) + else if (val0 < 0 && val1 < 0 && val0 < (LONGLONG_MIN - val1)) goto err; } } + +#ifndef WITH_UBSAN + res= val0 + val1; +#else + if (res_unsigned) + res= (longlong) ((ulonglong) val0 + (ulonglong) val1); + else + res= val0+val1; +#endif /* WITH_UBSAN */ + return check_integer_overflow(res, res_unsigned); err: return raise_integer_overflow(); } +#if defined(__powerpc64__) && GCC_VERSION >= 6003 && GCC_VERSION <= 10002 +#pragma GCC pop_options +#endif /** Calculate plus of two decimals. @@ -1248,12 +1264,17 @@ double Item_func_minus::real_op() } +#if defined(__powerpc64__) && GCC_VERSION >= 6003 && GCC_VERSION <= 10002 +#pragma GCC push_options +#pragma GCC optimize ("no-expensive-optimizations") +#endif + longlong Item_func_minus::int_op() { longlong val0= args[0]->val_int(); longlong val1= args[1]->val_int(); - longlong res= val0 - val1; bool res_unsigned= FALSE; + longlong res; if ((null_value= args[0]->null_value || args[1]->null_value)) return 0; @@ -1268,12 +1289,8 @@ longlong Item_func_minus::int_op() if (args[1]->unsigned_flag) { if ((ulonglong) val0 < (ulonglong) val1) - { - if (res >= 0) - goto err; - } - else - res_unsigned= TRUE; + goto err; + res_unsigned= TRUE; } else { @@ -1294,23 +1311,35 @@ longlong Item_func_minus::int_op() { if (args[1]->unsigned_flag) { - if ((ulonglong) (val0 - LONGLONG_MIN) < (ulonglong) val1) + if (((ulonglong) val0 - (ulonglong) LONGLONG_MIN) < (ulonglong) val1) goto err; } else { if (val0 > 0 && val1 < 0) res_unsigned= TRUE; - else if (val0 < 0 && val1 > 0 && res >= 0) + else if (val0 < 0 && val1 > 0 && val0 < (LONGLONG_MIN + val1)) goto err; } } +#ifndef WITH_UBSAN + res= val0 - val1; +#else + if (res_unsigned) + res= (longlong) ((ulonglong) val0 - (ulonglong) val1); + else + res= val0 - val1; +#endif /* WITH_UBSAN */ + return check_integer_overflow(res, res_unsigned); err: return raise_integer_overflow(); } +#if defined(__powerpc64__) && GCC_VERSION >= 6003 && GCC_VERSION <= 10002 +#pragma GCC pop_options +#endif /** See Item_func_plus::decimal_op for comments. @@ -2130,31 +2159,29 @@ double Item_func_cot::val_real() longlong Item_func_shift_left::val_int() { DBUG_ASSERT(fixed == 1); - uint shift; - ulonglong res= ((ulonglong) args[0]->val_int() << - (shift=(uint) args[1]->val_int())); + uint shift= (uint) args[1]->val_int(); + ulonglong value= args[0]->val_int(); if (args[0]->null_value || args[1]->null_value) { null_value=1; return 0; } null_value=0; - return (shift < sizeof(longlong)*8 ? (longlong) res : 0); + return (shift < sizeof(longlong)*8 ? (value << shift) : 0); } longlong Item_func_shift_right::val_int() { DBUG_ASSERT(fixed == 1); - uint shift; - ulonglong res= (ulonglong) args[0]->val_int() >> - (shift=(uint) args[1]->val_int()); + uint shift= (uint) args[1]->val_int(); + ulonglong value= args[0]->val_int(); if (args[0]->null_value || args[1]->null_value) { null_value=1; return 0; } null_value=0; - return (shift < sizeof(longlong)*8 ? (longlong) res : 0); + return (shift < sizeof(longlong)*8 ? (value >> shift) : 0); } @@ -3054,10 +3081,11 @@ longlong Item_func_locate::val_int() if (arg_count == 3) { - start0= start= args[2]->val_int() - 1; + start0= start= args[2]->val_int(); - if ((start < 0) || (start > a->length())) + if ((start <= 0) || (start > a->length())) return 0; + start0--; start--; /* start is now sufficiently valid to pass to charpos function */ start= a->charpos((int) start); @@ -3222,7 +3250,7 @@ bool Item_func_find_in_set::fix_length_and_dec() find->length(), 0); enum_bit=0; if (enum_value) - enum_bit=1LL << (enum_value-1); + enum_bit= 1ULL << (enum_value-1); } } } diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index 682051f2448..ef7ccf1dad6 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -136,7 +136,7 @@ String *Item_func_geometry_from_json::val_str(String *str) { String *sv= args[1]->val_str(&tmp_js); my_error(ER_WRONG_VALUE_FOR_TYPE, MYF(0), - "option", sv->c_ptr_safe(), "ST_GeometryFromJSON"); + "option", sv->c_ptr_safe(), "ST_GeomFromGeoJSON"); null_value= 1; return 0; } @@ -173,7 +173,7 @@ String *Item_func_geometry_from_json::val_str(String *str) code= ER_GEOJSON_NOT_CLOSED; break; case Geometry::GEOJ_DIMENSION_NOT_SUPPORTED: - my_error(ER_GIS_INVALID_DATA, MYF(0), "ST_GeometryFromJSON"); + my_error(ER_GIS_INVALID_DATA, MYF(0), "ST_GeomFromGeoJSON"); break; default: report_json_error_ex(js, &je, func_name(), 0, Sql_condition::WARN_LEVEL_WARN); @@ -2528,11 +2528,151 @@ mem_error: } +double Item_func_sphere_distance::val_real() +{ + /* To test null_value of item, first get well-known bytes as a backups */ + String bak1, bak2; + String *arg1= args[0]->val_str(&bak1); + String *arg2= args[1]->val_str(&bak2); + double distance= 0.0; + double sphere_radius= 6370986.0; // Default radius equals Earth radius + + null_value= (args[0]->null_value || args[1]->null_value); + if (null_value) + { + return 0; + } + + if (arg_count == 3) + { + sphere_radius= args[2]->val_real(); + // Radius cannot be Null + if (args[2]->null_value) + { + null_value= true; + return 0; + } + if (sphere_radius <= 0) + { + my_error(ER_INTERNAL_ERROR, MYF(0), "Radius must be greater than zero."); + return 1; + } + } + Geometry_buffer buffer1, buffer2; + Geometry *g1, *g2; + if (!(g1= Geometry::construct(&buffer1, arg1->ptr(), arg1->length())) || + !(g2= Geometry::construct(&buffer2, arg2->ptr(), arg2->length()))) + { + my_error(ER_GIS_INVALID_DATA, MYF(0), "ST_Distance_Sphere"); + goto handle_errors; + } +// Method allowed for points and multipoints + if (!(g1->get_class_info()->m_type_id == Geometry::wkb_point || + g1->get_class_info()->m_type_id == Geometry::wkb_multipoint) || + !(g2->get_class_info()->m_type_id == Geometry::wkb_point || + g2->get_class_info()->m_type_id == Geometry::wkb_multipoint)) + { + // Generate error message in case different geometry is used? + my_error(ER_INTERNAL_ERROR, MYF(0), func_name()); + return 0; + } + distance= spherical_distance_points(g1, g2, sphere_radius); + if (distance < 0) + { + my_error(ER_INTERNAL_ERROR, MYF(0), "Returned distance cannot be negative."); + return 1; + } + return distance; + + handle_errors: + return 0; +} + + +double Item_func_sphere_distance::spherical_distance_points(Geometry *g1, + Geometry *g2, + const double r) +{ + double res= 0.0; + // Length for the single point (25 Bytes) + uint32 len= SRID_SIZE + POINT_DATA_SIZE + WKB_HEADER_SIZE; + int error= 0; + + switch (g2->get_class_info()->m_type_id) + { + case Geometry::wkb_point: + // Optimization for point-point case + if (g1->get_class_info()->m_type_id == Geometry::wkb_point) + { + res= static_cast<Gis_point *>(g2)->calculate_haversine(g1, r, &error); + } + else + { + // Optimization for single point in Multipoint + if (g1->get_data_size() == len) + { + res= static_cast<Gis_point *>(g2)->calculate_haversine(g1, r, &error); + } + else + { + // There are multipoints in g1 + // g1 is MultiPoint and calculate MP.sphericaldistance from g2 Point + if (g1->get_data_size() != GET_SIZE_ERROR) + static_cast<Gis_point *>(g2)->spherical_distance_multipoints( + (Gis_multi_point *)g1, r, &res, &error); + } + } + break; + + case Geometry::wkb_multipoint: + // Optimization for point-point case + if (g1->get_class_info()->m_type_id == Geometry::wkb_point) + { + // Optimization for single point in Multipoint g2 + if (g2->get_data_size() == len) + { + res= static_cast<Gis_point *>(g1)->calculate_haversine(g2, r, &error); + } + else + { + if (g2->get_data_size() != GET_SIZE_ERROR) + // g1 is a point (casted to multi_point) and g2 multipoint + static_cast<Gis_point *>(g1)->spherical_distance_multipoints( + (Gis_multi_point *)g2, r, &res, &error); + } + } + else + { + // Multipoints in g1 and g2 - no optimization + static_cast<Gis_multi_point *>(g1)->spherical_distance_multipoints( + (Gis_multi_point *)g2, r, &res, &error); + } + break; + + default: + DBUG_ASSERT(0); + break; + } + + if (res < 0) + goto handle_error; + + handle_error: + if (error > 0) + my_error(ER_STD_OUT_OF_RANGE_ERROR, MYF(0), + "Longitude should be [-180,180]", "ST_Distance_Sphere"); + else if(error < 0) + my_error(ER_STD_OUT_OF_RANGE_ERROR, MYF(0), + "Latitude should be [-90,90]", "ST_Distance_Sphere"); + return res; +} + + String *Item_func_pointonsurface::val_str(String *str) { Gcalc_operation_transporter trn(&func, &collector); - DBUG_ENTER("Item_func_pointonsurface::val_real"); + DBUG_ENTER("Item_func_pointonsurface::val_str"); DBUG_ASSERT(fixed == 1); String *res= args[0]->val_str(&tmp_value); Geometry_buffer buffer; diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h index 4e7cda137c2..245a8353d35 100644 --- a/sql/item_geofunc.h +++ b/sql/item_geofunc.h @@ -2,7 +2,7 @@ #define ITEM_GEOFUNC_INCLUDED /* Copyright (c) 2000, 2016 Oracle and/or its affiliates. - Copyright (C) 2011, 2016, MariaDB + Copyright (C) 2011, 2021, 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 @@ -934,6 +934,20 @@ public: }; +class Item_func_sphere_distance: public Item_real_func +{ + double spherical_distance_points(Geometry *g1, Geometry *g2, + const double sphere_r); +public: + Item_func_sphere_distance(THD *thd, List<Item> &list): + Item_real_func(thd, list) {} + double val_real(); + const char *func_name() const { return "st_distance_sphere"; } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_sphere_distance>(thd, this); } +}; + + class Item_func_pointonsurface: public Item_geometry_func_args_geometry { String tmp_value; diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 0d15c5e9ad0..4b4a94c814e 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -618,8 +618,6 @@ String *Item_func_json_unquote::read_json(json_engine_t *je) json_scan_start(je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); - je->value_type= (enum json_value_types) -1; /* To report errors right. */ - if (json_read_value(je)) goto error; @@ -982,7 +980,8 @@ my_decimal *Item_func_json_extract::val_decimal(my_decimal *to) case JSON_VALUE_ARRAY: case JSON_VALUE_FALSE: case JSON_VALUE_NULL: - break; + case JSON_VALUE_UNINITALIZED: + break; }; } int2my_decimal(E_DEC_FATAL_ERROR, 0, false/*unsigned_flag*/, to); diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index ea14e9d44f8..759b18c4657 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1514,17 +1514,18 @@ String *Item_func_insert::val_str(String *str) null_value=0; res=args[0]->val_str(str); res2=args[3]->val_str(&tmp_value); - start= args[1]->val_int() - 1; + start= args[1]->val_int(); length= args[2]->val_int(); if (args[0]->null_value || args[1]->null_value || args[2]->null_value || args[3]->null_value) goto null; /* purecov: inspected */ - if ((start < 0) || (start > res->length())) + if ((start <= 0) || (start > res->length())) return res; // Wrong param; skip insert if ((length < 0) || (length > res->length())) length= res->length(); + start--; /* There is one exception not handled (intentionaly) by the character set @@ -3795,13 +3796,12 @@ String *Item_func_unhex::val_str(String *str) } for (end=res->ptr()+res->length(); from < end ; from+=2, to++) { - int hex_char; - *to= (hex_char= hexchar_to_int(from[0])) << 4; - if ((null_value= (hex_char == -1))) - return 0; - *to|= hex_char= hexchar_to_int(from[1]); - if ((null_value= (hex_char == -1))) + int hex_char1, hex_char2; + hex_char1= hexchar_to_int(from[0]); + hex_char2= hexchar_to_int(from[1]); + if ((null_value= (hex_char1 == -1 || hex_char2 == -1))) return 0; + *to= (char) ((hex_char1 << 4) | hex_char2); } return str; } diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index c13c49b8363..826a978805e 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -42,8 +42,13 @@ protected: we don't want to free and potentially have to reallocate the buffer for each call. */ - str_value.length(0); - str_value.set_charset(collation.collation); + if (!str_value.is_alloced()) + str_value.set("", 0, collation.collation); /* Avoid null ptrs */ + else + { + str_value.length(0); /* Reuse allocated area */ + str_value.set_charset(collation.collation); + } return &str_value; } public: diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index e2058475d0e..53a6847c52f 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -276,7 +276,11 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref) { if (sl->tvc) { - wrap_tvc_into_select(thd, sl); + if (!(sl= wrap_tvc_into_select(thd, sl))) + { + res= TRUE; + goto end; + } } } @@ -380,7 +384,7 @@ bool Item_subselect::mark_as_eliminated_processor(void *arg) bool Item_subselect::eliminate_subselect_processor(void *arg) { unit->item= NULL; - unit->exclude_from_tree(); + unit->exclude(); eliminated= TRUE; return FALSE; } @@ -449,6 +453,26 @@ bool Item_subselect::mark_as_dependent(THD *thd, st_select_lex *select, /* + @brief + Update the table bitmaps for the outer references used within a subquery +*/ + +bool Item_subselect::update_table_bitmaps_processor(void *arg) +{ + List_iterator<Ref_to_outside> it(upper_refs); + Ref_to_outside *upper; + + while ((upper= it++)) + { + if (upper->item && + upper->item->walk(&Item::update_table_bitmaps_processor, FALSE, arg)) + return TRUE; + } + return FALSE; +} + + +/* Adjust attributes after our parent select has been merged into grandparent DESCRIPTION @@ -659,6 +683,31 @@ bool Item_subselect::is_expensive() } +static +int walk_items_for_table_list(Item_processor processor, + bool walk_subquery, void *argument, + List<TABLE_LIST>& join_list) +{ + List_iterator<TABLE_LIST> li(join_list); + int res; + while (TABLE_LIST *table= li++) + { + if (table->on_expr) + { + if ((res= table->on_expr->walk(processor, walk_subquery, argument))) + return res; + } + if (table->nested_join) + { + if ((res= walk_items_for_table_list(processor, walk_subquery, argument, + table->nested_join->join_list))) + return res; + } + } + return 0; +} + + bool Item_subselect::walk(Item_processor processor, bool walk_subquery, void *argument) { @@ -690,7 +739,10 @@ bool Item_subselect::walk(Item_processor processor, bool walk_subquery, if (lex->having && (lex->having)->walk(processor, walk_subquery, argument)) return 1; - /* TODO: why does this walk WHERE/HAVING but not ON expressions of outer joins? */ + + if (walk_items_for_table_list(processor, walk_subquery, argument, + *lex->join_list)) + return 1; while ((item=li++)) { @@ -855,7 +907,7 @@ bool Item_subselect::expr_cache_is_needed(THD *thd) inline bool Item_in_subselect::left_expr_has_null() { - return (*(optimizer->get_cache()))->null_value; + return (*(optimizer->get_cache()))->null_value_inside; } @@ -1309,7 +1361,17 @@ bool Item_singlerow_subselect::null_inside() void Item_singlerow_subselect::bring_value() { if (!exec() && assigned()) - null_value= 0; + { + null_value= true; + for (uint i= 0; i < max_columns ; i++) + { + if (!row[i]->null_value) + { + null_value= false; + return; + } + } + } else reset(); } @@ -1335,7 +1397,11 @@ longlong Item_singlerow_subselect::val_int() { DBUG_ASSERT(fixed == 1); if (forced_const) - return value->val_int(); + { + longlong val= value->val_int(); + null_value= value->null_value; + return val; + } if (!exec() && !value->null_value) { null_value= FALSE; @@ -1344,6 +1410,7 @@ longlong Item_singlerow_subselect::val_int() else { reset(); + DBUG_ASSERT(null_value); return 0; } } @@ -1352,7 +1419,11 @@ String *Item_singlerow_subselect::val_str(String *str) { DBUG_ASSERT(fixed == 1); if (forced_const) - return value->val_str(str); + { + String *res= value->val_str(str); + null_value= value->null_value; + return res; + } if (!exec() && !value->null_value) { null_value= FALSE; @@ -1361,6 +1432,7 @@ String *Item_singlerow_subselect::val_str(String *str) else { reset(); + DBUG_ASSERT(null_value); return 0; } } @@ -1388,7 +1460,11 @@ my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value) { DBUG_ASSERT(fixed == 1); if (forced_const) - return value->val_decimal(decimal_value); + { + my_decimal *val= value->val_decimal(decimal_value); + null_value= value->null_value; + return val; + } if (!exec() && !value->null_value) { null_value= FALSE; @@ -1397,6 +1473,7 @@ my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value) else { reset(); + DBUG_ASSERT(null_value); return 0; } } @@ -1406,7 +1483,11 @@ bool Item_singlerow_subselect::val_bool() { DBUG_ASSERT(fixed == 1); if (forced_const) - return value->val_bool(); + { + bool val= value->val_bool(); + null_value= value->null_value; + return val; + } if (!exec() && !value->null_value) { null_value= FALSE; @@ -1415,6 +1496,7 @@ bool Item_singlerow_subselect::val_bool() else { reset(); + DBUG_ASSERT(null_value); return 0; } } @@ -1424,7 +1506,11 @@ bool Item_singlerow_subselect::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t { DBUG_ASSERT(fixed == 1); if (forced_const) - return value->get_date(thd, ltime, fuzzydate); + { + bool val= value->get_date(thd, ltime, fuzzydate); + null_value= value->null_value; + return val; + } if (!exec() && !value->null_value) { null_value= FALSE; @@ -1433,6 +1519,7 @@ bool Item_singlerow_subselect::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t else { reset(); + DBUG_ASSERT(null_value); return 1; } } diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 16a4735359b..4816785fa13 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -50,7 +50,11 @@ class Item_subselect :public Item_result_field, protected Used_tables_and_const_cache, protected With_sum_func_cache { - bool value_assigned; /* value already assigned to subselect */ + /* + Set to TRUE if the value is assigned for the subselect + FALSE: subquery not executed or the subquery returns an empty result + */ + bool value_assigned; bool own_engine; /* the engine was not taken from other Item_subselect */ protected: /* thread handler, will be assigned in fix_fields only */ @@ -256,6 +260,7 @@ public: @retval FALSE otherwise */ bool is_expensive_processor(void *arg) { return is_expensive(); } + bool update_table_bitmaps_processor(void *arg); /** Get the SELECT_LEX structure associated with this Item. @@ -277,7 +282,7 @@ public: Item* build_clone(THD *thd) { return 0; } Item* get_copy(THD *thd) { return 0; } - bool wrap_tvc_into_select(THD *thd, st_select_lex *tvc_sl); + st_select_lex *wrap_tvc_into_select(THD *thd, st_select_lex *tvc_sl); friend class select_result_interceptor; friend class Item_in_optimizer; @@ -286,7 +291,8 @@ public: friend bool Item_ref::fix_fields(THD *, Item **); friend void mark_select_range_as_dependent(THD*, st_select_lex*, st_select_lex*, - Field*, Item*, Item_ident*); + Field*, Item*, Item_ident*, + bool); friend bool convert_join_subqueries_to_semijoins(JOIN *join); }; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index e00fc2fd3ab..581c94bd191 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -2635,9 +2635,9 @@ bool Item_sum_bit::add_as_window(ulonglong value) void Item_sum_or::set_bits_from_counters() { ulonglong value= 0; - for (int i= 0; i < NUM_BIT_COUNTERS; i++) + for (uint i= 0; i < NUM_BIT_COUNTERS; i++) { - value|= bit_counters[i] > 0 ? (1 << i) : 0; + value|= bit_counters[i] > 0 ? (1ULL << i) : 0ULL; } bits= value | reset_bits; } @@ -3619,7 +3619,7 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)), { Item_func_group_concat *item= (Item_func_group_concat *) item_arg; TABLE *table= item->table; - uint max_length= (uint)table->in_use->variables.group_concat_max_len; + uint max_length= table->in_use->variables.group_concat_max_len; String tmp((char *)table->record[1], table->s->reclength, default_charset_info); String tmp2; @@ -3738,7 +3738,7 @@ Item_func_group_concat(THD *thd, Name_resolution_context *context_arg, arg_count_field(select_list->elements), row_count(0), distinct(distinct_arg), - warning_for_row(FALSE), + warning_for_row(FALSE), always_null(FALSE), force_copy_fields(0), row_limit(NULL), offset_limit(NULL), limit_clause(limit_clause), copy_offset_limit(0), copy_row_limit(0), original(0) @@ -3960,7 +3960,7 @@ bool Item_func_group_concat::repack_tree(THD *thd) DBUG_ASSERT(tree->size_of_element == st.tree.size_of_element); st.table= table; st.len= 0; - st.maxlen= (size_t)thd->variables.group_concat_max_len; + st.maxlen= thd->variables.group_concat_max_len; tree_walk(tree, ©_to_tree, &st, left_root_right); if (st.len <= st.maxlen) // Copying aborted. Must be OOM { @@ -3981,7 +3981,7 @@ bool Item_func_group_concat::repack_tree(THD *thd) decreases up to N=10 (that is, factor=1024) and then starts to increase, again, very slowly. */ -#define GCONCAT_REPACK_FACTOR (1 << 10) +#define GCONCAT_REPACK_FACTOR 10 bool Item_func_group_concat::add() { @@ -4027,7 +4027,7 @@ bool Item_func_group_concat::add() { THD *thd= table->in_use; table->field[0]->store(row_str_len, FALSE); - if (tree_len > thd->variables.group_concat_max_len * GCONCAT_REPACK_FACTOR + if ((tree_len >> GCONCAT_REPACK_FACTOR) > thd->variables.group_concat_max_len && tree->elements_in_tree > 1) if (repack_tree(thd)) return 1; @@ -4082,9 +4082,9 @@ Item_func_group_concat::fix_fields(THD *thd, Item **ref) result.set_charset(collation.collation); result_field= 0; null_value= 1; - max_length= (uint32)(thd->variables.group_concat_max_len - / collation.collation->mbminlen - * collation.collation->mbmaxlen); + max_length= (uint32)MY_MIN(thd->variables.group_concat_max_len + / collation.collation->mbminlen + * collation.collation->mbmaxlen, UINT_MAX32); uint32 offset; if (separator->needs_conversion(separator->length(), separator->charset(), diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index b476b5f1f27..7dbc81e8825 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -733,7 +733,10 @@ static bool make_date_time(const LEX_CSTRING &format, MYSQL_TIME *l_time, For example, '1.1' -> '1.100000' */ -static bool get_interval_info(const char *str, size_t length,CHARSET_INFO *cs, size_t count, ulonglong *values, +#define MAX_DIGITS_IN_TIME_SPEC 20 + +static bool get_interval_info(const char *str, size_t length,CHARSET_INFO *cs, + size_t count, ulonglong *values, bool transform_msec) { const char *end=str+length; @@ -745,11 +748,21 @@ static bool get_interval_info(const char *str, size_t length,CHARSET_INFO *cs, s for (i=0 ; i < count ; i++) { - longlong value; + ulonglong value; const char *start= str; - for (value= 0; str != end && my_isdigit(cs, *str); str++) + const char *local_end= end; + + /* + We limit things to 19 digits to not get an overflow. This is ok as + this function is meant to read up to microseconds + */ + if ((local_end-str) > MAX_DIGITS_IN_TIME_SPEC) + local_end= str+ MAX_DIGITS_IN_TIME_SPEC; + + for (value= 0; str != local_end && my_isdigit(cs, *str) ; str++) value= value*10 + *str - '0'; - if ((field_length= (size_t)(str - start)) >= 20) + + if ((field_length= (size_t)(str - start)) >= MAX_DIGITS_IN_TIME_SPEC) return true; values[i]= value; while (str != end && !my_isdigit(cs,*str)) @@ -2070,9 +2083,9 @@ bool Func_handler_date_add_interval_datetime_arg0_time:: bool Item_date_add_interval::eq(const Item *item, bool binary_cmp) const { - Item_date_add_interval *other= (Item_date_add_interval*) item; if (!Item_func::eq(item, binary_cmp)) return 0; + Item_date_add_interval *other= (Item_date_add_interval*) item; return ((int_type == other->int_type) && (date_sub_interval == other->date_sub_interval)); } diff --git a/sql/key.cc b/sql/key.cc index adff6975631..6f0a1112497 100644 --- a/sql/key.cc +++ b/sql/key.cc @@ -244,14 +244,13 @@ void key_restore(uchar *to_record, const uchar *from_key, KEY *key_info, else if (key_part->key_part_flag & HA_VAR_LENGTH_PART) { Field *field= key_part->field; - my_bitmap_map *old_map; my_ptrdiff_t ptrdiff= to_record - field->table->record[0]; field->move_field_offset(ptrdiff); key_length-= HA_KEY_BLOB_LENGTH; length= MY_MIN(key_length, key_part->length); - old_map= dbug_tmp_use_all_columns(field->table, field->table->write_set); + MY_BITMAP *old_map= dbug_tmp_use_all_columns(field->table, &field->table->write_set); field->set_key_image(from_key, length); - dbug_tmp_restore_column_map(field->table->write_set, old_map); + dbug_tmp_restore_column_map(&field->table->write_set, old_map); from_key+= HA_KEY_BLOB_LENGTH; field->move_field_offset(-ptrdiff); } @@ -419,7 +418,7 @@ void field_unpack(String *to, Field *field, const uchar *rec, uint max_length, void key_unpack(String *to, TABLE *table, KEY *key) { - my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); + MY_BITMAP *old_map= dbug_tmp_use_all_columns(table, &table->read_set); DBUG_ENTER("key_unpack"); to->length(0); @@ -443,7 +442,7 @@ void key_unpack(String *to, TABLE *table, KEY *key) field_unpack(to, key_part->field, table->record[0], key_part->length, MY_TEST(key_part->key_part_flag & HA_PART_KEY_SEG)); } - dbug_tmp_restore_column_map(table->read_set, old_map); + dbug_tmp_restore_column_map(&table->read_set, old_map); DBUG_VOID_RETURN; } diff --git a/sql/lock.cc b/sql/lock.cc index a3744d7f000..3a2001fbc34 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -729,6 +729,9 @@ static int unlock_external(THD *thd, TABLE **table,uint count) - GET_LOCK_STORE_LOCKS : Store lock info in TABLE - GET_LOCK_SKIP_SEQUENCES : Ignore sequences (for temporary unlock) - GET_LOCK_ON_THD : Store lock in thd->mem_root + + Temporary tables are not locked (as these are single user), except for + TRANSACTIONAL_TMP_TABLES as locking is needed to handle transactions. */ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags) @@ -745,8 +748,8 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags) { TABLE *t= table_ptr[i]; - if (t->s->tmp_table != NON_TRANSACTIONAL_TMP_TABLE && - t->s->tmp_table != INTERNAL_TMP_TABLE && + if ((likely(!t->s->tmp_table) || + (t->s->tmp_table == TRANSACTIONAL_TMP_TABLE)) && (!(flags & GET_LOCK_SKIP_SEQUENCES) || t->s->sequence == 0)) { lock_count+= t->file->lock_count(); @@ -774,13 +777,13 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags) for (i=0 ; i < count ; i++) { - TABLE *table; + TABLE *table= table_ptr[i]; enum thr_lock_type lock_type; THR_LOCK_DATA **locks_start; - table= table_ptr[i]; - if (table->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE || - table->s->tmp_table == INTERNAL_TMP_TABLE || - ((flags & GET_LOCK_SKIP_SEQUENCES) && table->s->sequence)) + + if (!((likely(!table->s->tmp_table) || + (table->s->tmp_table == TRANSACTIONAL_TMP_TABLE)) && + (!(flags & GET_LOCK_SKIP_SEQUENCES) || table->s->sequence == 0))) continue; lock_type= table->reginfo.lock_type; DBUG_ASSERT(lock_type != TL_WRITE_DEFAULT && lock_type != TL_READ_DEFAULT); diff --git a/sql/log.cc b/sql/log.cc index 480e3b696cc..49a319eb29d 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -6390,8 +6390,9 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate) DBUG_ASSERT(!thd->backup_commit_lock); mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT, MDL_EXPLICIT); - thd->mdl_context.acquire_lock(&mdl_request, - thd->variables.lock_wait_timeout); + if (thd->mdl_context.acquire_lock(&mdl_request, + thd->variables.lock_wait_timeout)) + DBUG_RETURN(1); thd->backup_commit_lock= &mdl_request; if ((res= thd->wait_for_prior_commit())) @@ -6983,6 +6984,9 @@ int MYSQL_BIN_LOG::rotate_and_purge(bool force_rotate, bool check_purge= false; mysql_mutex_lock(&LOCK_log); + + DEBUG_SYNC(current_thd, "rotate_after_acquire_LOCK_log"); + prev_binlog_id= current_binlog_id; if ((err_gtid= do_delete_gtid_domain(domain_drop_lex))) @@ -6993,11 +6997,22 @@ int MYSQL_BIN_LOG::rotate_and_purge(bool force_rotate, } else if (unlikely((error= rotate(force_rotate, &check_purge)))) check_purge= false; + + DEBUG_SYNC(current_thd, "rotate_after_rotate"); + /* NOTE: Run purge_logs wo/ holding LOCK_log because it does not need the mutex. Otherwise causes various deadlocks. + Explicit binlog rotation must be synchronized with a concurrent + binlog ordered commit, in particular not let binlog + checkpoint notification request until early binlogged + concurrent commits have has been completed. */ + mysql_mutex_lock(&LOCK_after_binlog_sync); mysql_mutex_unlock(&LOCK_log); + mysql_mutex_lock(&LOCK_commit_ordered); + mysql_mutex_unlock(&LOCK_after_binlog_sync); + mysql_mutex_unlock(&LOCK_commit_ordered); if (check_purge) checkpoint_and_purge(prev_binlog_id); @@ -7470,6 +7485,8 @@ MYSQL_BIN_LOG::write_transaction_to_binlog(THD *thd, new transaction directly to participate in the group commit. @retval < 0 Error + @retval -2 WSREP error with commit ordering + @retval -3 WSREP return code to mark the leader @retval > 0 If queued as the first entry in the queue (meaning this is the leader) @retval 0 Otherwise (queued as participant, leader handles the commit) @@ -7767,6 +7784,22 @@ MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry) cur= entry->thd->wait_for_commit_ptr; } +#ifdef WITH_WSREP + if (wsrep_is_active(entry->thd) && + wsrep_run_commit_hook(entry->thd, entry->all)) + { + /* Release commit order here */ + if (wsrep_ordered_commit(entry->thd, entry->all, wsrep_apply_error())) + result= -2; + + /* return -3, if this is leader */ + if (orig_queue == NULL) + result= -3; + } + else + DBUG_ASSERT(result != -2 && result != -3); +#endif /* WITH_WSREP */ + if (opt_binlog_commit_wait_count > 0 && orig_queue != NULL) mysql_cond_signal(&COND_prepare_ordered); mysql_mutex_unlock(&LOCK_prepare_ordered); @@ -7788,25 +7821,32 @@ MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry) { int is_leader= queue_for_group_commit(entry); #ifdef WITH_WSREP - if (wsrep_is_active(entry->thd) && - wsrep_run_commit_hook(entry->thd, entry->all)) - { - /* - Release commit order and if leader, wait for prior commit to - complete. This establishes total order for group leaders. - */ - if (wsrep_ordered_commit(entry->thd, entry->all, wsrep_apply_error())) - { - entry->thd->wakeup_subsequent_commits(1); - return 1; - } - if (is_leader) - { - if (entry->thd->wait_for_prior_commit()) - return 1; - } + /* commit order was released in queue_for_group_commit() call, + here we check if wsrep_commit_ordered() failed or if we are leader */ + switch (is_leader) + { + case -2: /* wsrep_ordered_commit() has failed */ + DBUG_ASSERT(wsrep_is_active(entry->thd)); + DBUG_ASSERT(wsrep_run_commit_hook(entry->thd, entry->all)); + entry->thd->wakeup_subsequent_commits(1); + return true; + case -3: /* this is leader, wait for prior commit to + complete. This establishes total order for group leaders + */ + DBUG_ASSERT(wsrep_is_active(entry->thd)); + DBUG_ASSERT(wsrep_run_commit_hook(entry->thd, entry->all)); + if (entry->thd->wait_for_prior_commit()) + return true; + + /* retain the correct is_leader value */ + is_leader= 1; + break; + + default: /* native MariaDB cases */ + break; } #endif /* WITH_WSREP */ + /* The first in the queue handles group commit for all; the others just wait to be signalled when group commit is done. @@ -8173,7 +8213,12 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader) } DEBUG_SYNC(leader->thd, "commit_before_get_LOCK_commit_ordered"); + mysql_mutex_lock(&LOCK_commit_ordered); + DBUG_EXECUTE_IF("crash_before_engine_commit", + { + DBUG_SUICIDE(); + }); last_commit_pos_offset= commit_offset; /* diff --git a/sql/log_event.cc b/sql/log_event.cc index 6871eeda79e..337de3508ed 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -8983,8 +8983,20 @@ err: } #endif /* MYSQL_CLIENT */ - #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) +static bool wsrep_must_replay(THD *thd) +{ +#ifdef WITH_WSREP + mysql_mutex_lock(&thd->LOCK_thd_data); + bool res= WSREP(thd) && thd->wsrep_trx().state() == wsrep::transaction::s_must_replay; + mysql_mutex_unlock(&thd->LOCK_thd_data); + return res; +#else + return false; +#endif +} + + int Xid_log_event::do_apply_event(rpl_group_info *rgi) { bool res; @@ -9049,16 +9061,8 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi) res= trans_commit(thd); /* Automatically rolls back on error. */ thd->release_transactional_locks(); -#ifdef WITH_WSREP - if (WSREP(thd)) mysql_mutex_lock(&thd->LOCK_thd_data); - if ((!res || (WSREP(thd) && thd->wsrep_trx().state() == wsrep::transaction::s_must_replay )) && sub_id) -#else - if (likely(!res) && sub_id) -#endif /* WITH_WSREP */ + if (sub_id && (!res || wsrep_must_replay(thd))) rpl_global_gtid_slave_state->update_state_hash(sub_id, >id, hton, rgi); -#ifdef WITH_WSREP - if (WSREP(thd)) mysql_mutex_unlock(&thd->LOCK_thd_data); -#endif /* WITH_WSREP */ /* Increment the global status commit count variable */ @@ -11267,7 +11271,7 @@ int Rows_log_event::do_add_row_data(uchar *row_data, size_t length) There was the same problem with MERGE MYISAM tables and so here we try to go the same way. */ -static void restore_empty_query_table_list(LEX *lex) +inline void restore_empty_query_table_list(LEX *lex) { if (lex->first_not_own_table()) (*lex->first_not_own_table()->prev_global)= NULL; @@ -11282,6 +11286,8 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi) TABLE* table; DBUG_ENTER("Rows_log_event::do_apply_event(Relay_log_info*)"); int error= 0; + LEX *lex= thd->lex; + uint8 new_trg_event_map= get_trg_event_map(); /* If m_table_id == ~0ULL, then we have a dummy event that does not contain any data. In that case, we just remove all tables in the @@ -11372,27 +11378,29 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi) DBUG_ASSERT(!debug_sync_set_action(thd, STRING_WITH_LEN(action))); };); - if (slave_run_triggers_for_rbr) - { - LEX *lex= thd->lex; - uint8 new_trg_event_map= get_trg_event_map(); - - /* - Trigger's procedures work with global table list. So we have to add - rgi->tables_to_lock content there to get trigger's in the list. + /* + Trigger's procedures work with global table list. So we have to add + rgi->tables_to_lock content there to get trigger's in the list. - Then restore_empty_query_table_list() restore the list as it was - */ - DBUG_ASSERT(lex->query_tables == NULL); - if ((lex->query_tables= rgi->tables_to_lock)) - rgi->tables_to_lock->prev_global= &lex->query_tables; + Then restore_empty_query_table_list() restore the list as it was + */ + DBUG_ASSERT(lex->query_tables == NULL); + if ((lex->query_tables= rgi->tables_to_lock)) + rgi->tables_to_lock->prev_global= &lex->query_tables; - for (TABLE_LIST *tables= rgi->tables_to_lock; tables; - tables= tables->next_global) + for (TABLE_LIST *tables= rgi->tables_to_lock; tables; + tables= tables->next_global) + { + if (slave_run_triggers_for_rbr) { tables->trg_event_map= new_trg_event_map; lex->query_tables_last= &tables->next_global; } + else + { + tables->slave_fk_event_map= new_trg_event_map; + lex->query_tables_last= &tables->next_global; + } } if (unlikely(open_and_lock_tables(thd, rgi->tables_to_lock, FALSE, 0))) { @@ -11749,8 +11757,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi) } /* remove trigger's tables */ - if (slave_run_triggers_for_rbr) - restore_empty_query_table_list(thd->lex); + restore_empty_query_table_list(thd->lex); #if defined(WITH_WSREP) && defined(HAVE_QUERY_CACHE) if (WSREP(thd) && wsrep_thd_is_applying(thd)) @@ -11769,8 +11776,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi) DBUG_RETURN(error); err: - if (slave_run_triggers_for_rbr) - restore_empty_query_table_list(thd->lex); + restore_empty_query_table_list(thd->lex); rgi->slave_close_thread_tables(thd); DBUG_RETURN(error); } @@ -13739,11 +13745,11 @@ int Rows_log_event::update_sequence() /* This event come from a setval function executed on the master. Update the sequence next_number and round, like we do with setval() */ - my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, - table->read_set); + MY_BITMAP *old_map= dbug_tmp_use_all_columns(table, + &table->read_set); longlong nextval= table->field[NEXT_FIELD_NO]->val_int(); longlong round= table->field[ROUND_FIELD_NO]->val_int(); - dbug_tmp_restore_column_map(table->read_set, old_map); + dbug_tmp_restore_column_map(&table->read_set, old_map); return table->s->sequence->set_value(table, nextval, round, 0) > 0; } diff --git a/sql/mdl.cc b/sql/mdl.cc index 93b7982c4a5..5e54178db70 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -1138,7 +1138,7 @@ MDL_wait::timed_wait(MDL_context_owner *owner, struct timespec *abs_timeout, DBUG_ASSERT(!debug_sync_set_action((owner->get_thd()), STRING_WITH_LEN(act))); };); - if (wsrep_thd_is_BF(owner->get_thd(), false)) + if (WSREP_ON && wsrep_thd_is_BF(owner->get_thd(), false)) { wait_result= mysql_cond_wait(&m_COND_wait_status, &m_LOCK_wait_status); } @@ -1211,7 +1211,7 @@ void MDL_lock::Ticket_list::add_ticket(MDL_ticket *ticket) */ DBUG_ASSERT(ticket->get_lock()); #ifdef WITH_WSREP - if ((this == &(ticket->get_lock()->m_waiting)) && + if (WSREP_ON && (this == &(ticket->get_lock()->m_waiting)) && wsrep_thd_is_BF(ticket->get_ctx()->get_thd(), false)) { Ticket_iterator itw(ticket->get_lock()->m_waiting); @@ -2782,6 +2782,7 @@ void MDL_context::find_deadlock() context was waiting is concurrently satisfied. */ (void) victim->m_wait.set_status(MDL_wait::VICTIM); + victim->inc_deadlock_overweight(); victim->unlock_deadlock_victim(); if (victim == this) diff --git a/sql/mdl.h b/sql/mdl.h index 9a788b0ea31..a2cb7c2aa85 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -909,7 +909,8 @@ public: /** @pre Only valid if we started waiting for lock. */ inline uint get_deadlock_weight() const - { return m_waiting_for->get_deadlock_weight(); } + { return m_waiting_for->get_deadlock_weight() + m_deadlock_overweight; } + void inc_deadlock_overweight() { m_deadlock_overweight++; } /** Post signal to the context (and wake it up if necessary). @@ -1027,6 +1028,7 @@ private: */ MDL_wait_for_subgraph *m_waiting_for; LF_PINS *m_pins; + uint m_deadlock_overweight= 0; private: MDL_ticket *find_ticket(MDL_request *mdl_req, enum_mdl_duration *duration); diff --git a/sql/mf_iocache.cc b/sql/mf_iocache.cc index c9cff6ad930..877a49edbec 100644 --- a/sql/mf_iocache.cc +++ b/sql/mf_iocache.cc @@ -26,7 +26,7 @@ Used instead of FILE when reading or writing whole files. This will make mf_rec_cache obsolete. One can change info->pos_in_file to a higher value to skip bytes in file if - also info->rc_pos is set to info->rc_end. + also info->read_pos is set to info->read_end. If called through open_cached_file(), then the temporary file will only be created if a write exeeds the file buffer or if one calls flush_io_cache(). diff --git a/sql/mysqld.cc b/sql/mysqld.cc index a74fb4326e4..7e3ce878cdc 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -363,7 +363,6 @@ static bool binlog_format_used= false; LEX_STRING opt_init_connect, opt_init_slave; mysql_cond_t COND_thread_cache; static mysql_cond_t COND_flush_thread_cache; -mysql_cond_t COND_slave_background; static DYNAMIC_ARRAY all_options; static longlong start_memory_used; @@ -701,7 +700,7 @@ mysql_mutex_t LOCK_crypt, LOCK_global_system_variables, LOCK_user_conn, - LOCK_connection_count, LOCK_error_messages, LOCK_slave_background; + LOCK_connection_count, LOCK_error_messages; mysql_mutex_t LOCK_stats, LOCK_global_user_client_stats, LOCK_global_table_stats, LOCK_global_index_stats; @@ -893,8 +892,7 @@ PSI_mutex_key key_LOCK_stats, PSI_mutex_key key_LOCK_gtid_waiting; PSI_mutex_key key_LOCK_after_binlog_sync; -PSI_mutex_key key_LOCK_prepare_ordered, key_LOCK_commit_ordered, - key_LOCK_slave_background; +PSI_mutex_key key_LOCK_prepare_ordered, key_LOCK_commit_ordered; PSI_mutex_key key_TABLE_SHARE_LOCK_share; PSI_mutex_key key_LOCK_ack_receiver; @@ -968,7 +966,6 @@ static PSI_mutex_info all_server_mutexes[]= { &key_LOCK_prepare_ordered, "LOCK_prepare_ordered", PSI_FLAG_GLOBAL}, { &key_LOCK_after_binlog_sync, "LOCK_after_binlog_sync", PSI_FLAG_GLOBAL}, { &key_LOCK_commit_ordered, "LOCK_commit_ordered", PSI_FLAG_GLOBAL}, - { &key_LOCK_slave_background, "LOCK_slave_background", PSI_FLAG_GLOBAL}, { &key_LOCK_thread_cache, "LOCK_thread_cache", PSI_FLAG_GLOBAL}, { &key_PARTITION_LOCK_auto_inc, "HA_DATA_PARTITION::LOCK_auto_inc", 0}, { &key_LOCK_slave_state, "LOCK_slave_state", 0}, @@ -1039,7 +1036,7 @@ PSI_cond_key key_TC_LOG_MMAP_COND_queue_busy; PSI_cond_key key_COND_rpl_thread_queue, key_COND_rpl_thread, key_COND_rpl_thread_stop, key_COND_rpl_thread_pool, key_COND_parallel_entry, key_COND_group_commit_orderer, - key_COND_prepare_ordered, key_COND_slave_background; + key_COND_prepare_ordered; PSI_cond_key key_COND_wait_gtid, key_COND_gtid_ignore_duplicates; PSI_cond_key key_COND_ack_receiver; @@ -1087,7 +1084,6 @@ static PSI_cond_info all_server_conds[]= { &key_COND_parallel_entry, "COND_parallel_entry", 0}, { &key_COND_group_commit_orderer, "COND_group_commit_orderer", 0}, { &key_COND_prepare_ordered, "COND_prepare_ordered", 0}, - { &key_COND_slave_background, "COND_slave_background", 0}, { &key_COND_start_thread, "COND_start_thread", PSI_FLAG_GLOBAL}, { &key_COND_wait_gtid, "COND_wait_gtid", 0}, { &key_COND_gtid_ignore_duplicates, "COND_gtid_ignore_duplicates", 0}, @@ -1135,14 +1131,6 @@ PSI_file_key key_file_binlog_state; PSI_statement_info stmt_info_new_packet; #endif -#ifdef WITH_WSREP -/** Whether the Galera write-set replication is enabled. A cached copy of -global_system_variables.wsrep_on && wsrep_provider && - strcmp(wsrep_provider, WSREP_NONE) -*/ -bool WSREP_ON_; -#endif /* WITH_WSREP */ - #ifndef EMBEDDED_LIBRARY void net_before_header_psi(struct st_net *net, void *thd, size_t /* unused: count */) { @@ -1525,31 +1513,9 @@ static void end_ssl(); /* common callee of two shutdown phases */ static void kill_thread(THD *thd) { - if (WSREP(thd)) mysql_mutex_lock(&thd->LOCK_thd_data); mysql_mutex_lock(&thd->LOCK_thd_kill); - if (thd->mysys_var) - { - thd->mysys_var->abort= 1; - mysql_mutex_lock(&thd->mysys_var->mutex); - if (thd->mysys_var->current_cond) - { - for (uint i= 0; i < 2; i++) - { - int ret= mysql_mutex_trylock(thd->mysys_var->current_mutex); - mysql_cond_broadcast(thd->mysys_var->current_cond); - if (!ret) - { - /* Thread has surely got the signal, unlock and abort */ - mysql_mutex_unlock(thd->mysys_var->current_mutex); - break; - } - sleep(1); - } - } - mysql_mutex_unlock(&thd->mysys_var->mutex); - } + thd->abort_current_cond_wait(true); mysql_mutex_unlock(&thd->LOCK_thd_kill); - if (WSREP(thd)) mysql_mutex_unlock(&thd->LOCK_thd_data); } @@ -1905,6 +1871,7 @@ extern "C" void unireg_abort(int exit_code) wsrep_deinit(true); wsrep_deinit_server(); } + wsrep_sst_auth_free(); #endif // WITH_WSREP clean_up(!opt_abort && (exit_code || !opt_bootstrap)); /* purecov: inspected */ @@ -2140,8 +2107,6 @@ static void clean_up_mutexes() mysql_cond_destroy(&COND_prepare_ordered); mysql_mutex_destroy(&LOCK_after_binlog_sync); mysql_mutex_destroy(&LOCK_commit_ordered); - mysql_mutex_destroy(&LOCK_slave_background); - mysql_cond_destroy(&COND_slave_background); #ifndef EMBEDDED_LIBRARY mysql_mutex_destroy(&LOCK_error_log); #endif @@ -3288,7 +3253,13 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused))) } break; case SIGHUP: +#if defined(SI_KERNEL) if (!abort_loop && origin != SI_KERNEL) +#elif defined(SI_USER) + if (!abort_loop && origin <= SI_USER) +#else + if (!abort_loop) +#endif { int not_used; mysql_print_status(); // Print some debug info @@ -4555,9 +4526,6 @@ static int init_thread_environment() MY_MUTEX_INIT_SLOW); mysql_mutex_init(key_LOCK_commit_ordered, &LOCK_commit_ordered, MY_MUTEX_INIT_SLOW); - mysql_mutex_init(key_LOCK_slave_background, &LOCK_slave_background, - MY_MUTEX_INIT_SLOW); - mysql_cond_init(key_COND_slave_background, &COND_slave_background, NULL); #ifdef HAVE_OPENSSL mysql_mutex_init(key_LOCK_des_key_file, @@ -5255,6 +5223,10 @@ static int init_server_components() that there are unprocessed options. */ my_getopt_skip_unknown= 0; +#ifdef WITH_WSREP + if (wsrep_recovery) + my_getopt_skip_unknown= TRUE; +#endif if ((ho_error= handle_options(&remaining_argc, &remaining_argv, no_opts, mysqld_get_one_option))) @@ -5264,20 +5236,27 @@ static int init_server_components() remaining_argv--; my_getopt_skip_unknown= TRUE; - if (remaining_argc > 1) +#ifdef WITH_WSREP + if (!wsrep_recovery) { - fprintf(stderr, "%s: Too many arguments (first extra is '%s').\n", - my_progname, remaining_argv[1]); - unireg_abort(1); +#endif + if (remaining_argc > 1) + { + fprintf(stderr, "%s: Too many arguments (first extra is '%s').\n", + my_progname, remaining_argv[1]); + unireg_abort(1); + } +#ifdef WITH_WSREP } +#endif } - if (init_io_cache_encryption()) - unireg_abort(1); - if (opt_abort) unireg_abort(0); + if (init_io_cache_encryption()) + unireg_abort(1); + /* if the errmsg.sys is not loaded, terminate to maintain behaviour */ if (!DEFAULT_ERRMSGS[0][0]) unireg_abort(1); @@ -5729,10 +5708,7 @@ int mysqld_main(int argc, char **argv) } #ifdef WITH_WSREP - WSREP_ON_= (global_system_variables.wsrep_on && - wsrep_provider && - strcmp(wsrep_provider, WSREP_NONE)); - + wsrep_set_wsrep_on(); if (WSREP_ON && wsrep_check_opts()) unireg_abort(1); #endif @@ -5823,9 +5799,12 @@ int mysqld_main(int argc, char **argv) wsrep_init_startup (false); } - WSREP_DEBUG("Startup creating %ld applier threads running %lu", - wsrep_slave_threads - 1, wsrep_running_applier_threads); - wsrep_create_appliers(wsrep_slave_threads - 1); + if (wsrep_cluster_address_exists()) + { + WSREP_DEBUG("Startup creating %ld applier threads running %lu", + wsrep_slave_threads - 1, wsrep_running_applier_threads); + wsrep_create_appliers(wsrep_slave_threads - 1); + } } } @@ -7574,8 +7553,11 @@ static int show_memory_used(THD *thd, SHOW_VAR *var, char *buff, var->type= SHOW_LONGLONG; var->value= buff; if (scope == OPT_GLOBAL) + { + calc_sum_of_all_status_if_needed(status_var); *(longlong*) buff= (status_var->global_memory_used + status_var->local_memory_used); + } else *(longlong*) buff= status_var->local_memory_used; return 0; diff --git a/sql/mysqld.h b/sql/mysqld.h index bd45ff7b798..f4d0d891a0f 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -615,8 +615,7 @@ extern mysql_mutex_t LOCK_error_log, LOCK_delayed_insert, LOCK_short_uuid_generator, LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone, LOCK_active_mi, LOCK_manager, LOCK_user_conn, - LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count, - LOCK_slave_background; + LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count ; extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_global_system_variables; extern mysql_rwlock_t LOCK_all_status_vars; extern mysql_mutex_t LOCK_start_thread; @@ -631,7 +630,6 @@ extern mysql_rwlock_t LOCK_ssl_refresh; extern mysql_prlock_t LOCK_system_variables_hash; extern mysql_cond_t COND_start_thread; extern mysql_cond_t COND_manager; -extern mysql_cond_t COND_slave_background; extern Atomic_counter<uint32_t> thread_count; extern char *opt_ssl_ca, *opt_ssl_capath, *opt_ssl_cert, *opt_ssl_cipher, diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 1a7ac1044c3..d47aa1ee41e 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -449,6 +449,7 @@ void print_range_for_non_indexed_field(String *out, Field *field, static void print_min_range_operator(String *out, const ha_rkey_function flag); static void print_max_range_operator(String *out, const ha_rkey_function flag); +static bool is_field_an_unique_index(RANGE_OPT_PARAM *param, Field *field); /* SEL_IMERGE is a list of possible ways to do index merge, i.e. it is @@ -3636,8 +3637,7 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond) void store_key_image_to_rec(Field *field, uchar *ptr, uint len) { - /* Do the same as print_key() does */ - my_bitmap_map *old_map; + /* Do the same as print_key() does */ if (field->real_maybe_null()) { @@ -3649,10 +3649,10 @@ void store_key_image_to_rec(Field *field, uchar *ptr, uint len) field->set_notnull(); ptr++; } - old_map= dbug_tmp_use_all_columns(field->table, - field->table->write_set); + MY_BITMAP *old_map= dbug_tmp_use_all_columns(field->table, + &field->table->write_set); field->set_key_image(ptr, len); - dbug_tmp_restore_column_map(field->table->write_set, old_map); + dbug_tmp_restore_column_map(&field->table->write_set, old_map); } #ifdef WITH_PARTITION_STORAGE_ENGINE @@ -3867,7 +3867,7 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond) PART_PRUNE_PARAM prune_param; MEM_ROOT alloc; RANGE_OPT_PARAM *range_par= &prune_param.range_param; - my_bitmap_map *old_sets[2]; + MY_BITMAP *old_sets[2]; prune_param.part_info= part_info; init_sql_alloc(&alloc, "prune_partitions", @@ -3884,7 +3884,7 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond) } dbug_tmp_use_all_columns(table, old_sets, - table->read_set, table->write_set); + &table->read_set, &table->write_set); range_par->thd= thd; range_par->table= table; /* range_par->cond doesn't need initialization */ @@ -3981,7 +3981,7 @@ all_used: retval= FALSE; // some partitions are used mark_all_partitions_as_used(prune_param.part_info); end: - dbug_tmp_restore_column_maps(table->read_set, table->write_set, old_sets); + dbug_tmp_restore_column_maps(&table->read_set, &table->write_set, old_sets); thd->no_errors=0; thd->mem_root= range_par->old_root; free_root(&alloc,MYF(0)); // Return memory & allocator @@ -7690,6 +7690,21 @@ SEL_TREE *Item_bool_func::get_ne_mm_tree(RANGE_OPT_PARAM *param, } +SEL_TREE *Item_func_ne::get_func_mm_tree(RANGE_OPT_PARAM *param, + Field *field, Item *value) +{ + DBUG_ENTER("Item_func_ne::get_func_mm_tree"); + /* + If this condition is a "col1<>...", where there is a UNIQUE KEY(col1), + do not construct a SEL_TREE from it. A condition that excludes just one + row in the table is not selective (unless there are only a few rows) + */ + if (is_field_an_unique_index(param, field)) + DBUG_RETURN(NULL); + DBUG_RETURN(get_ne_mm_tree(param, field, value, value)); +} + + SEL_TREE *Item_func_between::get_func_mm_tree(RANGE_OPT_PARAM *param, Field *field, Item *value) { @@ -7788,28 +7803,16 @@ SEL_TREE *Item_func_in::get_func_mm_tree(RANGE_OPT_PARAM *param, DBUG_RETURN(0); /* - If this is "unique_key NOT IN (...)", do not consider it sargable (for - any index, not just the unique one). The logic is as follows: + if this is a "col1 NOT IN (...)", and there is a UNIQUE KEY(col1), do + not constuct a SEL_TREE from it. The rationale is as follows: - if there are only a few constants, this condition is not selective (unless the table is also very small in which case we won't gain anything) - - If there are a lot of constants, the overhead of building and + - if there are a lot of constants, the overhead of building and processing enormous range list is not worth it. */ - if (param->using_real_indexes) - { - key_map::Iterator it(field->key_start); - uint key_no; - while ((key_no= it.next_bit()) != key_map::Iterator::BITMAP_END) - { - KEY *key_info= ¶m->table->key_info[key_no]; - if (key_info->user_defined_key_parts == 1 && - (key_info->flags & HA_NOSAME)) - { - DBUG_RETURN(0); - } - } - } + if (is_field_an_unique_index(param, field)) + DBUG_RETURN(0); /* Get a SEL_TREE for "(-inf|NULL) < X < c_0" interval. */ uint i=0; @@ -8527,6 +8530,38 @@ SEL_TREE *Item_equal::get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr) } +/* + @brief + Check if there is an one-segment unique key that matches the field exactly + + @detail + In the future we could also add "almost unique" indexes where any value is + present only in a few rows (but necessarily exactly one row) +*/ +static bool is_field_an_unique_index(RANGE_OPT_PARAM *param, Field *field) +{ + DBUG_ENTER("is_field_an_unique_index"); + + // The check for using_real_indexes is there because of the heuristics + // this function is used for. + if (param->using_real_indexes) + { + key_map::Iterator it(field->key_start); + uint key_no; + while ((key_no= it++) != key_map::Iterator::BITMAP_END) + { + KEY *key_info= &field->table->key_info[key_no]; + if (key_info->user_defined_key_parts == 1 && + (key_info->flags & HA_NOSAME)) + { + DBUG_RETURN(true); + } + } + } + DBUG_RETURN(false); +} + + SEL_TREE * Item_bool_func::get_mm_parts(RANGE_OPT_PARAM *param, Field *field, Item_func::Functype type, Item *value) @@ -15658,8 +15693,8 @@ static void print_sel_arg_key(Field *field, const uchar *key, String *out) { TABLE *table= field->table; - my_bitmap_map *old_sets[2]; - dbug_tmp_use_all_columns(table, old_sets, table->read_set, table->write_set); + MY_BITMAP *old_sets[2]; + dbug_tmp_use_all_columns(table, old_sets, &table->read_set, &table->write_set); if (field->real_maybe_null()) { @@ -15679,7 +15714,7 @@ print_sel_arg_key(Field *field, const uchar *key, String *out) field->val_str(out); end: - dbug_tmp_restore_column_maps(table->read_set, table->write_set, old_sets); + dbug_tmp_restore_column_maps(&table->read_set, &table->write_set, old_sets); } @@ -15774,9 +15809,9 @@ print_key(KEY_PART *key_part, const uchar *key, uint used_length) const uchar *key_end= key+used_length; uint store_length; TABLE *table= key_part->field->table; - my_bitmap_map *old_sets[2]; + MY_BITMAP *old_sets[2]; - dbug_tmp_use_all_columns(table, old_sets, table->read_set, table->write_set); + dbug_tmp_use_all_columns(table, old_sets, &table->read_set, &table->write_set); for (; key < key_end; key+=store_length, key_part++) { @@ -15803,7 +15838,7 @@ print_key(KEY_PART *key_part, const uchar *key, uint used_length) if (key+store_length < key_end) fputc('/',DBUG_FILE); } - dbug_tmp_restore_column_maps(table->read_set, table->write_set, old_sets); + dbug_tmp_restore_column_maps(&table->read_set, &table->write_set, old_sets); } @@ -15811,16 +15846,16 @@ static void print_quick(QUICK_SELECT_I *quick, const key_map *needed_reg) { char buf[MAX_KEY/8+1]; TABLE *table; - my_bitmap_map *old_sets[2]; + MY_BITMAP *old_sets[2]; DBUG_ENTER("print_quick"); if (!quick) DBUG_VOID_RETURN; DBUG_LOCK_FILE; table= quick->head; - dbug_tmp_use_all_columns(table, old_sets, table->read_set, table->write_set); + dbug_tmp_use_all_columns(table, old_sets, &table->read_set, &table->write_set); quick->dbug_dump(0, TRUE); - dbug_tmp_restore_column_maps(table->read_set, table->write_set, old_sets); + dbug_tmp_restore_column_maps(&table->read_set, &table->write_set, old_sets); fprintf(DBUG_FILE,"other_keys: 0x%s:\n", needed_reg->print(buf)); @@ -16043,8 +16078,8 @@ void print_range_for_non_indexed_field(String *out, Field *field, KEY_MULTI_RANGE *range) { TABLE *table= field->table; - my_bitmap_map *old_sets[2]; - dbug_tmp_use_all_columns(table, old_sets, table->read_set, table->write_set); + MY_BITMAP *old_sets[2]; + dbug_tmp_use_all_columns(table, old_sets, &table->read_set, &table->write_set); if (range->start_key.length) { @@ -16059,7 +16094,7 @@ void print_range_for_non_indexed_field(String *out, Field *field, print_max_range_operator(out, range->end_key.flag); field->print_key_part_value(out, range->end_key.key, field->key_length()); } - dbug_tmp_restore_column_maps(table->read_set, table->write_set, old_sets); + dbug_tmp_restore_column_maps(&table->read_set, &table->write_set, old_sets); } @@ -16126,8 +16161,8 @@ static void print_key_value(String *out, const KEY_PART_INFO *key_part, StringBuffer<128> tmp(system_charset_info); TABLE *table= field->table; uint store_length; - my_bitmap_map *old_sets[2]; - dbug_tmp_use_all_columns(table, old_sets, table->read_set, table->write_set); + MY_BITMAP *old_sets[2]; + dbug_tmp_use_all_columns(table, old_sets, &table->read_set, &table->write_set); const uchar *key_end= key+used_length; for (; key < key_end; key+=store_length, key_part++) @@ -16140,7 +16175,7 @@ static void print_key_value(String *out, const KEY_PART_INFO *key_part, if (key + store_length < key_end) out->append(STRING_WITH_LEN(",")); } - dbug_tmp_restore_column_maps(table->read_set, table->write_set, old_sets); + dbug_tmp_restore_column_maps(&table->read_set, &table->write_set, old_sets); out->append(STRING_WITH_LEN(")")); } diff --git a/sql/opt_split.cc b/sql/opt_split.cc index 6807a623b73..395422de3c3 100644 --- a/sql/opt_split.cc +++ b/sql/opt_split.cc @@ -204,7 +204,7 @@ struct SplM_field_info struct SplM_plan_info { /* The cached splitting execution plan P */ - struct st_position *best_positions; + POSITION *best_positions; /* The cost of the above plan */ double cost; /* Selectivity of splitting used in P */ @@ -236,6 +236,8 @@ public: SplM_field_info *spl_fields; /* The number of elements in the above list */ uint spl_field_cnt; + /* The list of equalities injected into WHERE for split optimization */ + List<Item> inj_cond_list; /* Contains the structures to generate all KEYUSEs for pushable equalities */ List<KEY_FIELD> added_key_fields; /* The cache of evaluated execution plans for 'join' with pushed equalities */ @@ -1047,22 +1049,22 @@ SplM_plan_info * JOIN_TAB::choose_best_splitting(double record_count, bool JOIN::inject_best_splitting_cond(table_map remaining_tables) { Item *inj_cond= 0; - List<Item> inj_cond_list; + List<Item> *inj_cond_list= &spl_opt_info->inj_cond_list; List_iterator<KEY_FIELD> li(spl_opt_info->added_key_fields); KEY_FIELD *added_key_field; while ((added_key_field= li++)) { if (remaining_tables & added_key_field->val->used_tables()) continue; - if (inj_cond_list.push_back(added_key_field->cond, thd->mem_root)) + if (inj_cond_list->push_back(added_key_field->cond, thd->mem_root)) return true; } - DBUG_ASSERT(inj_cond_list.elements); - switch (inj_cond_list.elements) { + DBUG_ASSERT(inj_cond_list->elements); + switch (inj_cond_list->elements) { case 1: - inj_cond= inj_cond_list.head(); break; + inj_cond= inj_cond_list->head(); break; default: - inj_cond= new (thd->mem_root) Item_cond_and(thd, inj_cond_list); + inj_cond= new (thd->mem_root) Item_cond_and(thd, *inj_cond_list); if (!inj_cond) return true; } @@ -1082,6 +1084,40 @@ bool JOIN::inject_best_splitting_cond(table_map remaining_tables) /** @brief + Test if equality is injected for split optimization + + @param + eq_item equality to to test + + @retval + true eq_item is equality injected for split optimization + false otherwise +*/ + +bool is_eq_cond_injected_for_split_opt(Item_func_eq *eq_item) +{ + Item *left_item= eq_item->arguments()[0]->real_item(); + if (left_item->type() != Item::FIELD_ITEM) + return false; + Field *field= ((Item_field *) left_item)->field; + if (!field->table->reginfo.join_tab) + return false; + JOIN *join= field->table->reginfo.join_tab->join; + if (!join->spl_opt_info) + return false; + List_iterator_fast<Item> li(join->spl_opt_info->inj_cond_list); + Item *item; + while ((item= li++)) + { + if (item == eq_item) + return true; + } + return false; +} + + +/** + @brief Fix the splitting chosen for a splittable table in the final query plan @param @@ -1149,7 +1185,7 @@ bool JOIN_TAB::fix_splitting(SplM_plan_info *spl_plan, bool JOIN::fix_all_splittings_in_plan() { table_map prev_tables= 0; - table_map all_tables= (1 << table_count) - 1; + table_map all_tables= (table_map(1) << table_count) - 1; for (uint tablenr= 0; tablenr < table_count; tablenr++) { POSITION *cur_pos= &best_positions[tablenr]; diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index f7349e7a1bf..7bd778e339f 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -2990,7 +2990,7 @@ void advance_sj_state(JOIN *join, table_map remaining_tables, uint idx, } -void Sj_materialization_picker::set_from_prev(struct st_position *prev) +void Sj_materialization_picker::set_from_prev(POSITION *prev) { if (prev->sjmat_picker.is_used) set_empty(); @@ -3176,7 +3176,7 @@ bool Sj_materialization_picker::check_qep(JOIN *join, } -void LooseScan_picker::set_from_prev(struct st_position *prev) +void LooseScan_picker::set_from_prev(POSITION *prev) { if (prev->loosescan_picker.is_used) set_empty(); @@ -3197,7 +3197,7 @@ bool LooseScan_picker::check_qep(JOIN *join, double *read_time, table_map *handled_fanout, sj_strategy_enum *strategy, - struct st_position *loose_scan_pos) + POSITION *loose_scan_pos) { POSITION *first= join->positions + first_loosescan_table; /* @@ -3275,7 +3275,7 @@ bool LooseScan_picker::check_qep(JOIN *join, return FALSE; } -void Firstmatch_picker::set_from_prev(struct st_position *prev) +void Firstmatch_picker::set_from_prev(POSITION *prev) { if (prev->firstmatch_picker.is_used) invalidate_firstmatch_prefix(); @@ -5789,8 +5789,8 @@ Item *and_new_conditions_to_optimized_cond(THD *thd, Item *cond, ((Item_func *) item)->functype() == Item_func::EQ_FUNC && check_simple_equality(thd, Item::Context(Item::ANY_SUBST, - ((Item_func_equal *)item)->compare_type_handler(), - ((Item_func_equal *)item)->compare_collation()), + ((Item_func_eq *)item)->compare_type_handler(), + ((Item_func_eq *)item)->compare_collation()), ((Item_func *)item)->arguments()[0], ((Item_func *)item)->arguments()[1], &new_cond_equal)) diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index af2d9ddc2e7..27360d4a10c 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -842,7 +842,10 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, if (is_field_part) { if (between || eq_type) + { *range_fl&= ~(NO_MAX_RANGE | NO_MIN_RANGE); + *range_fl&= ~(max_fl ? NEAR_MAX : NEAR_MIN); + } else { *range_fl&= ~(max_fl ? NO_MAX_RANGE : NO_MIN_RANGE); diff --git a/sql/partition_element.h b/sql/partition_element.h index ff0d0d59fc4..e0a519065cc 100644 --- a/sql/partition_element.h +++ b/sql/partition_element.h @@ -144,6 +144,7 @@ public: part_min_rows(part_elem->part_min_rows), range_value(0), partition_name(NULL), tablespace_name(part_elem->tablespace_name), + log_entry(NULL), part_comment(part_elem->part_comment), data_file_name(part_elem->data_file_name), index_file_name(part_elem->index_file_name), @@ -152,6 +153,8 @@ public: part_state(part_elem->part_state), nodegroup_id(part_elem->nodegroup_id), has_null_value(FALSE), + signed_flag(part_elem->signed_flag), + max_value(part_elem->max_value), id(part_elem->id), empty(part_elem->empty), type(CONVENTIONAL) diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 9f08964e62c..a8459438be7 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -1449,13 +1449,13 @@ void partition_info::print_no_partition_found(TABLE *table_arg, myf errflag) buf_ptr= (char*)"from column_list"; else { - my_bitmap_map *old_map= dbug_tmp_use_all_columns(table_arg, table_arg->read_set); + MY_BITMAP *old_map= dbug_tmp_use_all_columns(table_arg, &table_arg->read_set); if (part_expr->null_value) buf_ptr= (char*)"NULL"; else longlong10_to_str(err_value, buf, part_expr->unsigned_flag ? 10 : -10); - dbug_tmp_restore_column_map(table_arg->read_set, old_map); + dbug_tmp_restore_column_map(&table_arg->read_set, old_map); } my_error(ER_NO_PARTITION_FOR_GIVEN_VALUE, errflag, buf_ptr); } diff --git a/sql/protocol.cc b/sql/protocol.cc index aa9651e974c..eb7f19d2bd0 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -1269,15 +1269,15 @@ bool Protocol_text::store(Field *field) CHARSET_INFO *tocs= this->thd->variables.character_set_results; #ifdef DBUG_ASSERT_EXISTS TABLE *table= field->table; - my_bitmap_map *old_map= 0; + MY_BITMAP *old_map= 0; if (table->file) - old_map= dbug_tmp_use_all_columns(table, table->read_set); + old_map= dbug_tmp_use_all_columns(table, &table->read_set); #endif field->val_str(&str); #ifdef DBUG_ASSERT_EXISTS if (old_map) - dbug_tmp_restore_column_map(table->read_set, old_map); + dbug_tmp_restore_column_map(&table->read_set, old_map); #endif return store_string_aux(str.ptr(), str.length(), str.charset(), tocs); diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc index 154636480ca..35901cb5263 100644 --- a/sql/rpl_parallel.cc +++ b/sql/rpl_parallel.cc @@ -4,6 +4,7 @@ #include "rpl_mi.h" #include "sql_parse.h" #include "debug_sync.h" +#include "sql_repl.h" #include "wsrep_mysqld.h" #ifdef WITH_WSREP #include "wsrep_trans_observer.h" @@ -100,7 +101,7 @@ handle_queued_pos_update(THD *thd, rpl_parallel_thread::queued_event *qev) return; mysql_mutex_lock(&rli->data_lock); - cmp= strcmp(rli->group_relay_log_name, qev->event_relay_log_name); + cmp= compare_log_name(rli->group_relay_log_name, qev->event_relay_log_name); if (cmp < 0) { rli->group_relay_log_pos= qev->future_event_relay_log_pos; @@ -109,7 +110,7 @@ handle_queued_pos_update(THD *thd, rpl_parallel_thread::queued_event *qev) rli->group_relay_log_pos < qev->future_event_relay_log_pos) rli->group_relay_log_pos= qev->future_event_relay_log_pos; - cmp= strcmp(rli->group_master_log_name, qev->future_event_master_log_name); + cmp= compare_log_name(rli->group_master_log_name, qev->future_event_master_log_name); if (cmp < 0) { strcpy(rli->group_master_log_name, qev->future_event_master_log_name); diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index bcdff1e33a8..c8f77acf523 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -991,7 +991,7 @@ void Relay_log_info::inc_group_relay_log_pos(ulonglong log_pos, if (rgi->is_parallel_exec) { /* In case of parallel replication, do not update the position backwards. */ - int cmp= strcmp(group_relay_log_name, rgi->event_relay_log_name); + int cmp= compare_log_name(group_relay_log_name, rgi->event_relay_log_name); if (cmp < 0) { group_relay_log_pos= rgi->future_event_relay_log_pos; @@ -1003,7 +1003,7 @@ void Relay_log_info::inc_group_relay_log_pos(ulonglong log_pos, In the parallel case we need to update the master_log_name here, rather than in Rotate_log_event::do_update_pos(). */ - cmp= strcmp(group_master_log_name, rgi->future_event_master_log_name); + cmp= compare_log_name(group_master_log_name, rgi->future_event_master_log_name); if (cmp <= 0) { if (cmp < 0) diff --git a/sql/semisync_master_ack_receiver.cc b/sql/semisync_master_ack_receiver.cc index e189fc5f631..b24d6452480 100644 --- a/sql/semisync_master_ack_receiver.cc +++ b/sql/semisync_master_ack_receiver.cc @@ -267,6 +267,11 @@ void Ack_receiver::run() net_clear(&net, 0); net.vio= &slave->vio; + /* + Set compress flag. This is needed to support + Slave_compress_protocol flag enabled Slaves + */ + net.compress= slave->thd->net.compress; len= my_net_read(&net); if (likely(len != packet_error)) diff --git a/sql/service_wsrep.cc b/sql/service_wsrep.cc index 4fc927cfd86..14f136ca480 100644 --- a/sql/service_wsrep.cc +++ b/sql/service_wsrep.cc @@ -37,6 +37,16 @@ extern "C" void wsrep_thd_UNLOCK(const THD *thd) mysql_mutex_unlock(&thd->LOCK_thd_data); } +extern "C" void wsrep_thd_kill_LOCK(const THD *thd) +{ + mysql_mutex_lock(&thd->LOCK_thd_kill); +} + +extern "C" void wsrep_thd_kill_UNLOCK(const THD *thd) +{ + mysql_mutex_unlock(&thd->LOCK_thd_kill); +} + extern "C" const char* wsrep_thd_client_state_str(const THD *thd) { return wsrep::to_c_string(thd->wsrep_cs().state()); @@ -110,15 +120,23 @@ extern "C" my_bool wsrep_get_debug() return wsrep_debug; } +/* + Test if this connection is a true local (user) connection and not + a replication or wsrep applier thread. + + Note that this is only usable for galera (as there are other kinds + of system threads, and only if WSREP_NNULL() is tested by the caller. + */ extern "C" my_bool wsrep_thd_is_local(const THD *thd) { /* - async replication IO and background threads have nothing to replicate in the cluster, - marking them as non-local here to prevent write set population and replication + async replication IO and background threads have nothing to + replicate in the cluster, marking them as non-local here to + prevent write set population and replication - async replication SQL thread, applies client transactions from mariadb master - and will be replicated into cluster - */ + async replication SQL thread, applies client transactions from + mariadb master and will be replicated into cluster + */ return ( thd->system_thread != SYSTEM_THREAD_SLAVE_BACKGROUND && thd->system_thread != SYSTEM_THREAD_SLAVE_IO && @@ -200,16 +218,8 @@ extern "C" void wsrep_handle_SR_rollback(THD *bf_thd, extern "C" my_bool wsrep_thd_bf_abort(THD *bf_thd, THD *victim_thd, my_bool signal) { - DBUG_EXECUTE_IF("sync.before_wsrep_thd_abort", - { - const char act[]= - "now " - "SIGNAL sync.before_wsrep_thd_abort_reached " - "WAIT_FOR signal.before_wsrep_thd_abort"; - DBUG_ASSERT(!debug_sync_set_action(bf_thd, - STRING_WITH_LEN(act))); - };); - + mysql_mutex_assert_owner(&victim_thd->LOCK_thd_kill); + mysql_mutex_assert_not_owner(&victim_thd->LOCK_thd_data); my_bool ret= wsrep_bf_abort(bf_thd, victim_thd); /* Send awake signal if victim was BF aborted or does not @@ -218,8 +228,6 @@ extern "C" my_bool wsrep_thd_bf_abort(THD *bf_thd, THD *victim_thd, */ if ((ret || !wsrep_on(victim_thd)) && signal) { - mysql_mutex_assert_not_owner(&victim_thd->LOCK_thd_data); - mysql_mutex_assert_not_owner(&victim_thd->LOCK_thd_kill); mysql_mutex_lock(&victim_thd->LOCK_thd_data); if (victim_thd->wsrep_aborter && victim_thd->wsrep_aborter != bf_thd->thread_id) @@ -230,10 +238,8 @@ extern "C" my_bool wsrep_thd_bf_abort(THD *bf_thd, THD *victim_thd, return false; } - mysql_mutex_lock(&victim_thd->LOCK_thd_kill); victim_thd->wsrep_aborter= bf_thd->thread_id; victim_thd->awake_no_mutex(KILL_QUERY); - mysql_mutex_unlock(&victim_thd->LOCK_thd_kill); mysql_mutex_unlock(&victim_thd->LOCK_thd_data); } else { WSREP_DEBUG("wsrep_thd_bf_abort skipped awake"); diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index ef0dfd5eb63..1c6a1326538 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -191,7 +191,7 @@ ER_DB_DROP_DELETE hun "Adatbazis megszuntetesi hiba ('%-.192s' nem torolheto, hibakod: %M)" ita "Errore durante la cancellazione del database (impossibile cancellare '%-.192s', errno: %M)" jpn "データベース削除エラー ('%-.192s' を削除できません。エラー番号: %M)" - kor "데이타베이스 제거 에러('%-.192s'를 삭제할 수 없읍니다, 에러번호: %M)" + kor "데이타베이스 제거 에러('%-.192s'를 삭제할 수 없습니다, 에러번호: %M)" nor "Feil ved fjerning (drop) av databasen (kan ikke slette '%-.192s', feil %M)" norwegian-ny "Feil ved fjerning (drop) av databasen (kan ikkje slette '%-.192s', feil %M)" pol "Bł?d podczas usuwania bazy danych (nie można usun?ć '%-.192s', bł?d %M)" @@ -216,7 +216,7 @@ ER_DB_DROP_RMDIR hun "Adatbazis megszuntetesi hiba ('%-.192s' nem szuntetheto meg, hibakod: %M)" ita "Errore durante la cancellazione del database (impossibile rmdir '%-.192s', errno: %M)" jpn "データベース削除エラー (ディレクトリ '%-.192s' を削除できません。エラー番号: %M)" - kor "데이타베이스 제거 에러(rmdir '%-.192s'를 할 수 없읍니다, 에러번호: %M)" + kor "데이타베이스 제거 에러(rmdir '%-.192s'를 할 수 없습니다, 에러번호: %M)" nor "Feil ved sletting av database (kan ikke slette katalogen '%-.192s', feil %M)" norwegian-ny "Feil ved sletting av database (kan ikkje slette katalogen '%-.192s', feil %M)" pol "Bł?d podczas usuwania bazy danych (nie można wykonać rmdir '%-.192s', bł?d %M)" @@ -516,7 +516,7 @@ ER_DUP_KEY 23000 hun "Irasi hiba, duplikalt kulcs a '%-.192s' tablaban" ita "Scrittura impossibile: chiave duplicata nella tabella '%-.192s'" jpn "書き込めません。表 '%-.192s' に重複するキーがあります。" - kor "기록할 수 없읍니다., 테이블 '%-.192s'에서 중복 키" + kor "기록할 수 없습니다., 테이블 '%-.192s'에서 중복 키" nor "Kan ikke skrive, flere like nøkler i tabellen '%-.192s'" norwegian-ny "Kan ikkje skrive, flere like nyklar i tabellen '%-.192s'" pol "Nie można zapisać, powtórzone klucze w tabeli '%-.192s'" @@ -641,7 +641,7 @@ ER_FILE_USED hun "'%-.192s' a valtoztatas ellen zarolva" ita "'%-.192s' e` soggetto a lock contro i cambiamenti" jpn "'%-.192s' はロックされています。" - kor "'%-.192s'가 변경할 수 없도록 잠겨있읍니다." + kor "'%-.192s'가 변경할 수 없도록 잠겨있습니다." nor "'%-.192s' er låst mot oppdateringer" norwegian-ny "'%-.192s' er låst mot oppdateringar" pol "'%-.192s' jest zablokowany na wypadek zmian" @@ -691,7 +691,7 @@ ER_FORM_NOT_FOUND hun "A(z) '%-.192s' nezet nem letezik a(z) '%-.192s'-hoz" ita "La view '%-.192s' non esiste per '%-.192s'" jpn "ビュー '%-.192s' は '%-.192s' に存在しません。" - kor "뷰 '%-.192s'가 '%-.192s'에서는 존재하지 않읍니다." + kor "뷰 '%-.192s'가 '%-.192s'에서는 존재하지 않습니다." nor "View '%-.192s' eksisterer ikke for '%-.192s'" norwegian-ny "View '%-.192s' eksisterar ikkje for '%-.192s'" pol "Widok '%-.192s' nie istnieje dla '%-.192s'" @@ -739,7 +739,7 @@ ER_KEY_NOT_FOUND hun "Nem talalhato a rekord '%-.192s'-ben" ita "Impossibile trovare il record in '%-.192s'" jpn "'%-.192s' にレコードが見つかりません。" - kor "'%-.192s'에서 레코드를 찾을 수 없읍니다." + kor "'%-.192s'에서 레코드를 찾을 수 없습니다." nor "Kan ikke finne posten i '%-.192s'" norwegian-ny "Kan ikkje finne posten i '%-.192s'" pol "Nie można znaleĽć rekordu w '%-.192s'" @@ -985,7 +985,7 @@ ER_BAD_HOST_ERROR 08S01 hun "A gepnev nem allapithato meg a cimbol" ita "Impossibile risalire al nome dell'host dall'indirizzo (risoluzione inversa)" jpn "IPアドレスからホスト名を解決できません。" - kor "당신의 컴퓨터의 호스트이름을 얻을 수 없읍니다." + kor "당신의 컴퓨터의 호스트이름을 얻을 수 없습니다." nor "Kan ikke få tak i vertsnavn for din adresse" norwegian-ny "Kan ikkje få tak i vertsnavn for di adresse" pol "Nie można otrzymać nazwy hosta dla twojego adresu" @@ -1533,7 +1533,7 @@ ER_PARSE_ERROR 42000 s1009 hun "A %s a '%-.80T'-hez kozeli a %d sorban" ita "%s vicino a '%-.80T' linea %d" jpn "%s : '%-.80T' 付近 %d 行目" - kor "'%s' 에러 같읍니다. ('%-.80T' 명령어 라인 %d)" + kor "'%s' 에러 같습니다. ('%-.80T' 명령어 라인 %d)" nor "%s nær '%-.80T' på linje %d" norwegian-ny "%s attmed '%-.80T' på line %d" pol "%s obok '%-.80T' w linii %d" @@ -1658,7 +1658,7 @@ ER_TOO_MANY_KEYS 42000 S1009 hindi "बहुत सारी KEYS निर्दिष्ट हैं; अधिकतम %d KEYS की अनुमति है" ita "Troppe chiavi. Sono ammesse max %d chiavi" jpn "索引の数が多すぎます。最大 %d 個までです。" - kor "너무 많은 키가 정의되어 있읍니다.. 최대 %d의 키가 가능함" + kor "너무 많은 키가 정의되어 있습니다.. 최대 %d의 키가 가능함" nor "For mange nøkler spesifisert. Maks %d nøkler tillatt" norwegian-ny "For mange nykler spesifisert. Maks %d nyklar tillatt" pol "Okre?lono zbyt wiele kluczy. Dostępnych jest maksymalnie %d kluczy" @@ -1683,7 +1683,7 @@ ER_TOO_MANY_KEY_PARTS 42000 S1009 hun "Tul sok kulcsdarabot definialt. Maximum %d resz engedelyezett" ita "Troppe parti di chiave specificate. Sono ammesse max %d parti" jpn "索引のキー列指定が多すぎます。最大 %d 個までです。" - kor "너무 많은 키 부분(parts)들이 정의되어 있읍니다.. 최대 %d 부분이 가능함" + kor "너무 많은 키 부분(parts)들이 정의되어 있습니다.. 최대 %d 부분이 가능함" nor "For mange nøkkeldeler spesifisert. Maks %d deler tillatt" norwegian-ny "For mange nykkeldelar spesifisert. Maks %d delar tillatt" pol "Okre?lono zbyt wiele czę?ci klucza. Dostępnych jest maksymalnie %d czę?ci" @@ -2593,7 +2593,7 @@ ER_FIELD_SPECIFIED_TWICE 42000 hun "A(z) '%-.192s' mezot ketszer definialta" ita "Campo '%-.192s' specificato 2 volte" jpn "列 '%-.192s' は2回指定されています。" - kor "칼럼 '%-.192s'는 두번 정의되어 있읍니다." + kor "칼럼 '%-.192s'는 두번 정의되어 있습니다." nor "Feltet '%-.192s' er spesifisert to ganger" norwegian-ny "Feltet '%-.192s' er spesifisert to gangar" pol "Field '%-.192s' specified twice" @@ -2774,7 +2774,7 @@ ER_TOO_BIG_ROWSIZE 42000 hun "Tul nagy sormeret. A maximalis sormeret (nem szamolva a blob objektumokat) %ld. Nehany mezot meg kell valtoztatnia" ita "Riga troppo grande. La massima grandezza di una riga, non contando i BLOB, e` %ld. Devi cambiare alcuni campi in BLOB" jpn "行サイズが大きすぎます。この表の最大行サイズは BLOB を含まずに %ld です。格納時のオーバーヘッドも含まれます(マニュアルを確認してください)。列をTEXTまたはBLOBに変更する必要があります。" - kor "너무 큰 row 사이즈입니다. BLOB를 계산하지 않고 최대 row 사이즈는 %ld입니다. 얼마간의 필드들을 BLOB로 바꾸셔야 겠군요.." + kor "너무 큰 row 사이즈입니다. BLOB를 계산하지 않고 최대 row 사이즈는 %ld입니다. 일부열을 BLOB 또는 TEXT로 변경해야 합니다." por "Tamanho de linha grande demais. O máximo tamanho de linha, não contando BLOBs, é %ld. Você tem que mudar alguns campos para BLOBs" rum "Marimea liniei (row) prea mare. Marimea maxima a liniei, excluzind BLOB-urile este de %ld. Trebuie sa schimbati unele cimpuri in BLOB-uri" rus "Слишком большой размер записи. Максимальный размер строки, исключая поля BLOB, - %ld. Возможно, вам следует изменить тип некоторых полей на BLOB" @@ -3077,7 +3077,7 @@ ER_PASSWORD_NO_MATCH 28000 hun "Nincs megegyezo sor a user tablaban" ita "Impossibile trovare la riga corrispondente nella tabella user" jpn "ユーザーテーブルに該当するレコードが見つかりません。" - kor "사용자 테이블에서 일치하는 것을 찾을 수 없읍니다." + kor "사용자 테이블에서 일치하는 것을 찾을 수 없습니다." por "Não pode encontrar nenhuma linha que combine na tabela usuário (user table)" rum "Nu pot gasi nici o linie corespunzatoare in tabela utilizatorului" rus "Невозможно отыскать подходящую запись в таблице пользователей" @@ -3219,7 +3219,7 @@ ER_MIX_OF_GROUP_FUNC_AND_FIELDS 42000 hun "A GROUP mezok (MIN(),MAX(),COUNT()...) kevert hasznalata nem lehetseges GROUP BY hivatkozas nelkul" ita "Il mescolare funzioni di aggregazione (MIN(),MAX(),COUNT()...) e non e` illegale se non c'e` una clausula GROUP BY" jpn "GROUP BY句が無い場合、集計関数(MIN(),MAX(),COUNT(),...)と通常の列を同時に使用できません。" - kor "Mixing of GROUP 칼럼s (MIN(),MAX(),COUNT(),...) with no GROUP 칼럼s is illegal if there is no GROUP BY clause" + kor "GROUP BY 절 없이 혼합된 GROUP 함수 (MIN(),MAX(),COUNT(),...) 를 사용할 수 없습니다." por "Mistura de colunas agrupadas (com MIN(), MAX(), COUNT(), ...) com colunas não agrupadas é ilegal, se não existir uma cláusula de agrupamento (cláusula GROUP BY)" rum "Amestecarea de coloane GROUP (MIN(),MAX(),COUNT()...) fara coloane GROUP este ilegala daca nu exista o clauza GROUP BY" rus "Одновременное использование сгруппированных (GROUP) столбцов (MIN(),MAX(),COUNT(),...) с несгруппированными столбцами является некорректным, если в выражении есть GROUP BY" @@ -3668,6 +3668,7 @@ ER_TOO_LONG_STRING 42000 est "Tulemus on pikem kui lubatud 'max_allowed_packet' muutujaga" fre "La chaîne résultat est plus grande que 'max_allowed_packet'" ger "Ergebnis-String ist länger als 'max_allowed_packet' Bytes" + kor "결과 문자열이 설정된 max_allowed_packet 값보다 큽니다." hindi "रिजल्ट स्ट्रिंग 'max_allowed_packet' से लंबा है" hun "Ez eredmeny sztring nagyobb, mint a lehetseges maximum: 'max_allowed_packet'" ita "La stringa di risposta e` piu` lunga di 'max_allowed_packet'" @@ -3689,6 +3690,7 @@ ER_TABLE_CANT_HANDLE_BLOB 42000 ger "Der verwendete Tabellentyp (%s) unterstützt keine BLOB- und TEXT-Felder" hindi "स्टोरेज इंजन %s BLOB/TEXT कॉलम्स को सपोर्ट नहीं करता" hun "A hasznalt tabla tipus (%s) nem tamogatja a BLOB/TEXT mezoket" + kor "스토리지 엔진 (%s)는 BLOB/TEXT 컬럼을 지원하지 않습니다." ita "Il tipo di tabella usata (%s) non supporta colonne di tipo BLOB/TEXT" por "Tipo de tabela usado (%s) não permite colunas BLOB/TEXT" rum "Tipul de tabela folosit (%s) nu suporta coloane de tip BLOB/TEXT" @@ -3706,6 +3708,7 @@ ER_TABLE_CANT_HANDLE_AUTO_INCREMENT 42000 fre "Ce type de table (%s) ne supporte pas les colonnes AUTO_INCREMENT" ger "Der verwendete Tabellentyp (%s) unterstützt keine AUTO_INCREMENT-Felder" hindi "स्टोरेज इंजन %s AUTO_INCREMENT कॉलम्स को सपोर्ट नहीं करता" + kor "스토리지 엔진 (%s)는 AUTO_INCREMENT를 지원하지 않습니다." hun "A hasznalt tabla tipus (%s) nem tamogatja az AUTO_INCREMENT tipusu mezoket" ita "Il tipo di tabella usata (%s) non supporta colonne di tipo AUTO_INCREMENT" por "Tipo de tabela usado (%s) não permite colunas AUTO_INCREMENT" diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc index 5439f13b3f4..f27676bee19 100644 --- a/sql/signal_handler.cc +++ b/sql/signal_handler.cc @@ -15,6 +15,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ #include "mariadb.h" +#include "my_dbug.h" #include <signal.h> //#include "sys_vars.h" @@ -30,6 +31,11 @@ #define SIGNAL_FMT "signal %d" #endif + +#if defined(__APPLE__) || defined(__FreeBSD__) +#include <sys/sysctl.h> +#endif + #ifndef PATH_MAX #define PATH_MAX 4096 #endif @@ -51,7 +57,7 @@ extern const char *optimizer_switch_names[]; static inline void output_core_info() { /* proc is optional on some BSDs so it can't hurt to look */ -#ifdef HAVE_READLINK +#if defined(HAVE_READLINK) && !defined(__APPLE__) && !defined(__FreeBSD__) char buff[PATH_MAX]; ssize_t len; int fd; @@ -77,6 +83,13 @@ static inline void output_core_info() my_close(fd, MYF(0)); } #endif +#elif defined(__APPLE__) || defined(__FreeBSD__) + char buff[PATH_MAX]; + size_t len = sizeof(buff); + if (sysctlbyname("kern.corefile", buff, &len, NULL, 0) == 0) + { + my_safe_printf_stderr("Core pattern: %.*s\n", (int) len, buff); + } #else char buff[80]; my_getwd(buff, sizeof(buff), 0); @@ -118,8 +131,8 @@ extern "C" sig_handler handle_fatal_signal(int sig) my_safe_printf_stderr("Fatal " SIGNAL_FMT " while backtracing\n", sig); goto end; } - segfaulted = 1; + DBUG_PRINT("error", ("handling fatal signal")); curr_time= my_time(0); localtime_r(&curr_time, &tm); diff --git a/sql/slave.cc b/sql/slave.cc index 9d4049c6452..31bd9372a14 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -64,6 +64,7 @@ #include "rpl_parallel.h" #include "sql_show.h" #include "semisync_slave.h" +#include "sql_manager.h" #define FLAGSTR(V,F) ((V)&(F)?#F" ":"") @@ -362,10 +363,33 @@ end: return err; } +static THD *new_bg_THD() +{ + THD *thd= new THD(next_thread_id()); + thd->thread_stack= (char*) &thd; + thd->store_globals(); + thd->system_thread = SYSTEM_THREAD_SLAVE_BACKGROUND; + thd->security_ctx->skip_grants(); + thd->set_command(COM_DAEMON); + thd->variables.wsrep_on= 0; + return thd; +} -static void -handle_gtid_pos_auto_create_request(THD *thd, void *hton) +static void bg_gtid_delete_pending(void *) { + THD *thd= new_bg_THD(); + + rpl_slave_state::list_element *list; + list= rpl_global_gtid_slave_state->gtid_grab_pending_delete_list(); + rpl_global_gtid_slave_state->gtid_delete_pending(thd, &list); + if (list) + rpl_global_gtid_slave_state->put_back_list(list); + delete thd; +} + +static void bg_gtid_pos_auto_create(void *hton) +{ + THD *thd= NULL; int UNINIT_VAR(err); plugin_ref engine= NULL, *auto_engines; rpl_slave_state::gtid_pos_table *entry; @@ -377,7 +401,6 @@ handle_gtid_pos_auto_create_request(THD *thd, void *hton) it. */ mysql_mutex_lock(&LOCK_global_system_variables); - engine= NULL; for (auto_engines= opt_gtid_pos_auto_plugins; auto_engines && *auto_engines; ++auto_engines) @@ -422,6 +445,7 @@ handle_gtid_pos_auto_create_request(THD *thd, void *hton) table_name.str= loc_table_name.c_ptr_safe(); table_name.length= loc_table_name.length(); + thd= new_bg_THD(); err= gtid_pos_table_creation(thd, engine, &table_name); if (err) { @@ -449,46 +473,16 @@ handle_gtid_pos_auto_create_request(THD *thd, void *hton) mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); end: + delete thd; if (engine) plugin_unlock(NULL, engine); } - -static bool slave_background_thread_running; -static bool slave_background_thread_stop; static bool slave_background_thread_gtid_loaded; -static struct slave_background_kill_t { - slave_background_kill_t *next; - THD *to_kill; -} *slave_background_kill_list; - -static struct slave_background_gtid_pos_create_t { - slave_background_gtid_pos_create_t *next; - void *hton; -} *slave_background_gtid_pos_create_list; - -static volatile bool slave_background_gtid_pending_delete_flag; - - -pthread_handler_t -handle_slave_background(void *arg __attribute__((unused))) +static void bg_rpl_load_gtid_slave_state(void *) { - THD *thd; - PSI_stage_info old_stage; - bool stop; - - my_thread_init(); - thd= new THD(next_thread_id()); - thd->thread_stack= (char*) &thd; /* Set approximate stack start */ - thd->system_thread = SYSTEM_THREAD_SLAVE_BACKGROUND; - thd->store_globals(); - thd->security_ctx->skip_grants(); - thd->set_command(COM_DAEMON); -#ifdef WITH_WSREP - thd->variables.wsrep_on= 0; -#endif - + THD *thd= new_bg_THD(); thd_proc_info(thd, "Loading slave GTID position from table"); if (rpl_load_gtid_slave_state(thd)) sql_print_warning("Failed to load slave replication state from table " @@ -497,207 +491,62 @@ handle_slave_background(void *arg __attribute__((unused))) thd->get_stmt_da()->sql_errno(), thd->get_stmt_da()->message()); - mysql_mutex_lock(&LOCK_slave_background); + // hijacking global_rpl_thread_pool cond here - it's only once on startup + mysql_mutex_lock(&global_rpl_thread_pool.LOCK_rpl_thread_pool); slave_background_thread_gtid_loaded= true; - mysql_cond_broadcast(&COND_slave_background); - - THD_STAGE_INFO(thd, stage_slave_background_process_request); - do - { - slave_background_kill_t *kill_list; - slave_background_gtid_pos_create_t *create_list; - bool pending_deletes; - - thd->ENTER_COND(&COND_slave_background, &LOCK_slave_background, - &stage_slave_background_wait_request, - &old_stage); - for (;;) - { - stop= thd->killed || slave_background_thread_stop; - kill_list= slave_background_kill_list; - create_list= slave_background_gtid_pos_create_list; - pending_deletes= slave_background_gtid_pending_delete_flag; - if (stop || kill_list || create_list || pending_deletes) - break; - mysql_cond_wait(&COND_slave_background, &LOCK_slave_background); - } - - slave_background_kill_list= NULL; - slave_background_gtid_pos_create_list= NULL; - slave_background_gtid_pending_delete_flag= false; - thd->EXIT_COND(&old_stage); - - while (kill_list) - { - slave_background_kill_t *p = kill_list; - THD *to_kill= p->to_kill; - kill_list= p->next; - - to_kill->awake(KILL_CONNECTION); - mysql_mutex_lock(&to_kill->LOCK_wakeup_ready); - to_kill->rgi_slave->killed_for_retry= - rpl_group_info::RETRY_KILL_KILLED; - mysql_cond_broadcast(&to_kill->COND_wakeup_ready); - mysql_mutex_unlock(&to_kill->LOCK_wakeup_ready); - my_free(p); - } - - while (create_list) - { - slave_background_gtid_pos_create_t *next= create_list->next; - void *hton= create_list->hton; - handle_gtid_pos_auto_create_request(thd, hton); - my_free(create_list); - create_list= next; - } - - if (pending_deletes) - { - rpl_slave_state::list_element *list; - - slave_background_gtid_pending_delete_flag= false; - list= rpl_global_gtid_slave_state->gtid_grab_pending_delete_list(); - rpl_global_gtid_slave_state->gtid_delete_pending(thd, &list); - if (list) - rpl_global_gtid_slave_state->put_back_list(list); - } - - mysql_mutex_lock(&LOCK_slave_background); - } while (!stop); - - slave_background_thread_running= false; - mysql_cond_broadcast(&COND_slave_background); - mysql_mutex_unlock(&LOCK_slave_background); - + mysql_cond_signal(&global_rpl_thread_pool.COND_rpl_thread_pool); + mysql_mutex_unlock(&global_rpl_thread_pool.LOCK_rpl_thread_pool); delete thd; - - my_thread_end(); - return 0; } +static void bg_slave_kill(void *victim) +{ + THD *to_kill= (THD *)victim; + to_kill->awake(KILL_CONNECTION); + mysql_mutex_lock(&to_kill->LOCK_wakeup_ready); + to_kill->rgi_slave->killed_for_retry= rpl_group_info::RETRY_KILL_KILLED; + mysql_cond_broadcast(&to_kill->COND_wakeup_ready); + mysql_mutex_unlock(&to_kill->LOCK_wakeup_ready); +} - -void -slave_background_kill_request(THD *to_kill) +void slave_background_kill_request(THD *to_kill) { if (to_kill->rgi_slave->killed_for_retry) return; // Already deadlock killed. - slave_background_kill_t *p= - (slave_background_kill_t *)my_malloc(sizeof(*p), MYF(MY_WME)); - if (p) - { - p->to_kill= to_kill; - to_kill->rgi_slave->killed_for_retry= - rpl_group_info::RETRY_KILL_PENDING; - mysql_mutex_lock(&LOCK_slave_background); - p->next= slave_background_kill_list; - slave_background_kill_list= p; - mysql_cond_signal(&COND_slave_background); - mysql_mutex_unlock(&LOCK_slave_background); - } + to_kill->rgi_slave->killed_for_retry= rpl_group_info::RETRY_KILL_PENDING; + mysql_manager_submit(bg_slave_kill, to_kill); } - /* This function must only be called from a slave SQL thread (or worker thread), to ensure that the table_entry will not go away before we can lock the LOCK_slave_state. */ -void -slave_background_gtid_pos_create_request( +void slave_background_gtid_pos_create_request( rpl_slave_state::gtid_pos_table *table_entry) { - slave_background_gtid_pos_create_t *p; - if (table_entry->state != rpl_slave_state::GTID_POS_AUTO_CREATE) return; - p= (slave_background_gtid_pos_create_t *)my_malloc(sizeof(*p), MYF(MY_WME)); - if (!p) - return; mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state); if (table_entry->state != rpl_slave_state::GTID_POS_AUTO_CREATE) { - my_free(p); mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); return; } table_entry->state= rpl_slave_state::GTID_POS_CREATE_REQUESTED; mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); - p->hton= table_entry->table_hton; - mysql_mutex_lock(&LOCK_slave_background); - p->next= slave_background_gtid_pos_create_list; - slave_background_gtid_pos_create_list= p; - mysql_cond_signal(&COND_slave_background); - mysql_mutex_unlock(&LOCK_slave_background); + mysql_manager_submit(bg_gtid_pos_auto_create, table_entry->table_hton); } /* - Request the slave background thread to delete no longer used rows from the + Request the manager thread to delete no longer used rows from the mysql.gtid_slave_pos* tables. - - This is called from time-critical rpl_slave_state::update(), so we avoid - taking any locks here. This means we may race with the background thread - to occasionally lose a signal. This is not a problem; any pending rows to - be deleted will just be deleted a bit later as part of the next batch. */ -void -slave_background_gtid_pending_delete_request(void) +void slave_background_gtid_pending_delete_request(void) { - slave_background_gtid_pending_delete_flag= true; - mysql_cond_signal(&COND_slave_background); -} - - -/* - Start the slave background thread. - - This thread is currently used for two purposes: - - 1. To load the GTID state from mysql.gtid_slave_pos at server start; reading - from table requires valid THD, which is otherwise not available during - server init. - - 2. To kill worker thread transactions during parallel replication, when a - storage engine attempts to take an errorneous conflicting lock that would - cause a deadlock. Killing is done asynchroneously, as the kill may not - be safe within the context of a callback from inside storage engine - locking code. -*/ -static int -start_slave_background_thread() -{ - pthread_t th; - - slave_background_thread_running= true; - slave_background_thread_stop= false; - slave_background_thread_gtid_loaded= false; - if (mysql_thread_create(key_thread_slave_background, - &th, &connection_attrib, handle_slave_background, - NULL)) - { - sql_print_error("Failed to create thread while initialising slave"); - return 1; - } - mysql_mutex_lock(&LOCK_slave_background); - while (!slave_background_thread_gtid_loaded) - mysql_cond_wait(&COND_slave_background, &LOCK_slave_background); - mysql_mutex_unlock(&LOCK_slave_background); - - return 0; -} - - -static void -stop_slave_background_thread() -{ - mysql_mutex_lock(&LOCK_slave_background); - slave_background_thread_stop= true; - mysql_cond_broadcast(&COND_slave_background); - while (slave_background_thread_running) - mysql_cond_wait(&COND_slave_background, &LOCK_slave_background); - mysql_mutex_unlock(&LOCK_slave_background); + mysql_manager_submit(bg_gtid_delete_pending, NULL); } @@ -712,12 +561,19 @@ int init_slave() init_slave_psi_keys(); #endif - if (start_slave_background_thread()) - return 1; - if (global_rpl_thread_pool.init(opt_slave_parallel_threads)) return 1; + slave_background_thread_gtid_loaded= false; + mysql_manager_submit(bg_rpl_load_gtid_slave_state, NULL); + + // hijacking global_rpl_thread_pool cond here - it's only once on startup + mysql_mutex_lock(&global_rpl_thread_pool.LOCK_rpl_thread_pool); + while (!slave_background_thread_gtid_loaded) + mysql_cond_wait(&global_rpl_thread_pool.COND_rpl_thread_pool, + &global_rpl_thread_pool.LOCK_rpl_thread_pool); + mysql_mutex_unlock(&global_rpl_thread_pool.LOCK_rpl_thread_pool); + /* This is called when mysqld starts. Before client connections are accepted. However bootstrap may conflict with us if it does START SLAVE. @@ -1213,12 +1069,8 @@ terminate_slave_thread(THD *thd, int error __attribute__((unused)); DBUG_PRINT("loop", ("killing slave thread")); -#ifdef WITH_WSREP - /* awake_no_mutex() requires LOCK_thd_data to be locked if wsrep - is enabled */ - if (WSREP(thd)) mysql_mutex_lock(&thd->LOCK_thd_data); -#endif /* WITH_WSREP */ mysql_mutex_lock(&thd->LOCK_thd_kill); + mysql_mutex_lock(&thd->LOCK_thd_data); #ifndef DONT_USE_THR_ALARM /* Error codes from pthread_kill are: @@ -1231,9 +1083,7 @@ terminate_slave_thread(THD *thd, thd->awake_no_mutex(NOT_KILLED); mysql_mutex_unlock(&thd->LOCK_thd_kill); -#ifdef WITH_WSREP - if (WSREP(thd)) mysql_mutex_unlock(&thd->LOCK_thd_data); -#endif /* WITH_WSREP */ + mysql_mutex_unlock(&thd->LOCK_thd_data); /* There is a small chance that slave thread might miss the first @@ -1446,7 +1296,6 @@ void slave_prepare_for_shutdown() // It's safe to destruct worker pool now when // all driver threads are gone. global_rpl_thread_pool.deactivate(); - stop_slave_background_thread(); } /* @@ -1477,8 +1326,6 @@ void end_slave() active_mi= 0; mysql_mutex_unlock(&LOCK_active_mi); - stop_slave_background_thread(); - global_rpl_thread_pool.destroy(); free_all_rpl_filters(); DBUG_VOID_RETURN; @@ -4737,10 +4584,7 @@ pthread_handler_t handle_slave_io(void *arg) goto err; } - -#ifdef WITH_WSREP thd->variables.wsrep_on= 0; -#endif if (DBUG_EVALUATE_IF("failed_slave_start", 1, 0) || repl_semisync_slave.slave_start(mi)) { @@ -5060,8 +4904,11 @@ log space"); err: // print the current replication position if (mi->using_gtid == Master_info::USE_GTID_NO) + { sql_print_information("Slave I/O thread exiting, read up to log '%s', " "position %llu", IO_RPL_LOG_NAME, mi->master_log_pos); + sql_print_information("master was %s:%d", mi->host, mi->port); + } else { StringBuffer<100> tmp; @@ -5070,6 +4917,7 @@ err: "position %llu; GTID position %s", IO_RPL_LOG_NAME, mi->master_log_pos, tmp.c_ptr_safe()); + sql_print_information("master was %s:%d", mi->host, mi->port); } repl_semisync_slave.slave_stop(mi); thd->reset_query(); @@ -5672,6 +5520,7 @@ pthread_handler_t handle_slave_sql(void *arg) sql_print_information("Slave SQL thread exiting, replication stopped in " "log '%s' at position %llu%s", RPL_LOG_NAME, rli->group_master_log_pos, tmp.c_ptr_safe()); + sql_print_information("master was %s:%d", mi->host, mi->port); } #ifdef WITH_WSREP wsrep_after_command_before_result(thd); diff --git a/sql/sp.cc b/sql/sp.cc index 0b553ebf7a1..de1a8a04756 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -2949,7 +2949,7 @@ Sp_handler::show_create_sp(THD *thd, String *buf, buf->append(STRING_WITH_LEN(" RETURN ")); else buf->append(STRING_WITH_LEN(" RETURNS ")); - buf->append(&returns); + buf->append(returns.str, returns.length); // Not \0 terminated } buf->append('\n'); switch (chistics.daccess) { diff --git a/sql/spatial.cc b/sql/spatial.cc index 2b36468e158..bda64c6d420 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2002, 2013, Oracle and/or its affiliates. - Copyright (c) 2011, 2013, Monty Program Ab. + Copyright (c) 2011, 2021, 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 @@ -605,6 +605,7 @@ Geometry *Geometry::create_from_json(Geometry_buffer *buffer, if (feature_type_found) goto handle_geometry_key; } + goto err_return; } else { @@ -1031,6 +1032,127 @@ const Geometry::Class_info *Gis_point::get_class_info() const } +/** + Function to calculate haversine. + Taking as arguments Point and Multipoint geometries. + Multipoint geometry has to be single point only. + It is up to caller to ensure valid input. + + @param g pointer to the Geometry + @param r sphere radius + @param error pointer describing the error in case of the boundary conditions + + @return distance in case without error, it is caclulcated distance (non-negative), + in case error exist, negative value. +*/ +double Gis_point::calculate_haversine(const Geometry *g, + const double sphere_radius, + int *error) +{ + DBUG_ASSERT(sphere_radius > 0); + double x1r, x2r, y1r, y2r; + + // This check is done only for optimization purposes where we know it will + // be one and only one point in Multipoint + if (g->get_class_info()->m_type_id == Geometry::wkb_multipoint) + { + const char point_size= 4 + WKB_HEADER_SIZE + POINT_DATA_SIZE+1; //1 for the type + char point_temp[point_size]; + memset(point_temp+4, Geometry::wkb_point, 1); + memcpy(point_temp+5, static_cast<const Gis_multi_point *>(g)->get_data_ptr()+5, 4); + memcpy(point_temp+4+WKB_HEADER_SIZE, g->get_data_ptr()+4+WKB_HEADER_SIZE, + POINT_DATA_SIZE); + point_temp[point_size-1]= '\0'; + Geometry_buffer gbuff; + Geometry *gg= Geometry::construct(&gbuff, point_temp, point_size-1); + DBUG_ASSERT(gg); + if (static_cast<Gis_point *>(gg)->get_xy_radian(&x2r, &y2r)) + { + DBUG_ASSERT(0); + return -1; + } + } + else + { + if (static_cast<const Gis_point *>(g)->get_xy_radian(&x2r, &y2r)) + { + DBUG_ASSERT(0); + return -1; + } + } + if (this->get_xy_radian(&x1r, &y1r)) + { + DBUG_ASSERT(0); + return -1; + } + // Check boundary conditions: longitude[-180,180] + if (!((x2r >= -M_PI && x2r <= M_PI) && (x1r >= -M_PI && x1r <= M_PI))) + { + *error=1; + return -1; + } + // Check boundary conditions: latitude[-90,90] + if (!((y2r >= -M_PI/2 && y2r <= M_PI/2) && (y1r >= -M_PI/2 && y1r <= M_PI/2))) + { + *error=-1; + return -1; + } + double dlat= sin((y2r - y1r)/2)*sin((y2r - y1r)/2); + double dlong= sin((x2r - x1r)/2)*sin((x2r - x1r)/2); + return 2*sphere_radius*asin((sqrt(dlat + cos(y1r)*cos(y2r)*dlong))); +} + + +/** + Function that calculate spherical distance of Point from Multipoint geometries. + In case there is single point in Multipoint geometries calculate_haversine() + can handle such case. Otherwise, new geometry (Point) has to be constructed. + + @param g pointer to the Geometry + @param r sphere radius + @param result pointer to the result + @param err pointer to the error obtained from calculate_haversin() + + @return state + @retval TRUE failed + @retval FALSE success +*/ +int Gis_point::spherical_distance_multipoints(Geometry *g, const double r, + double *result, int *err) +{ + uint32 num_of_points2; + // To find the minimum radius it cannot be greater than Earth radius + double res= 6370986.0; + double temp_res= 0.0; + const uint32 len= 4 + WKB_HEADER_SIZE + POINT_DATA_SIZE + 1; + char s[len]; + g->num_geometries(&num_of_points2); + DBUG_ASSERT(num_of_points2 >= 1); + if (num_of_points2 == 1) + { + *result= this->calculate_haversine(g, r, err); + return 0; + } + for (uint32 i=1; i <= num_of_points2; i++) + { + Geometry_buffer buff_temp; + Geometry *temp; + + // First 4 bytes are handled already, make sure to create a Point + memset(s + 4, Geometry::wkb_point, 1); + memcpy(s + 5, g->get_data_ptr() + 5, 4); + memcpy(s + 4 + WKB_HEADER_SIZE, g->get_data_ptr() + 4 + WKB_HEADER_SIZE*i +\ + POINT_DATA_SIZE*(i-1), POINT_DATA_SIZE); + s[len-1]= '\0'; + temp= Geometry::construct(&buff_temp, s, len); + DBUG_ASSERT(temp); + temp_res= this->calculate_haversine(temp, r, err); + if (res > temp_res) + res= temp_res; + } + *result= res; + return 0; +} /***************************** LineString *******************************/ uint32 Gis_line_string::get_data_size() const @@ -2162,6 +2284,81 @@ const Geometry::Class_info *Gis_multi_point::get_class_info() const } +/** + Function that calculate spherical distance of Multipoints geometries. + In case there is single point in Multipoint geometries calculate_haversine() + can handle such case. Otherwise, new geometry (Point) has to be constructed. + + @param g pointer to the Geometry + @param r sphere radius + @param result pointer to the result + @param err pointer to the error obtained from calculate_haversin() + + @return state + @retval TRUE failed + @retval FALSE success +*/ +int Gis_multi_point::spherical_distance_multipoints(Geometry *g, const double r, + double *result, int *err) +{ + const uint32 len= 4 + WKB_HEADER_SIZE + POINT_DATA_SIZE + 1; + // Check how many points are stored in Multipoints + uint32 num_of_points1, num_of_points2; + // To find the minimum radius it cannot be greater than Earth radius + double res= 6370986.0; + + /* From Item_func_sphere_distance::spherical_distance_points, + we are sure that there will be multiple points and we have to construct + Point geometry and return the smallest result. + */ + num_geometries(&num_of_points1); + DBUG_ASSERT(num_of_points1 >= 1); + g->num_geometries(&num_of_points2); + DBUG_ASSERT(num_of_points2 >= 1); + + for (uint32 i=1; i <= num_of_points1; i++) + { + Geometry_buffer buff_temp; + Geometry *temp; + double temp_res= 0.0; + char s[len]; + // First 4 bytes are handled already, make sure to create a Point + memset(s + 4, Geometry::wkb_point, 1); + memcpy(s + 5, this->get_data_ptr() + 5, 4); + memcpy(s + 4 + WKB_HEADER_SIZE, this->get_data_ptr() + 4 + WKB_HEADER_SIZE*i +\ + POINT_DATA_SIZE*(i-1), POINT_DATA_SIZE); + s[len-1]= '\0'; + temp= Geometry::construct(&buff_temp, s, len); + DBUG_ASSERT(temp); + // Optimization for single Multipoint + if (num_of_points2 == 1) + { + *result= static_cast<Gis_point *>(temp)->calculate_haversine(g, r, err); + return 0; + } + for (uint32 j=1; j<= num_of_points2; j++) + { + Geometry_buffer buff_temp2; + Geometry *temp2; + char s2[len]; + // First 4 bytes are handled already, make sure to create a Point + memset(s2 + 4, Geometry::wkb_point, 1); + memcpy(s2 + 5, g->get_data_ptr() + 5, 4); + memcpy(s2 + 4 + WKB_HEADER_SIZE, g->get_data_ptr() + 4 + WKB_HEADER_SIZE*j +\ + POINT_DATA_SIZE*(j-1), POINT_DATA_SIZE); + s2[len-1]= '\0'; + temp2= Geometry::construct(&buff_temp2, s2, len); + DBUG_ASSERT(temp2); + temp_res= static_cast<Gis_point *>(temp)->calculate_haversine(temp2, r, err); + if (res > temp_res) + res= temp_res; + } + } + *result= res; + return 0; +} + + /***************************** MultiLineString *******************************/ uint32 Gis_multi_line_string::get_data_size() const diff --git a/sql/spatial.h b/sql/spatial.h index 55f450b1b1b..0c00482c09b 100644 --- a/sql/spatial.h +++ b/sql/spatial.h @@ -332,6 +332,11 @@ public: m_data+= WKB_HEADER_SIZE; } + const char *get_data_ptr() const + { + return m_data; + } + bool envelope(String *result) const; static Class_info *ci_collection[wkb_last+1]; @@ -410,6 +415,17 @@ public: return 0; } + int get_xy_radian(double *x, double *y) const + { + if (!get_xy(x, y)) + { + *x= (*x)*M_PI/180; + *y= (*y)*M_PI/180; + return 0; + } + return 1; + } + int get_x(double *x) const { if (no_data(m_data, SIZEOF_STORED_DOUBLE)) @@ -436,6 +452,10 @@ public: } int store_shapes(Gcalc_shape_transporter *trn) const; const Class_info *get_class_info() const; + double calculate_haversine(const Geometry *g, const double sphere_radius, + int *error); + int spherical_distance_multipoints(Geometry *g, const double r, double *result, + int *error); }; @@ -535,6 +555,8 @@ public: } int store_shapes(Gcalc_shape_transporter *trn) const; const Class_info *get_class_info() const; + int spherical_distance_multipoints(Geometry *g, const double r, double *res, + int *error); }; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index cb2757ba48f..96f1b87d5d7 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3159,6 +3159,12 @@ end: int acl_check_setrole(THD *thd, const char *rolename, ulonglong *access) { + if (!initialized) + { + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables"); + return 1; + } + return check_user_can_set_role(thd, thd->security_ctx->priv_user, thd->security_ctx->host, thd->security_ctx->ip, rolename, access); } @@ -3788,7 +3794,7 @@ bool change_password(THD *thd, LEX_USER *user) char buff[512]; ulong query_length= 0; enum_binlog_format save_binlog_format; - int result=0; + bool result= false, acl_cache_is_locked= false; ACL_USER *acl_user; ACL_USER::AUTH auth; const char *password_plugin= 0; @@ -3813,7 +3819,7 @@ bool change_password(THD *thd, LEX_USER *user) if ((result= tables.open_and_lock(thd, Table_user, TL_WRITE))) DBUG_RETURN(result != 1); - result= 1; + acl_cache_is_locked= 1; mysql_mutex_lock(&acl_cache->lock); if (!(acl_user= find_user_exact(user->host.str, user->user.str))) @@ -3866,7 +3872,7 @@ bool change_password(THD *thd, LEX_USER *user) acl_cache->clear(1); // Clear locked hostname cache mysql_mutex_unlock(&acl_cache->lock); - result= 0; + result= acl_cache_is_locked= 0; if (mysql_bin_log.is_open()) { query_length= sprintf(buff, "SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'", @@ -3877,7 +3883,7 @@ bool change_password(THD *thd, LEX_USER *user) FALSE, FALSE, FALSE, 0) > 0; } end: - if (result) + if (acl_cache_is_locked) mysql_mutex_unlock(&acl_cache->lock); close_mysql_tables(thd); @@ -5342,7 +5348,7 @@ routine_hash_search(const char *host, const char *ip, const char *db, const char *user, const char *tname, const Sp_handler *sph, bool exact) { - return (GRANT_TABLE*) + return (GRANT_NAME*) name_hash_search(sph->get_priv_hash(), host, ip, db, user, tname, exact, TRUE); } @@ -5356,6 +5362,10 @@ table_hash_search(const char *host, const char *ip, const char *db, user, tname, exact, FALSE); } +static bool column_priv_insert(GRANT_TABLE *grant) +{ + return my_hash_insert(&column_priv_hash,(uchar*) grant); +} static GRANT_COLUMN * column_hash_search(GRANT_TABLE *t, const char *cname, size_t length) @@ -5585,6 +5595,15 @@ static inline void get_grantor(THD *thd, char *grantor) strxmov(grantor, user, "@", host, NullS); } + +/** + Revoke rights from a grant table entry. + + @return 0 ok + @return 1 fatal error (error given) + @return -1 grant table was revoked +*/ + static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, TABLE *table, const LEX_USER &combo, const char *db, const char *table_name, @@ -5609,7 +5628,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, { my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH), MYF(0)); /* purecov: deadcode */ - DBUG_RETURN(-1); /* purecov: deadcode */ + DBUG_RETURN(1); /* purecov: deadcode */ } } @@ -5640,7 +5659,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0), combo.user.str, combo.host.str, table_name); /* purecov: deadcode */ - DBUG_RETURN(-1); /* purecov: deadcode */ + DBUG_RETURN(1); /* purecov: deadcode */ } old_row_exists = 0; restore_record(table,record[1]); // Get saved record @@ -5703,13 +5722,14 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, else { my_hash_delete(&column_priv_hash,(uchar*) grant_table); + DBUG_RETURN(-1); // Entry revoked } DBUG_RETURN(0); /* This should never happen */ table_error: table->file->print_error(error,MYF(0)); /* purecov: deadcode */ - DBUG_RETURN(-1); /* purecov: deadcode */ + DBUG_RETURN(1); /* purecov: deadcode */ } @@ -6470,7 +6490,7 @@ static int update_role_table_columns(GRANT_TABLE *merged, privs, cols); merged->init_privs= merged->init_cols= 0; update_role_columns(merged, first, last); - my_hash_insert(&column_priv_hash,(uchar*) merged); + column_priv_insert(merged); return 2; } else if ((privs | cols) == 0) @@ -6790,7 +6810,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, bool revoke_grant) { ulong column_priv= 0; - int result; + int result, res; List_iterator <LEX_USER> str_list (user_list); LEX_USER *Str, *tmp_Str; bool create_new_users=0; @@ -6933,12 +6953,12 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, result= TRUE; continue; } - grant_table = new GRANT_TABLE (Str->host.str, db_name, - Str->user.str, table_name, - rights, - column_priv); + grant_table= new (&grant_memroot) GRANT_TABLE(Str->host.str, db_name, + Str->user.str, table_name, + rights, + column_priv); if (!grant_table || - my_hash_insert(&column_priv_hash,(uchar*) grant_table)) + column_priv_insert(grant_table)) { result= TRUE; /* purecov: deadcode */ continue; /* purecov: deadcode */ @@ -6981,22 +7001,24 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, /* TODO(cvicentiu) refactor replace_table_table to use Tables_priv_table instead of TABLE directly. */ - if (replace_table_table(thd, grant_table, tables.tables_priv_table().table(), - *Str, db_name, table_name, - rights, column_priv, revoke_grant)) - { - /* Should only happen if table is crashed */ - result= TRUE; /* purecov: deadcode */ - } - else if (tables.columns_priv_table().table_exists()) + if (tables.columns_priv_table().table_exists()) { /* TODO(cvicentiu) refactor replace_column_table to use Columns_priv_table instead of TABLE directly. */ if (replace_column_table(grant_table, tables.columns_priv_table().table(), *Str, columns, db_name, table_name, rights, revoke_grant)) - { result= TRUE; + } + if ((res= replace_table_table(thd, grant_table, + tables.tables_priv_table().table(), + *Str, db_name, table_name, + rights, column_priv, revoke_grant))) + { + if (res > 0) + { + /* Should only happen if table is crashed */ + result= TRUE; /* purecov: deadcode */ } } if (Str->is_role()) @@ -7008,9 +7030,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, mysql_mutex_unlock(&acl_cache->lock); if (!result) /* success */ - { result= write_bin_log(thd, TRUE, thd->query(), thd->query_length()); - } mysql_rwlock_unlock(&LOCK_grant); @@ -7698,7 +7718,7 @@ static bool grant_load(THD *thd, if (! mem_check->ok()) delete mem_check; - else if (my_hash_insert(&column_priv_hash,(uchar*) mem_check)) + else if (column_priv_insert(mem_check)) { delete mem_check; goto end_unlock; @@ -8896,6 +8916,16 @@ static bool print_grants_for_role(THD *thd, ACL_ROLE * role) } +static void append_auto_expiration_policy(ACL_USER *acl_user, String *r) { + if (!acl_user->password_lifetime) + r->append(STRING_WITH_LEN(" PASSWORD EXPIRE NEVER")); + else if (acl_user->password_lifetime > 0) + { + r->append(STRING_WITH_LEN(" PASSWORD EXPIRE INTERVAL ")); + r->append_longlong(acl_user->password_lifetime); + r->append(STRING_WITH_LEN(" DAY")); + } +} bool mysql_show_create_user(THD *thd, LEX_USER *lex_user) { @@ -8955,14 +8985,8 @@ bool mysql_show_create_user(THD *thd, LEX_USER *lex_user) if (acl_user->password_expired) result.append(STRING_WITH_LEN(" PASSWORD EXPIRE")); - else if (!acl_user->password_lifetime) - result.append(STRING_WITH_LEN(" PASSWORD EXPIRE NEVER")); - else if (acl_user->password_lifetime > 0) - { - result.append(STRING_WITH_LEN(" PASSWORD EXPIRE INTERVAL ")); - result.append_longlong(acl_user->password_lifetime); - result.append(STRING_WITH_LEN(" DAY")); - } + else + append_auto_expiration_policy(acl_user, &result); protocol->prepare_for_resend(); protocol->store(result.ptr(), result.length(), result.charset()); @@ -8970,6 +8994,28 @@ bool mysql_show_create_user(THD *thd, LEX_USER *lex_user) { error= true; } + + /* MDEV-24114 - PASSWORD EXPIRE and PASSWORD EXPIRE [NEVER | INTERVAL X DAY] + are two different mechanisms. To make sure a tool can restore the state + of a user account, including both the manual expiration state of the + account and the automatic expiration policy attached to it, we should + print two statements here, a CREATE USER (printed above) and an ALTER USER */ + if (acl_user->password_expired && acl_user->password_lifetime > -1) { + result.length(0); + result.append("ALTER USER "); + append_identifier(thd, &result, username, strlen(username)); + result.append('@'); + append_identifier(thd, &result, acl_user->host.hostname, + acl_user->hostname_length); + append_auto_expiration_policy(acl_user, &result); + protocol->prepare_for_resend(); + protocol->store(result.ptr(), result.length(), result.charset()); + if (protocol->write()) + { + error= true; + } + } + my_eof(thd); end: @@ -11068,7 +11114,7 @@ mysql_revoke_sp_privs(THD *thd, Grant_tables *tables, const Sp_handler *sph, bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) { uint counter, revoked; - int result; + int result, res; ACL_DB *acl_db; DBUG_ENTER("mysql_revoke_all"); @@ -11161,36 +11207,35 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) if (!strcmp(lex_user->user.str,user) && !strcmp(lex_user->host.str, host)) { - /* TODO(cvicentiu) refactor replace_db_table to use - Db_table instead of TABLE directly. */ - if (replace_table_table(thd, grant_table, - tables.tables_priv_table().table(), - *lex_user, grant_table->db, - grant_table->tname, ~(ulong)0, 0, 1)) - { + List<LEX_COLUMN> columns; + /* TODO(cvicentiu) refactor replace_db_table to use + Db_table instead of TABLE directly. */ + if (replace_column_table(grant_table, + tables.columns_priv_table().table(), + *lex_user, columns, grant_table->db, + grant_table->tname, ~(ulong)0, 1)) result= -1; - } - else + + /* TODO(cvicentiu) refactor replace_db_table to use + Db_table instead of TABLE directly. */ + if ((res= replace_table_table(thd, grant_table, + tables.tables_priv_table().table(), + *lex_user, grant_table->db, + grant_table->tname, ~(ulong)0, 0, 1))) { - if (!grant_table->cols) - { - revoked= 1; - continue; - } - List<LEX_COLUMN> columns; - /* TODO(cvicentiu) refactor replace_db_table to use - Db_table instead of TABLE directly. */ - if (!replace_column_table(grant_table, - tables.columns_priv_table().table(), - *lex_user, columns, grant_table->db, - grant_table->tname, ~(ulong)0, 1)) - { - revoked= 1; - continue; - } - result= -1; - } - } + if (res > 0) + result= -1; + else + { + /* + Entry was deleted. We have to retry the loop as the + hash table has probably been reorganized. + */ + revoked= 1; + continue; + } + } + } counter++; } } while (revoked); diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index 0a6e76d117f..a96eb58809b 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -447,8 +447,6 @@ dbug_err: */ static bool wsrep_toi_replication(THD *thd, TABLE_LIST *tables) { - if (!WSREP(thd) || !WSREP_CLIENT(thd)) return false; - LEX *lex= thd->lex; /* only handle OPTIMIZE and REPAIR here */ switch (lex->sql_command) @@ -549,10 +547,13 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, for (table= tables; table; table= table->next_local) table->table= NULL; #ifdef WITH_WSREP - if (wsrep_toi_replication(thd, tables)) + if (WSREP(thd)) { - WSREP_INFO("wsrep TOI replication of has failed, skipping OPTIMIZE"); - goto err; + if(wsrep_toi_replication(thd, tables)) + { + WSREP_INFO("wsrep TOI replication of has failed."); + goto err; + } } #endif /* WITH_WSREP */ @@ -1396,7 +1397,9 @@ bool Sql_cmd_analyze_table::execute(THD *thd) /* Presumably, ANALYZE and binlog writing doesn't require synchronization */ + thd->get_stmt_da()->set_overwrite_status(true); res= write_bin_log(thd, TRUE, thd->query(), thd->query_length()); + thd->get_stmt_da()->set_overwrite_status(false); } m_lex->first_select_lex()->table_list.first= first_table; m_lex->query_tables= first_table; @@ -1455,7 +1458,9 @@ bool Sql_cmd_optimize_table::execute(THD *thd) /* Presumably, OPTIMIZE and binlog writing doesn't require synchronization */ + thd->get_stmt_da()->set_overwrite_status(true); res= write_bin_log(thd, TRUE, thd->query(), thd->query_length()); + thd->get_stmt_da()->set_overwrite_status(false); } m_lex->first_select_lex()->table_list.first= first_table; m_lex->query_tables= first_table; @@ -1487,7 +1492,9 @@ bool Sql_cmd_repair_table::execute(THD *thd) /* Presumably, REPAIR and binlog writing doesn't require synchronization */ + thd->get_stmt_da()->set_overwrite_status(true); res= write_bin_log(thd, TRUE, thd->query(), thd->query_length()); + thd->get_stmt_da()->set_overwrite_status(false); } m_lex->first_select_lex()->table_list.first= first_table; m_lex->query_tables= first_table; diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc index 2bbc8169df2..f1a67e7d968 100644 --- a/sql/sql_alter.cc +++ b/sql/sql_alter.cc @@ -471,7 +471,7 @@ bool Sql_cmd_alter_table::execute(THD *thd) if (check_grant(thd, priv_needed, first_table, FALSE, UINT_MAX, FALSE)) DBUG_RETURN(TRUE); /* purecov: inspected */ #ifdef WITH_WSREP - if (WSREP(thd) && WSREP_CLIENT(thd) && + if (WSREP(thd) && (!thd->is_current_stmt_binlog_format_row() || !thd->find_temporary_table(first_table))) { diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 868d76572e3..cc6ecda9327 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -4693,7 +4693,72 @@ add_internal_tables(THD *thd, Query_tables_list *prelocking_ctx, DBUG_RETURN(FALSE); } +/** + Extend the table_list to include foreign tables for prelocking. + + @param[in] thd Thread context. + @param[in] prelocking_ctx Prelocking context of the statement. + @param[in] table_list Table list element for table. + @param[in] sp Routine body. + @param[out] need_prelocking Set to TRUE if method detects that prelocking + required, not changed otherwise. + + @retval FALSE Success. + @retval TRUE Failure (OOM). +*/ +inline bool +prepare_fk_prelocking_list(THD *thd, Query_tables_list *prelocking_ctx, + TABLE_LIST *table_list, bool *need_prelocking, + uint8 op) +{ + DBUG_ENTER("prepare_fk_prelocking_list"); + List <FOREIGN_KEY_INFO> fk_list; + List_iterator<FOREIGN_KEY_INFO> fk_list_it(fk_list); + FOREIGN_KEY_INFO *fk; + Query_arena *arena, backup; + TABLE *table= table_list->table; + + arena= thd->activate_stmt_arena_if_needed(&backup); + table->file->get_parent_foreign_key_list(thd, &fk_list); + if (unlikely(thd->is_error())) + { + if (arena) + thd->restore_active_arena(arena, &backup); + return TRUE; + } + + *need_prelocking= TRUE; + + while ((fk= fk_list_it++)) + { + // FK_OPTION_RESTRICT and FK_OPTION_NO_ACTION only need read access + thr_lock_type lock_type; + + if ((op & (1 << TRG_EVENT_DELETE) && fk_modifies_child(fk->delete_method)) + || (op & (1 << TRG_EVENT_UPDATE) && fk_modifies_child(fk->update_method))) + lock_type= TL_WRITE_ALLOW_WRITE; + else + lock_type= TL_READ; + + if (table_already_fk_prelocked(prelocking_ctx->query_tables, + fk->foreign_db, fk->foreign_table, + lock_type)) + continue; + + TABLE_LIST *tl= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST)); + tl->init_one_table_for_prelocking(fk->foreign_db, + fk->foreign_table, + NULL, lock_type, + TABLE_LIST::PRELOCK_FK, + table_list->belong_to_view, op, + &prelocking_ctx->query_tables_last, + table_list->for_insert_data); + } + if (arena) + thd->restore_active_arena(arena, &backup); + DBUG_RETURN(FALSE); +} /** Defines how prelocking algorithm for DML statements should handle table list @@ -4740,53 +4805,20 @@ handle_table(THD *thd, Query_tables_list *prelocking_ctx, if (table->file->referenced_by_foreign_key()) { - List <FOREIGN_KEY_INFO> fk_list; - List_iterator<FOREIGN_KEY_INFO> fk_list_it(fk_list); - FOREIGN_KEY_INFO *fk; - Query_arena *arena, backup; - - arena= thd->activate_stmt_arena_if_needed(&backup); - - table->file->get_parent_foreign_key_list(thd, &fk_list); - if (unlikely(thd->is_error())) - { - if (arena) - thd->restore_active_arena(arena, &backup); - DBUG_RETURN(TRUE); - } - - *need_prelocking= TRUE; - - while ((fk= fk_list_it++)) - { - // FK_OPTION_RESTRICT and FK_OPTION_NO_ACTION only need read access - uint8 op= table_list->trg_event_map; - thr_lock_type lock_type; - - if ((op & (1 << TRG_EVENT_DELETE) && fk_modifies_child(fk->delete_method)) - || (op & (1 << TRG_EVENT_UPDATE) && fk_modifies_child(fk->update_method))) - lock_type= TL_WRITE_ALLOW_WRITE; - else - lock_type= TL_READ; - - if (table_already_fk_prelocked(prelocking_ctx->query_tables, - fk->foreign_db, fk->foreign_table, - lock_type)) - continue; - - TABLE_LIST *tl= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST)); - tl->init_one_table_for_prelocking(fk->foreign_db, - fk->foreign_table, - NULL, lock_type, - TABLE_LIST::PRELOCK_FK, - table_list->belong_to_view, op, - &prelocking_ctx->query_tables_last, - table_list->for_insert_data); - } - if (arena) - thd->restore_active_arena(arena, &backup); + if (prepare_fk_prelocking_list(thd, prelocking_ctx, table_list, + need_prelocking, + table_list->trg_event_map)) + return TRUE; } } + else if (table_list->slave_fk_event_map && + table->file->referenced_by_foreign_key()) + { + if (prepare_fk_prelocking_list(thd, prelocking_ctx, table_list, + need_prelocking, + table_list->slave_fk_event_map)) + return TRUE; + } /* Open any tables used by DEFAULT (like sequence tables) */ DBUG_PRINT("info", ("table: %p name: %s db: %s flags: %u", @@ -6231,6 +6263,7 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, #ifndef NO_EMBEDDED_ACCESS_CHECKS /* Check if there are sufficient access rights to the found field. */ if (check_privileges && + !table_list->is_derived() && check_column_grant_in_table_ref(thd, *actual_table, name, length, fld)) fld= WRONG_GRANT; else @@ -6453,7 +6486,7 @@ find_field_in_tables(THD *thd, Item_ident *item, if (!all_merged && current_sel != last_select) { mark_select_range_as_dependent(thd, last_select, current_sel, - found, *ref, item); + found, *ref, item, true); } } return found; @@ -7865,11 +7898,15 @@ bool setup_tables(THD *thd, Name_resolution_context *context, DBUG_RETURN(1); } tablenr++; - } - if (tablenr > MAX_TABLES) - { - my_error(ER_TOO_MANY_TABLES,MYF(0), static_cast<int>(MAX_TABLES)); - DBUG_RETURN(1); + /* + We test the max tables here as we setup_table_map() should not be called + with tablenr >= 64 + */ + if (tablenr > MAX_TABLES) + { + my_error(ER_TOO_MANY_TABLES,MYF(0), static_cast<int>(MAX_TABLES)); + DBUG_RETURN(1); + } } } else @@ -7915,7 +7952,8 @@ bool setup_tables(THD *thd, Name_resolution_context *context, if (table_list->jtbm_subselect) { Item *item= table_list->jtbm_subselect->optimizer; - if (table_list->jtbm_subselect->optimizer->fix_fields(thd, &item)) + if (!table_list->jtbm_subselect->optimizer->fixed && + table_list->jtbm_subselect->optimizer->fix_fields(thd, &item)) { my_error(ER_TOO_MANY_TABLES,MYF(0), static_cast<int>(MAX_TABLES)); /* psergey-todo: WHY ER_TOO_MANY_TABLES ???*/ DBUG_RETURN(1); @@ -8092,36 +8130,23 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, #ifndef NO_EMBEDDED_ACCESS_CHECKS /* - Ensure that we have access rights to all fields to be inserted. Under - some circumstances, this check may be skipped. - - - If any_privileges is true, skip the check. + Ensure that we have access rights to all fields to be inserted + the table 'tables'. Under some circumstances, this check may be skipped. - - If the SELECT privilege has been found as fulfilled already for both - the TABLE and TABLE_LIST objects (and both of these exist, of - course), the check is skipped. + The check is skipped in the following cases: - - If the SELECT privilege has been found fulfilled for the TABLE object - and the TABLE_LIST represents a derived table other than a view (see - below), the check is skipped. + - any_privileges is true - - If the TABLE_LIST object represents a view, we may skip checking if - the SELECT privilege has been found fulfilled for it, regardless of - the TABLE object. + - the table is a derived table - - If there is no TABLE object, the test is skipped if either - * the TABLE_LIST does not represent a view, or - * the SELECT privilege has been found fulfilled. + - the table is a view with SELECT privilege - A TABLE_LIST that is not a view may be a subquery, an - information_schema table, or a nested table reference. See the comment - for TABLE_LIST. + - the table is a base table with SELECT privilege */ - if (!((table && tables->is_non_derived() && - (table->grant.privilege & SELECT_ACL)) || - ((!tables->is_non_derived() && - (tables->grant.privilege & SELECT_ACL)))) && - !any_privileges) + if (!any_privileges && + !tables->is_derived() && + !(tables->is_view() && (tables->grant.privilege & SELECT_ACL)) && + !(table && (table->grant.privilege & SELECT_ACL))) { field_iterator.set(tables); if (check_grant_all_columns(thd, SELECT_ACL, &field_iterator)) diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 0ba4732b6cd..bef3318f974 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1449,7 +1449,7 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) DBUG_PRINT("qcache", ("\ long %d, 4.1: %d, eof: %d, bin_proto: %d, more results %d, pkt_nr: %d, \ CS client: %u, CS result: %u, CS conn: %u, limit: %llu, TZ: %p, \ -sql mode: 0x%llx, sort len: %llu, conncat len: %llu, div_precision: %zu, \ +sql mode: 0x%llx, sort len: %llu, concat len: %u, div_precision: %zu, \ def_week_frmt: %zu, in_trans: %d, autocommit: %d", (int)flags.client_long_flag, (int)flags.client_protocol_41, @@ -1949,7 +1949,7 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length) DBUG_PRINT("qcache", ("\ long %d, 4.1: %d, eof: %d, bin_proto: %d, more results %d, pkt_nr: %d, \ CS client: %u, CS result: %u, CS conn: %u, limit: %llu, TZ: %p, \ -sql mode: 0x%llx, sort len: %llu, conncat len: %llu, div_precision: %zu, \ +sql mode: 0x%llx, sort len: %llu, concat len: %u, div_precision: %zu, \ def_week_frmt: %zu, in_trans: %d, autocommit: %d", (int)flags.client_long_flag, (int)flags.client_protocol_41, diff --git a/sql/sql_cache.h b/sql/sql_cache.h index 92635ecacc7..d59bc37b7a3 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -558,11 +558,11 @@ struct Query_cache_query_flags uint character_set_client_num; uint character_set_results_num; uint collation_connection_num; + uint group_concat_max_len; ha_rows limit; Time_zone *time_zone; sql_mode_t sql_mode; ulonglong max_sort_length; - ulonglong group_concat_max_len; size_t default_week_format; size_t div_precision_increment; MY_LOCALE *lc_time_names; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index ceb8dc1ade8..093a94f44f8 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, 2020, MariaDB Corporation. + Copyright (c) 2008, 2021, 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 @@ -49,9 +49,6 @@ #include <m_ctype.h> #include <sys/stat.h> #include <thr_alarm.h> -#ifdef __WIN__0 -#include <io.h> -#endif #include <mysys_err.h> #include <limits.h> @@ -70,6 +67,8 @@ #ifdef WITH_WSREP #include "wsrep_thd.h" #include "wsrep_trans_observer.h" +#else +static inline bool wsrep_is_bf_aborted(THD* thd) { return false; } #endif /* WITH_WSREP */ #include "opt_trace.h" @@ -450,6 +449,7 @@ void thd_set_ha_data(THD *thd, const struct handlerton *hton, const void *ha_data) { plugin_ref *lock= &thd->ha_data[hton->slot].lock; + DBUG_ASSERT(thd == current_thd); if (ha_data && !*lock) *lock= ha_lock_engine(NULL, (handlerton*) hton); else if (!ha_data && *lock) @@ -457,7 +457,9 @@ void thd_set_ha_data(THD *thd, const struct handlerton *hton, plugin_unlock(NULL, *lock); *lock= NULL; } + mysql_mutex_lock(&thd->LOCK_thd_data); *thd_ha_data(thd, hton)= (void*) ha_data; + mysql_mutex_unlock(&thd->LOCK_thd_data); } @@ -782,7 +784,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) net.reading_or_writing= 0; client_capabilities= 0; // minimalistic client system_thread= NON_SYSTEM_THREAD; - cleanup_done= free_connection_done= abort_on_warning= 0; + cleanup_done= free_connection_done= abort_on_warning= got_warning= 0; peer_port= 0; // For SHOW PROCESSLIST transaction.m_pending_rows_event= 0; transaction.on= 1; @@ -797,6 +799,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) mysql_mutex_init(key_LOCK_wakeup_ready, &LOCK_wakeup_ready, MY_MUTEX_INIT_FAST); mysql_mutex_init(key_LOCK_thd_kill, &LOCK_thd_kill, MY_MUTEX_INIT_FAST); mysql_cond_init(key_COND_wakeup_ready, &COND_wakeup_ready, 0); + mysql_mutex_record_order(&LOCK_thd_kill, &LOCK_thd_data); /* Variables with default values */ proc_info="login"; @@ -1520,9 +1523,7 @@ void THD::cleanup(void) set_killed(KILL_CONNECTION); #ifdef WITH_WSREP if (wsrep_cs().state() != wsrep::client_state::s_none) - { wsrep_cs().cleanup(); - } wsrep_client_thread= false; #endif /* WITH_WSREP */ @@ -1598,12 +1599,12 @@ void THD::cleanup(void) void THD::free_connection() { DBUG_ASSERT(free_connection_done == 0); - my_free((char*) db.str); + my_free(const_cast<char*>(db.str)); db= null_clex_str; #ifndef EMBEDDED_LIBRARY if (net.vio) vio_delete(net.vio); - net.vio= 0; + net.vio= nullptr; net_end(&net); #endif if (!cleanup_done) @@ -1676,19 +1677,16 @@ THD::~THD() THD is not deleted while they access it. The following mutex_lock ensures that no one else is using this THD and it's now safe to delete */ - if (WSREP_NNULL(this)) mysql_mutex_lock(&LOCK_thd_data); mysql_mutex_lock(&LOCK_thd_kill); mysql_mutex_unlock(&LOCK_thd_kill); - if (WSREP_NNULL(this)) mysql_mutex_unlock(&LOCK_thd_data); +#ifdef WITH_WSREP + delete wsrep_rgi; +#endif if (!free_connection_done) free_connection(); #ifdef WITH_WSREP - if (wsrep_rgi != NULL) { - delete wsrep_rgi; - wsrep_rgi = NULL; - } mysql_cond_destroy(&COND_wsrep_thd); #endif mdl_context.destroy(); @@ -1871,7 +1869,7 @@ void THD::awake_no_mutex(killed_state state_to_set) DBUG_PRINT("enter", ("this: %p current_thd: %p state: %d", this, current_thd, (int) state_to_set)); THD_CHECK_SENTRY(this); - if (WSREP_NNULL(this)) mysql_mutex_assert_owner(&LOCK_thd_data); + mysql_mutex_assert_owner(&LOCK_thd_data); mysql_mutex_assert_owner(&LOCK_thd_kill); print_aborted_warning(3, "KILLED"); @@ -1904,15 +1902,21 @@ void THD::awake_no_mutex(killed_state state_to_set) } /* Interrupt target waiting inside a storage engine. */ - if (IF_WSREP(state_to_set != NOT_KILLED && !wsrep_is_bf_aborted(this), - state_to_set != NOT_KILLED)) + if (state_to_set != NOT_KILLED && !wsrep_is_bf_aborted(this)) ha_kill_query(this, thd_kill_level(this)); - /* Broadcast a condition to kick the target if it is waiting on it. */ + abort_current_cond_wait(false); + DBUG_VOID_RETURN; +} + +/* Broadcast a condition to kick the target if it is waiting on it. */ +void THD::abort_current_cond_wait(bool force) +{ + mysql_mutex_assert_owner(&LOCK_thd_kill); if (mysys_var) { mysql_mutex_lock(&mysys_var->mutex); - if (!system_thread) // Don't abort locks + if (!system_thread || force) // Don't abort locks mysys_var->abort=1; /* @@ -1970,7 +1974,6 @@ void THD::awake_no_mutex(killed_state state_to_set) } mysql_mutex_unlock(&mysys_var->mutex); } - DBUG_VOID_RETURN; } @@ -2024,16 +2027,7 @@ bool THD::notify_shared_lock(MDL_context_owner *ctx_in_use, mysql_mutex_lock(&in_use->LOCK_thd_kill); if (in_use->killed < KILL_CONNECTION) in_use->set_killed_no_mutex(KILL_CONNECTION); - if (in_use->mysys_var) - { - mysql_mutex_lock(&in_use->mysys_var->mutex); - if (in_use->mysys_var->current_cond) - mysql_cond_broadcast(in_use->mysys_var->current_cond); - - /* Abort if about to wait in thr_upgrade_write_delay_lock */ - in_use->mysys_var->abort= 1; - mysql_mutex_unlock(&in_use->mysys_var->mutex); - } + in_use->abort_current_cond_wait(true); mysql_mutex_unlock(&in_use->LOCK_thd_kill); signalled= TRUE; } @@ -3142,12 +3136,12 @@ static File create_file(THD *thd, char *path, sql_exchange *exchange, } /* Create the file world readable */ if ((file= mysql_file_create(key_select_to_file, - path, 0666, O_WRONLY|O_EXCL, MYF(MY_WME))) < 0) + path, 0644, O_WRONLY|O_EXCL, MYF(MY_WME))) < 0) return file; #ifdef HAVE_FCHMOD - (void) fchmod(file, 0666); // Because of umask() + (void) fchmod(file, 0644); // Because of umask() #else - (void) chmod(path, 0666); + (void) chmod(path, 0644); #endif if (init_io_cache(cache, file, 0L, WRITE_CACHE, 0L, 1, MYF(MY_WME))) { @@ -5062,6 +5056,18 @@ thd_need_ordering_with(const MYSQL_THD thd, const MYSQL_THD other_thd) DBUG_EXECUTE_IF("disable_thd_need_ordering_with", return 1;); if (!thd || !other_thd) return 1; +#ifdef WITH_WSREP + /* wsrep applier, replayer and TOI processing threads are ordered + by replication provider, relaxed GAP locking protocol can be used + between high priority wsrep threads. + Note that wsrep_thd_is_BF() doesn't take LOCK_thd_data for either thd, + the caller should guarantee that the BF state won't change. + (e.g. InnoDB does it by keeping lock_sys.mutex locked) + */ + if (WSREP_ON && wsrep_thd_is_BF(thd, false) && + wsrep_thd_is_BF(other_thd, false)) + return 0; +#endif /* WITH_WSREP */ rgi= thd->rgi_slave; other_rgi= other_thd->rgi_slave; if (!rgi || !other_rgi) diff --git a/sql/sql_class.h b/sql/sql_class.h index 6b3c1594e60..d33cd1b35a4 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -592,7 +592,6 @@ typedef struct system_variables ulonglong bulk_insert_buff_size; ulonglong join_buff_size; ulonglong sortbuff_size; - ulonglong group_concat_max_len; ulonglong default_regex_flags; ulonglong max_mem_used; @@ -685,6 +684,8 @@ typedef struct system_variables uint32 gtid_domain_id; uint64 gtid_seq_no; + uint group_concat_max_len; + /** Default transaction access mode. READ ONLY (true) or READ WRITE (false). */ @@ -932,11 +933,24 @@ void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var); void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var, STATUS_VAR *dec_var); +uint calc_sum_of_all_status(STATUS_VAR *to); +static inline void calc_sum_of_all_status_if_needed(STATUS_VAR *to) +{ + if (to->local_memory_used == 0) + { + mysql_mutex_lock(&LOCK_status); + *to= global_status_var; + mysql_mutex_unlock(&LOCK_status); + calc_sum_of_all_status(to); + DBUG_ASSERT(to->local_memory_used); + } +} + /* Update global_memory_used. We have to do this with atomic_add as the global value can change outside of LOCK_status. */ -inline void update_global_memory_status(int64 size) +static inline void update_global_memory_status(int64 size) { DBUG_PRINT("info", ("global memory_used: %lld size: %lld", (longlong) global_status_var.global_memory_used, @@ -954,7 +968,7 @@ inline void update_global_memory_status(int64 size) @retval NULL on error @retval Pointter to CHARSET_INFO with the given name on success */ -inline CHARSET_INFO * +static inline CHARSET_INFO * mysqld_collation_get_by_name(const char *name, CHARSET_INFO *name_cs= system_charset_info) { @@ -973,7 +987,7 @@ mysqld_collation_get_by_name(const char *name, return cs; } -inline bool is_supported_parser_charset(CHARSET_INFO *cs) +static inline bool is_supported_parser_charset(CHARSET_INFO *cs) { return MY_TEST(cs->mbminlen == 1); } @@ -2267,7 +2281,7 @@ public: - mysys_var (used by KILL statement and shutdown). - Also ensures that THD is not deleted while mutex is hold */ - mysql_mutex_t LOCK_thd_kill; + mutable mysql_mutex_t LOCK_thd_kill; /* all prepared statements and cursors of this connection */ Statement_map stmt_map; @@ -3308,19 +3322,13 @@ public: void awake_no_mutex(killed_state state_to_set); void awake(killed_state state_to_set) { - bool wsrep_on_local= variables.wsrep_on; - /* - mutex locking order (LOCK_thd_data - LOCK_thd_kill)) requires - to grab LOCK_thd_data here - */ - if (wsrep_on_local) - mysql_mutex_lock(&LOCK_thd_data); mysql_mutex_lock(&LOCK_thd_kill); + mysql_mutex_lock(&LOCK_thd_data); awake_no_mutex(state_to_set); + mysql_mutex_unlock(&LOCK_thd_data); mysql_mutex_unlock(&LOCK_thd_kill); - if (wsrep_on_local) - mysql_mutex_unlock(&LOCK_thd_data); } + void abort_current_cond_wait(bool force); /** Disconnect the associated communication endpoint. */ void disconnect(); @@ -4060,8 +4068,7 @@ public: mysql_mutex_lock(&LOCK_thd_kill); int err= killed_errno(); if (err) - my_message(err, killed_err ? killed_err->msg : ER_THD(this, err), - MYF(0)); + my_message(err, killed_err ? killed_err->msg : ER_THD(this, err), MYF(0)); mysql_mutex_unlock(&LOCK_thd_kill); } /* return TRUE if we will abort query if we make a warning now */ @@ -5787,10 +5794,15 @@ class select_union_recursive :public select_unit public: /* The temporary table with the new records generated by one iterative step */ TABLE *incr_table; + /* The TMP_TABLE_PARAM structure used to create incr_table */ + TMP_TABLE_PARAM incr_table_param; /* One of tables from the list rec_tables (determined dynamically) */ TABLE *first_rec_table_to_update; - /* The temporary tables used for recursive table references */ - List<TABLE> rec_tables; + /* + The list of all recursive table references to the CTE for whose + specification this select_union_recursive was created + */ + List<TABLE_LIST> rec_table_refs; /* The count of how many times cleanup() was called with cleaned==false for the unit specifying the recursive CTE for which this object was created @@ -5800,7 +5812,8 @@ class select_union_recursive :public select_unit select_union_recursive(THD *thd_arg): select_unit(thd_arg), - incr_table(0), first_rec_table_to_update(0), cleanup_count(0) {}; + incr_table(0), first_rec_table_to_update(0), cleanup_count(0) + { incr_table_param.init(); }; int send_data(List<Item> &items); bool create_result_table(THD *thd, List<Item> *column_types, @@ -6035,11 +6048,13 @@ public: - The sj-materialization temporary table - Members needed to make index lookup or a full scan of the temptable. */ +class POSITION; + class SJ_MATERIALIZATION_INFO : public Sql_alloc { public: /* Optimal join sub-order */ - struct st_position *positions; + POSITION *positions; uint tables; /* Number of tables in the sj-nest */ @@ -6095,8 +6110,6 @@ struct SORT_FIELD_ATTR { uint length; /* Length of sort field */ uint suffix_length; /* Length suffix (0-4) */ - enum Type { FIXED_SIZE, VARIABLE_SIZE } type; - bool is_variable_sized() { return type == VARIABLE_SIZE; } }; @@ -6254,7 +6267,8 @@ public: class multi_update :public select_result_interceptor { TABLE_LIST *all_tables; /* query/update command tables */ - List<TABLE_LIST> *leaves; /* list of leves of join table tree */ + List<TABLE_LIST> *leaves; /* list of leaves of join table tree */ + List<TABLE_LIST> updated_leaves; /* list of of updated leaves */ TABLE_LIST *update_tables; TABLE **tmp_tables, *main_table, *table_to_update; TMP_TABLE_PARAM *tmp_table_param; @@ -6292,6 +6306,7 @@ public: List<Item> *fields, List<Item> *values, enum_duplicates handle_duplicates, bool ignore); ~multi_update(); + bool init(THD *thd); int prepare(List<Item> &list, SELECT_LEX_UNIT *u); int send_data(List<Item> &items); bool initialize_tables (JOIN *join); @@ -6735,6 +6750,22 @@ class Sql_mode_save sql_mode_t old_mode; // SQL mode saved at construction time. }; +class Abort_on_warning_instant_set +{ + THD *m_thd; + bool m_save_abort_on_warning; +public: + Abort_on_warning_instant_set(THD *thd, bool temporary_value) + :m_thd(thd), m_save_abort_on_warning(thd->abort_on_warning) + { + thd->abort_on_warning= temporary_value; + } + ~Abort_on_warning_instant_set() + { + m_thd->abort_on_warning= m_save_abort_on_warning; + } +}; + class Switch_to_definer_security_ctx { public: diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 643b7ee898a..4d09dab392b 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -92,7 +92,6 @@ int get_or_create_user_conn(THD *thd, const char *user, uc->host= uc->user + user_len + 1; uc->len= (uint)temp_len; uc->connections= uc->questions= uc->updates= uc->conn_per_hour= 0; - uc->user_resources= *mqh; uc->reset_utime= thd->thr_create_utime; if (my_hash_insert(&hash_user_connections, (uchar*) uc)) { @@ -102,6 +101,7 @@ int get_or_create_user_conn(THD *thd, const char *user, goto end; } } + uc->user_resources= *mqh; thd->user_connect=uc; uc->connections++; end: diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc index d4b6d815118..f3861ebd3e9 100644 --- a/sql/sql_cte.cc +++ b/sql/sql_cte.cc @@ -252,6 +252,8 @@ With_element *With_clause::find_table_def(TABLE_LIST *table, !table->is_fqtn) { table->set_derived(); + table->db.str= empty_c_string; + table->db.length= 0; return with_elem; } } @@ -887,8 +889,6 @@ st_select_lex_unit *With_element::clone_parsed_spec(THD *thd, goto err; spec_tables_tail= tbl; } - if (check_table_access(thd, SELECT_ACL, spec_tables, FALSE, UINT_MAX, FALSE)) - goto err; if (spec_tables) { if (with_table->next_global) @@ -904,6 +904,7 @@ st_select_lex_unit *With_element::clone_parsed_spec(THD *thd, with_table->next_global= spec_tables; } res= &lex->unit; + res->with_element= this; lex->unit.include_down(with_table->select_lex); lex->unit.set_slave(with_select); @@ -914,6 +915,22 @@ st_select_lex_unit *With_element::clone_parsed_spec(THD *thd, with_select)); if (check_dependencies_in_with_clauses(lex->with_clauses_list)) res= NULL; + /* + Resolve references to CTE from the spec_tables list that has not + been resolved yet. + */ + for (TABLE_LIST *tbl= spec_tables; + tbl; + tbl= tbl->next_global) + { + if (!tbl->with) + tbl->with= with_select->find_table_def_in_with_clauses(tbl); + if (tbl == spec_tables_tail) + break; + } + if (check_table_access(thd, SELECT_ACL, spec_tables, FALSE, UINT_MAX, FALSE)) + goto err; + lex->sphead= NULL; // in order not to delete lex->sphead lex_end(lex); err: @@ -1466,10 +1483,11 @@ void With_element::print(String *str, enum_query_type query_type) bool With_element::instantiate_tmp_tables() { - List_iterator_fast<TABLE> li(rec_result->rec_tables); - TABLE *rec_table; - while ((rec_table= li++)) + List_iterator_fast<TABLE_LIST> li(rec_result->rec_table_refs); + TABLE_LIST *rec_tbl; + while ((rec_tbl= li++)) { + TABLE *rec_table= rec_tbl->table; if (!rec_table->is_created() && instantiate_tmp_table(rec_table, rec_table->s->key_info, diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 5d006a7518c..9fa1e015274 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2000, 2019, Oracle and/or its affiliates. - Copyright (c) 2010, 2019, MariaDB + Copyright (c) 2010, 2021, 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 @@ -293,7 +293,15 @@ int TABLE::delete_row() store_record(this, record[1]); vers_update_end(); - return file->ha_update_row(record[1], record[0]); + int err= file->ha_update_row(record[1], record[0]); + /* + MDEV-23644: we get HA_ERR_FOREIGN_DUPLICATE_KEY iff we already got history + row with same trx_id which is the result of foreign key action, so we + don't need one more history row. + */ + if (err == HA_ERR_FOREIGN_DUPLICATE_KEY) + return file->ha_delete_row(record[0]); + return err; } @@ -1120,14 +1128,11 @@ int mysql_multi_delete_prepare(THD *thd) FALSE, DELETE_ACL, SELECT_ACL, FALSE)) DBUG_RETURN(TRUE); - if (lex->first_select_lex()->handle_derived(thd->lex, DT_MERGE)) - DBUG_RETURN(TRUE); - /* Multi-delete can't be constructed over-union => we always have single SELECT on top and have to check underlying SELECTs of it */ - lex->first_select_lex()->exclude_from_table_unique_test= TRUE; + lex->first_select_lex()->set_unique_exclude(); /* Fix tables-to-be-deleted-from list to point at opened tables */ for (target_tbl= (TABLE_LIST*) aux_tables; target_tbl; @@ -1150,6 +1155,12 @@ int mysql_multi_delete_prepare(THD *thd) target_tbl->table_name.str, "DELETE"); DBUG_RETURN(TRUE); } + } + + for (target_tbl= (TABLE_LIST*) aux_tables; + target_tbl; + target_tbl= target_tbl->next_local) + { /* Check that table from which we delete is not used somewhere inside subqueries/view. @@ -1194,12 +1205,6 @@ multi_delete::prepare(List<Item> &values, SELECT_LEX_UNIT *u) unit= u; do_delete= 1; THD_STAGE_INFO(thd, stage_deleting_from_main_table); - SELECT_LEX *select_lex= u->first_select(); - if (select_lex->first_cond_optimization) - { - if (select_lex->handle_derived(thd->lex, DT_MERGE)) - DBUG_RETURN(TRUE); - } DBUG_RETURN(0); } diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 1e416c307cf..132872c4a9e 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2002, 2011, Oracle and/or its affiliates. - Copyright (c) 2010, 2020, MariaDB + Copyright (c) 2010, 2021, 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 @@ -382,10 +382,6 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived) DBUG_RETURN(FALSE); } - if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI || - thd->lex->sql_command == SQLCOM_DELETE_MULTI) - thd->save_prep_leaf_list= TRUE; - arena= thd->activate_stmt_arena_if_needed(&backup); // For easier test if (!derived->merged_for_insert || @@ -459,6 +455,7 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived) derived->on_expr= expr; derived->prep_on_expr= expr->copy_andor_structure(thd); } + thd->where= "on clause"; if (derived->on_expr && derived->on_expr->fix_fields_if_needed_for_bool(thd, &derived->on_expr)) { @@ -596,6 +593,32 @@ bool mysql_derived_init(THD *thd, LEX *lex, TABLE_LIST *derived) } +/** + @brief + Prevent name resolution out of context of ON expressions in derived tables + + @param + join_list list of tables used in from list of a derived + + @details + The function sets the Name_resolution_context::outer_context to NULL + for all ON expressions contexts in the given join list. It does this + recursively for all nested joins the list contains. +*/ + +static void nullify_outer_context_for_on_clauses(List<TABLE_LIST>& join_list) +{ + List_iterator<TABLE_LIST> li(join_list); + while (TABLE_LIST *table= li++) + { + if (table->on_context) + table->on_context->outer_context= NULL; + if (table->nested_join) + nullify_outer_context_for_on_clauses(table->nested_join->join_list); + } +} + + /* Create temporary table structure (but do not fill it) @@ -710,7 +733,7 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) if (derived->is_with_table_recursive_reference()) { /* Here 'derived" is a secondary recursive table reference */ - unit->with_element->rec_result->rec_tables.push_back(derived->table); + unit->with_element->rec_result->rec_table_refs.push_back(derived); } } DBUG_ASSERT(derived->table || res); @@ -760,7 +783,12 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) /* prevent name resolving out of derived table */ for (SELECT_LEX *sl= first_select; sl; sl= sl->next_select()) { + // Prevent it for the WHERE clause sl->context.outer_context= 0; + + // And for ON clauses, if there are any + nullify_outer_context_for_on_clauses(*sl->join_list); + if (!derived->is_with_table_recursive_reference() || (!derived->with->with_anchor && !derived->with->is_with_prepared_anchor())) @@ -808,17 +836,17 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) derived->fill_me= FALSE; - if (!(derived->derived_result= new (thd->mem_root) select_unit(thd))) + if ((!derived->is_with_table_recursive_reference() || + !derived->derived_result) && + !(derived->derived_result= new (thd->mem_root) select_unit(thd))) DBUG_RETURN(TRUE); // out of memory - lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED; // st_select_lex_unit::prepare correctly work for single select if ((res= unit->prepare(derived, derived->derived_result, 0))) goto exit; if (derived->with && (res= derived->with->rename_columns_of_derived_unit(thd, unit))) goto exit; - lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED; if ((res= check_duplicate_names(thd, unit->types, 0))) goto exit; @@ -827,7 +855,8 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) Depending on the result field translation will or will not be created. */ - if (derived->init_derived(thd, FALSE)) + if (!derived->is_with_table_recursive_reference() && + derived->init_derived(thd, FALSE)) goto exit; /* @@ -1452,6 +1481,8 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived) for (; sl; sl= sl->next_select()) { Item *extracted_cond_copy; + if (!sl->cond_pushdown_is_allowed()) + continue; /* For each select of the unit except the last one create a clone of extracted_cond diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 12119997430..8447d5bea7d 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -687,7 +687,6 @@ mysql_ha_fix_cond_and_key(SQL_HANDLER *handler, for (keypart_map= key_len=0 ; (item=it_ke++) ; key_part++) { - my_bitmap_map *old_map; /* note that 'item' can be changed by fix_fields() call */ if (item->fix_fields_if_needed_for_scalar(thd, it_ke.ref())) return 1; @@ -699,9 +698,9 @@ mysql_ha_fix_cond_and_key(SQL_HANDLER *handler, } if (!in_prepare) { - old_map= dbug_tmp_use_all_columns(table, table->write_set); + MY_BITMAP *old_map= dbug_tmp_use_all_columns(table, &table->write_set); (void) item->save_in_field(key_part->field, 1); - dbug_tmp_restore_column_map(table->write_set, old_map); + dbug_tmp_restore_column_map(&table->write_set, old_map); } key_len+= key_part->store_length; keypart_map= (keypart_map << 1) | 1; diff --git a/sql/sql_help.cc b/sql/sql_help.cc index e5f1e958d99..81e5ad48e7e 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -742,6 +742,9 @@ static bool mysqld_help_internal(THD *thd, const char *mask) &name, &description, &example); delete select; + if (thd->is_error()) + goto error; + if (count_topics == 0) { int UNINIT_VAR(key_id); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 01e5752def7..c969725bea4 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1981,6 +1981,8 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) if (likely(!error)) { info->deleted++; + if (!table->file->has_transactions()) + thd->transaction.stmt.modified_non_trans_table= TRUE; if (table->versioned(VERS_TIMESTAMP)) { store_record(table, record[2]); @@ -2701,7 +2703,7 @@ int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic, delayed_row *row= 0; Delayed_insert *di=thd->di; const Discrete_interval *forced_auto_inc; - size_t user_len, host_len, ip_len; + size_t user_len, host_len, ip_length; DBUG_ENTER("write_delayed"); DBUG_PRINT("enter", ("query = '%s' length %lu", query.str, (ulong) query.length)); @@ -2735,7 +2737,7 @@ int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic, goto err; } - user_len= host_len= ip_len= 0; + user_len= host_len= ip_length= 0; row->user= row->host= row->ip= NULL; if (thd->security_ctx) { @@ -2744,11 +2746,11 @@ int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic, if (thd->security_ctx->host) host_len= strlen(thd->security_ctx->host) + 1; if (thd->security_ctx->ip) - ip_len= strlen(thd->security_ctx->ip) + 1; + ip_length= strlen(thd->security_ctx->ip) + 1; } /* This can't be THREAD_SPECIFIC as it's freed in delayed thread */ if (!(row->record= (char*) my_malloc(table->s->reclength + - user_len + host_len + ip_len, + user_len + host_len + ip_length, MYF(MY_WME)))) goto err; memcpy(row->record, table->record[0], table->s->reclength); @@ -2768,7 +2770,7 @@ int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic, if (thd->security_ctx->ip) { row->ip= row->record + table->s->reclength + user_len + host_len; - memcpy(row->ip, thd->security_ctx->ip, ip_len); + memcpy(row->ip, thd->security_ctx->ip, ip_length); } } row->query_id= thd->query_id; @@ -2867,23 +2869,7 @@ void kill_delayed_threads(void) mysql_mutex_lock(&di->thd.LOCK_thd_kill); if (di->thd.killed < KILL_CONNECTION) di->thd.set_killed_no_mutex(KILL_CONNECTION); - if (di->thd.mysys_var) - { - mysql_mutex_lock(&di->thd.mysys_var->mutex); - if (di->thd.mysys_var->current_cond) - { - /* - We need the following test because the main mutex may be locked - in handle_delayed_insert() - */ - if (&di->mutex != di->thd.mysys_var->current_mutex) - mysql_mutex_lock(di->thd.mysys_var->current_mutex); - mysql_cond_broadcast(di->thd.mysys_var->current_cond); - if (&di->mutex != di->thd.mysys_var->current_mutex) - mysql_mutex_unlock(di->thd.mysys_var->current_mutex); - } - mysql_mutex_unlock(&di->thd.mysys_var->mutex); - } + di->thd.abort_current_cond_wait(false); mysql_mutex_unlock(&di->thd.LOCK_thd_kill); } mysql_mutex_unlock(&LOCK_delayed_insert); // For unlink from list @@ -4738,7 +4724,8 @@ bool select_create::send_eof() if (!table->s->tmp_table) { #ifdef WITH_WSREP - if (WSREP(thd)) + if (WSREP(thd) && + table->file->ht->db_type == DB_TYPE_INNODB) { if (thd->wsrep_trx_id() == WSREP_UNDEFINED_TRX_ID) { diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index 20ed976daab..f072a675e31 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -1200,7 +1200,7 @@ bool JOIN_CACHE::check_emb_key_usage() Item *item= ref->items[i]->real_item(); Field *fld= ((Item_field *) item)->field; CACHE_FIELD *init_copy= field_descr+flag_fields+i; - for (j= i, copy= init_copy; i < local_key_arg_fields; i++, copy++) + for (j= i, copy= init_copy; j < local_key_arg_fields; j++, copy++) { if (fld->eq(copy->field)) { @@ -1650,7 +1650,7 @@ void JOIN_CACHE::get_record_by_pos(uchar *rec_ptr) } -/* +/* Get the match flag from the referenced record: the default implementation SYNOPSIS @@ -1662,6 +1662,7 @@ void JOIN_CACHE::get_record_by_pos(uchar *rec_ptr) get the match flag for the record pointed by the reference at the position rec_ptr. If the match flag is placed in one of the previous buffers the function first reaches the linked record fields in this buffer. + The function returns the value of the first encountered match flag. RETURN VALUE match flag for the record at the position rec_ptr @@ -1686,6 +1687,39 @@ enum JOIN_CACHE::Match_flag JOIN_CACHE::get_match_flag_by_pos(uchar *rec_ptr) /* + Get the match flag for the referenced record from specified join buffer + + SYNOPSIS + get_match_flag_by_pos_from_join_buffer() + rec_ptr position of the first field of the record in the join buffer + tab join table with join buffer where to look for the match flag + + DESCRIPTION + This default implementation of the get_match_flag_by_pos_from_join_buffer + method gets the match flag for the record pointed by the reference at the + position rec_ptr from the join buffer attached to the join table tab. + + RETURN VALUE + match flag for the record at the position rec_ptr from the join + buffer attached to the table tab. +*/ + +enum JOIN_CACHE::Match_flag +JOIN_CACHE::get_match_flag_by_pos_from_join_buffer(uchar *rec_ptr, + JOIN_TAB *tab) +{ + DBUG_ASSERT(tab->cache && tab->cache->with_match_flag); + for (JOIN_CACHE *cache= this; ; ) + { + if (cache->join_tab == tab) + return (enum Match_flag) rec_ptr[0]; + cache= cache->prev_cache; + rec_ptr= cache->get_rec_ref(rec_ptr); + } +} + + +/* Calculate the increment of the auxiliary buffer for a record write SYNOPSIS @@ -1955,6 +1989,10 @@ bool JOIN_CACHE::read_referenced_field(CACHE_FIELD *copy, If the record is skipped the value of 'pos' is set to point to the position right after the record. + NOTE + Currently this function is called only when generating null complemented + records for outer joins (=> only when join_tab->first_unmatched != NULL). + RETURN VALUE TRUE the match flag is set to MATCH_FOUND and the record has been skipped FALSE otherwise @@ -1967,7 +2005,9 @@ bool JOIN_CACHE::skip_if_matched() if (prev_cache) offset+= prev_cache->get_size_of_rec_offset(); /* Check whether the match flag is MATCH_FOUND */ - if (get_match_flag_by_pos(pos+offset) == MATCH_FOUND) + if (get_match_flag_by_pos_from_join_buffer(pos+offset, + join_tab->first_unmatched) == + MATCH_FOUND) { pos+= size_of_rec_len + get_rec_length(pos); return TRUE; @@ -1984,13 +2024,23 @@ bool JOIN_CACHE::skip_if_matched() DESCRIPTION This default implementation of the virtual function skip_if_not_needed_match - skips the next record from the join buffer if its match flag is not - MATCH_NOT_FOUND, and, either its value is MATCH_FOUND and join_tab is the - first inner table of an inner join, or, its value is MATCH_IMPOSSIBLE - and join_tab is the first inner table of an outer join. + skips the next record from the join when generating join extensions + for the records in the join buffer depending on the value of the match flag. + - In the case of a semi-nest the match flag may be in two states + {MATCH_NOT_FOUND, MATCH_FOUND}. The record is skipped if the flag is set + to MATCH_FOUND. + - In the case of a outer join nest when not_exists optimization is applied + the match may be in three states {MATCH_NOT_FOUND, MATCH_IMPOSSIBLE, + MATCH_FOUND. The record is skipped if the flag is set to MATCH_FOUND or + to MATCH_IMPOSSIBLE. + If the record is skipped the value of 'pos' is set to point to the position right after the record. + NOTE + Currently the function is called only when generating non-null complemented + extensions for records in the join buffer. + RETURN VALUE TRUE the record has to be skipped FALSE otherwise @@ -2001,11 +2051,19 @@ bool JOIN_CACHE::skip_if_not_needed_match() DBUG_ASSERT(with_length); enum Match_flag match_fl; uint offset= size_of_rec_len; + bool skip= FALSE; if (prev_cache) offset+= prev_cache->get_size_of_rec_offset(); - if ((match_fl= get_match_flag_by_pos(pos+offset)) != MATCH_NOT_FOUND && - (join_tab->check_only_first_match() == (match_fl == MATCH_FOUND)) ) + if (!join_tab->check_only_first_match()) + return FALSE; + + match_fl= get_match_flag_by_pos(pos+offset); + skip= join_tab->first_sj_inner_tab ? + match_fl == MATCH_FOUND : // the case of semi-join + match_fl != MATCH_NOT_FOUND; // the case of outer-join + + if (skip) { pos+= size_of_rec_len + get_rec_length(pos); return TRUE; @@ -2105,7 +2163,14 @@ enum_nested_loop_state JOIN_CACHE::join_records(bool skip_last) goto finish; } join_tab->not_null_compl= FALSE; - /* Prepare for generation of null complementing extensions */ + /* + Prepare for generation of null complementing extensions. + For all inner tables of the outer join operation for which + regular matches have been just found the field 'first_unmatched' + is set to point the the first inner table. After all null + complement rows are generated for this outer join this field + is set back to NULL. + */ for (tab= join_tab->first_inner; tab <= join_tab->last_inner; tab++) tab->first_unmatched= join_tab->first_inner; } @@ -2222,7 +2287,10 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last) int error; enum_nested_loop_state rc= NESTED_LOOP_OK; join_tab->table->null_row= 0; - bool check_only_first_match= join_tab->check_only_first_match(); + bool check_only_first_match= + join_tab->check_only_first_match() && + (!join_tab->first_inner || // semi-join case + join_tab->first_inner == join_tab->first_unmatched); // outer join case bool outer_join_first_inner= join_tab->is_first_inner_for_outer_join(); DBUG_ENTER("JOIN_CACHE::join_matching_records"); diff --git a/sql/sql_join_cache.h b/sql/sql_join_cache.h index 7b8b942180f..d0bf4761f65 100644 --- a/sql/sql_join_cache.h +++ b/sql/sql_join_cache.h @@ -206,7 +206,9 @@ protected: /* This flag indicates that records written into the join buffer contain - a match flag field. The flag must be set by the init method. + a match flag field. The flag must be set by the init method. + Currently any implementation of the virtial init method calls + the function JOIN_CACHE::calc_record_fields() to set this flag. */ bool with_match_flag; /* @@ -646,6 +648,13 @@ public: /* Shall return the value of the match flag for the positioned record */ virtual enum Match_flag get_match_flag_by_pos(uchar *rec_ptr); + /* + Shall return the value of the match flag for the positioned record + from the join buffer attached to the specified table + */ + virtual enum Match_flag + get_match_flag_by_pos_from_join_buffer(uchar *rec_ptr, JOIN_TAB *tab); + /* Shall return the position of the current record */ virtual uchar *get_curr_rec() { return curr_rec_pos; } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 10cd20e1207..5937c43c95d 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -2289,6 +2289,8 @@ int Lex_input_stream::scan_ident_delimited(THD *thd, Return the quote character, to have the parser fail on syntax error. */ m_ptr= (char *) m_tok_start + 1; + if (m_echo) + m_cpp_ptr= (char *) m_cpp_tok_start + 1; return quote_char; } int var_length= my_charlen(cs, get_ptr() - 1, get_end_of_query()); @@ -2431,6 +2433,7 @@ void st_select_lex::init_query() is_service_select= 0; parsing_place= NO_MATTER; save_parsing_place= NO_MATTER; + context_analysis_place= NO_MATTER; exclude_from_table_unique_test= no_wrap_view_item= FALSE; nest_level= 0; link_next= 0; @@ -2485,6 +2488,8 @@ void st_select_lex::init_select() with_dep= 0; join= 0; lock_type= TL_READ_DEFAULT; + save_many_values.empty(); + save_insert_list= 0; tvc= 0; in_funcs.empty(); curr_tvc_name= 0; @@ -2528,6 +2533,8 @@ void st_select_lex_node::add_slave(st_select_lex_node *slave_arg) { slave= slave_arg; slave_arg->master= this; + slave->prev= &master->slave; + slave->next= 0; } } @@ -2550,6 +2557,27 @@ void st_select_lex_node::link_chain_down(st_select_lex_node *first) } /* + @brief + Substitute this node in select tree for a newly creates node + + @param subst the node to substitute for + + @details + The function substitute this node in the select tree for a newly + created node subst. This node is just removed from the tree but all + its link fields and the attached sub-tree remain untouched. +*/ + +void st_select_lex_node::substitute_in_tree(st_select_lex_node *subst) +{ + if ((subst->next= next)) + next->prev= &subst->next; + subst->prev= prev; + (*prev)= subst; + subst->master= master; +} + +/* include on level down (but do not link) SYNOPSYS @@ -2779,7 +2807,7 @@ void st_select_lex_unit::exclude_tree() */ bool st_select_lex::mark_as_dependent(THD *thd, st_select_lex *last, - Item *dependency) + Item_ident *dependency) { DBUG_ASSERT(this != last); @@ -2787,10 +2815,14 @@ bool st_select_lex::mark_as_dependent(THD *thd, st_select_lex *last, /* Mark all selects from resolved to 1 before select where was found table as depended (of select where was found table) + + We move by name resolution context, bacause during merge can some select + be excleded from SELECT tree */ - SELECT_LEX *s= this; + Name_resolution_context *c= &this->context; do { + SELECT_LEX *s= c->select_lex; if (!(s->uncacheable & UNCACHEABLE_DEPENDENT_GENERATED)) { // Select is dependent of outer select @@ -2812,7 +2844,7 @@ bool st_select_lex::mark_as_dependent(THD *thd, st_select_lex *last, if (subquery_expr && subquery_expr->mark_as_dependent(thd, last, dependency)) return TRUE; - } while ((s= s->outer_select()) != last && s != 0); + } while ((c= c->outer_context) != NULL && (c->select_lex != last)); is_correlated= TRUE; this->master_unit()->item->is_correlated= TRUE; return FALSE; @@ -4831,14 +4863,14 @@ void st_select_lex::set_explain_type(bool on_the_fly) /* pos_in_table_list=NULL for e.g. post-join aggregation JOIN_TABs. */ - if (!tab->table); - else if (const TABLE_LIST *pos= tab->table->pos_in_table_list) + if (!(tab->table && tab->table->pos_in_table_list)) + continue; + TABLE_LIST *tbl= tab->table->pos_in_table_list; + if (tbl->with && tbl->with->is_recursive && + tbl->is_with_table_recursive_reference()) { - if (pos->with && pos->with->is_recursive) - { - uses_cte= true; - break; - } + uses_cte= true; + break; } } if (uses_cte) @@ -4981,6 +5013,9 @@ bool LEX::save_prep_leaf_tables() bool st_select_lex::save_prep_leaf_tables(THD *thd) { + if (prep_leaf_list_state == SAVED) + return FALSE; + List_iterator_fast<TABLE_LIST> li(leaf_tables); TABLE_LIST *table; @@ -5012,6 +5047,27 @@ bool st_select_lex::save_prep_leaf_tables(THD *thd) } +/** + Set exclude_from_table_unique_test for selects of this select and all selects + belonging to the underlying units of derived tables or views +*/ + +void st_select_lex::set_unique_exclude() +{ + exclude_from_table_unique_test= TRUE; + for (SELECT_LEX_UNIT *unit= first_inner_unit(); + unit; + unit= unit->next_unit()) + { + if (unit->derived && unit->derived->is_view_or_derived()) + { + for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select()) + sl->set_unique_exclude(); + } + } +} + + /* Return true if this select_lex has been converted into a semi-join nest within 'ancestor'. @@ -8869,7 +8925,6 @@ bool LEX::last_field_generated_always_as_row_end() VERS_SYS_END_FLAG); } - void st_select_lex_unit::reset_distinct() { union_distinct= NULL; @@ -8885,6 +8940,20 @@ void st_select_lex_unit::reset_distinct() } +void LEX::save_values_list_state() +{ + current_select->save_many_values= many_values; + current_select->save_insert_list= insert_list; +} + + +void LEX::restore_values_list_state() +{ + many_values= current_select->save_many_values; + insert_list= current_select->save_insert_list; +} + + void st_select_lex_unit::fix_distinct() { if (union_distinct && this != union_distinct->master_unit()) @@ -9344,7 +9413,7 @@ SELECT_LEX *LEX::parsed_subselect(SELECT_LEX_UNIT *unit) (curr_sel == NULL && current_select == &builtin_select)); if (curr_sel) { - curr_sel->register_unit(unit, &curr_sel->context); + curr_sel->register_unit(unit, context_stack.head()); curr_sel->add_statistics(unit); } @@ -9381,6 +9450,7 @@ bool LEX::parsed_insert_select(SELECT_LEX *first_select) bool LEX::parsed_TVC_start() { SELECT_LEX *sel; + save_values_list_state(); many_values.empty(); insert_list= 0; if (!(sel= alloc_select(TRUE)) || @@ -9394,14 +9464,13 @@ bool LEX::parsed_TVC_start() SELECT_LEX *LEX::parsed_TVC_end() { - SELECT_LEX *res= pop_select(); // above TVC select if (!(res->tvc= new (thd->mem_root) table_value_constr(many_values, res, res->options))) return NULL; - many_values.empty(); + restore_values_list_state(); return res; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 66c44f2d901..466b23b3f94 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2019, Oracle and/or its affiliates. - Copyright (c) 2010, 2019, MariaDB Corporation. + Copyright (c) 2010, 2021, 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 @@ -501,7 +501,7 @@ struct LEX_MASTER_INFO } host= user= password= log_file_name= ssl_key= ssl_cert= ssl_ca= - ssl_capath= ssl_cipher= relay_log_name= 0; + ssl_capath= ssl_cipher= ssl_crl= ssl_crlpath= relay_log_name= NULL; pos= relay_log_pos= server_id= port= connect_retry= 0; heartbeat_period= 0; ssl= ssl_verify_server_cert= heartbeat_opt= @@ -764,7 +764,7 @@ public: link_next= NULL; link_prev= NULL; } - + void substitute_in_tree(st_select_lex_node *subst); void set_slave(st_select_lex_node *slave_arg) { slave= slave_arg; } void move_node(st_select_lex_node *where_to_move) @@ -938,6 +938,10 @@ public: void init_query(); st_select_lex* outer_select(); + const st_select_lex* first_select() const + { + return reinterpret_cast<const st_select_lex*>(slave); + } st_select_lex* first_select() { return reinterpret_cast<st_select_lex*>(slave); @@ -1287,6 +1291,8 @@ public: /* it is for correct printing SELECT options */ thr_lock_type lock_type; + List<List_item> save_many_values; + List<Item> *save_insert_list; table_value_constr *tvc; bool in_tvc; @@ -1334,7 +1340,8 @@ public: } inline bool is_subquery_function() { return master_unit()->item != 0; } - bool mark_as_dependent(THD *thd, st_select_lex *last, Item *dependency); + bool mark_as_dependent(THD *thd, st_select_lex *last, + Item_ident *dependency); void set_braces(bool value) { @@ -1470,6 +1477,8 @@ public: bool save_leaf_tables(THD *thd); bool save_prep_leaf_tables(THD *thd); + void set_unique_exclude(); + bool is_merged_child_of(st_select_lex *ancestor); /* @@ -3078,7 +3087,8 @@ public: struct LEX: public Query_tables_list { SELECT_LEX_UNIT unit; /* most upper unit */ - inline SELECT_LEX *first_select_lex() {return unit.first_select();} + SELECT_LEX *first_select_lex() { return unit.first_select(); } + const SELECT_LEX *first_select_lex() const { return unit.first_select(); } private: SELECT_LEX builtin_select; @@ -4342,6 +4352,25 @@ public: return false; } + bool create_like() const + { + DBUG_ASSERT(!create_info.like() || + !first_select_lex()->item_list.elements); + return create_info.like(); + } + + bool create_select() const + { + DBUG_ASSERT(!create_info.like() || + !first_select_lex()->item_list.elements); + return first_select_lex()->item_list.elements; + } + + bool create_simple() const + { + return !create_like() && !create_select(); + } + SELECT_LEX *exclude_last_select(); SELECT_LEX *exclude_not_first_select(SELECT_LEX *exclude); void check_automatic_up(enum sub_select_type type); @@ -4409,13 +4438,6 @@ public: return false; } - void tvc_start() - { - field_list.empty(); - many_values.empty(); - insert_list= 0; - } - SELECT_LEX_UNIT *alloc_unit(); SELECT_LEX *alloc_select(bool is_select); SELECT_LEX_UNIT *create_unit(SELECT_LEX*); @@ -4470,6 +4492,8 @@ public: bool distinct); SELECT_LEX *parsed_subselect(SELECT_LEX_UNIT *unit); bool parsed_insert_select(SELECT_LEX *firs_select); + void save_values_list_state(); + void restore_values_list_state(); bool parsed_TVC_start(); SELECT_LEX *parsed_TVC_end(); TABLE_LIST *parsed_derived_table(SELECT_LEX_UNIT *unit, diff --git a/sql/sql_manager.cc b/sql/sql_manager.cc index 2ad8d8a914a..b08e43e8af2 100644 --- a/sql/sql_manager.cc +++ b/sql/sql_manager.cc @@ -26,8 +26,8 @@ #include "sql_manager.h" #include "sql_base.h" // flush_tables -static bool volatile manager_thread_in_use; -static bool abort_manager; +static bool volatile manager_thread_in_use = 0; +static bool abort_manager = false; pthread_t manager_thread; mysql_mutex_t LOCK_manager; @@ -35,31 +35,31 @@ mysql_cond_t COND_manager; struct handler_cb { struct handler_cb *next; - void (*action)(void); + void (*action)(void *); + void *data; }; -static struct handler_cb * volatile cb_list; +static struct handler_cb *cb_list; // protected by LOCK_manager -bool mysql_manager_submit(void (*action)()) +bool mysql_manager_submit(void (*action)(void *), void *data) { bool result= FALSE; DBUG_ASSERT(manager_thread_in_use); - struct handler_cb * volatile *cb; + struct handler_cb **cb; mysql_mutex_lock(&LOCK_manager); cb= &cb_list; - while (*cb && (*cb)->action != action) + while (*cb) cb= &(*cb)->next; + *cb= (struct handler_cb *)my_malloc(sizeof(struct handler_cb), MYF(MY_WME)); if (!*cb) + result= TRUE; + else { - *cb= (struct handler_cb *)my_malloc(sizeof(struct handler_cb), MYF(MY_WME)); - if (!*cb) - result= TRUE; - else - { - (*cb)->next= NULL; - (*cb)->action= action; - } + (*cb)->next= NULL; + (*cb)->action= action; + (*cb)->data= data; } + mysql_cond_signal(&COND_manager); mysql_mutex_unlock(&LOCK_manager); return result; } @@ -69,18 +69,14 @@ pthread_handler_t handle_manager(void *arg __attribute__((unused))) int error = 0; struct timespec abstime; bool reset_flush_time = TRUE; - struct handler_cb *cb= NULL; my_thread_init(); DBUG_ENTER("handle_manager"); pthread_detach_this_thread(); manager_thread = pthread_self(); - mysql_cond_init(key_COND_manager, &COND_manager,NULL); - mysql_mutex_init(key_LOCK_manager, &LOCK_manager, NULL); - manager_thread_in_use = 1; - for (;;) + mysql_mutex_lock(&LOCK_manager); + while (!abort_manager) { - mysql_mutex_lock(&LOCK_manager); /* XXX: This will need to be made more general to handle different * polling needs. */ if (flush_time) @@ -90,40 +86,37 @@ pthread_handler_t handle_manager(void *arg __attribute__((unused))) set_timespec(abstime, flush_time); reset_flush_time = FALSE; } - while ((!error || error == EINTR) && !abort_manager) + while ((!error || error == EINTR) && !abort_manager && !cb_list) error= mysql_cond_timedwait(&COND_manager, &LOCK_manager, &abstime); + + if (error == ETIMEDOUT || error == ETIME) + { + tc_purge(); + error = 0; + reset_flush_time = TRUE; + } } else { - while ((!error || error == EINTR) && !abort_manager) + while ((!error || error == EINTR) && !abort_manager && !cb_list) error= mysql_cond_wait(&COND_manager, &LOCK_manager); } - if (cb == NULL) - { - cb= cb_list; - cb_list= NULL; - } - mysql_mutex_unlock(&LOCK_manager); - if (abort_manager) - break; - - if (error == ETIMEDOUT || error == ETIME) - { - tc_purge(); - error = 0; - reset_flush_time = TRUE; - } + struct handler_cb *cb= cb_list; + cb_list= NULL; + mysql_mutex_unlock(&LOCK_manager); while (cb) { struct handler_cb *next= cb->next; - cb->action(); + cb->action(cb->data); my_free(cb); cb= next; } + mysql_mutex_lock(&LOCK_manager); } manager_thread_in_use = 0; + mysql_mutex_unlock(&LOCK_manager); mysql_mutex_destroy(&LOCK_manager); mysql_cond_destroy(&COND_manager); DBUG_LEAVE; // Can't use DBUG_RETURN after my_thread_end @@ -137,15 +130,15 @@ void start_handle_manager() { DBUG_ENTER("start_handle_manager"); abort_manager = false; - if (flush_time && flush_time != ~(ulong) 0L) { pthread_t hThread; - int error; - if ((error= mysql_thread_create(key_thread_handle_manager, - &hThread, &connection_attrib, - handle_manager, 0))) - sql_print_warning("Can't create handle_manager thread (errno= %d)", - error); + int err; + manager_thread_in_use = 1; + mysql_cond_init(key_COND_manager, &COND_manager,NULL); + mysql_mutex_init(key_LOCK_manager, &LOCK_manager, NULL); + if ((err= mysql_thread_create(key_thread_handle_manager, &hThread, + &connection_attrib, handle_manager, 0))) + sql_print_warning("Can't create handle_manager thread (errno: %M)", err); } DBUG_VOID_RETURN; } @@ -155,10 +148,10 @@ void start_handle_manager() void stop_handle_manager() { DBUG_ENTER("stop_handle_manager"); - abort_manager = true; if (manager_thread_in_use) { mysql_mutex_lock(&LOCK_manager); + abort_manager = true; DBUG_PRINT("quit", ("initiate shutdown of handle manager thread: %lu", (ulong)manager_thread)); mysql_cond_signal(&COND_manager); diff --git a/sql/sql_manager.h b/sql/sql_manager.h index 9c6c84450ed..f97d4a2cfc5 100644 --- a/sql/sql_manager.h +++ b/sql/sql_manager.h @@ -18,6 +18,6 @@ void start_handle_manager(); void stop_handle_manager(); -bool mysql_manager_submit(void (*action)()); +bool mysql_manager_submit(void (*action)(void *), void *data); #endif /* SQL_MANAGER_INCLUDED */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 036c1215ea6..3ae7c7c7df3 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2017, Oracle and/or its affiliates. - Copyright (c) 2008, 2020, MariaDB + Copyright (c) 2008, 2021, 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 @@ -1164,6 +1164,14 @@ static bool wsrep_tables_accessible_when_detached(const TABLE_LIST *tables) } return true; } + +static bool wsrep_command_no_result(char command) +{ + return (command == COM_STMT_PREPARE || + command == COM_STMT_FETCH || + command == COM_STMT_SEND_LONG_DATA || + command == COM_STMT_CLOSE); +} #endif /* WITH_WSREP */ #ifndef EMBEDDED_LIBRARY @@ -1287,12 +1295,20 @@ bool do_command(THD *thd) #ifdef WITH_WSREP DEBUG_SYNC(thd, "wsrep_before_before_command"); /* - Aborted by background rollbacker thread. - Handle error here and jump straight to out + If this command does not return a result, then we + instruct wsrep_before_command() to skip result handling. + This causes BF aborted transaction to roll back but keep + the error state until next command which is able to return + a result to the client. */ - if (wsrep_before_command(thd)) + if (wsrep_before_command(thd, wsrep_command_no_result(command))) { - thd->store_globals(); + /* + Aborted by background rollbacker thread. + Handle error here and jump straight to out. + Notice that thd->store_globals() is called + in wsrep_before_command(). + */ WSREP_LOG_THD(thd, "enter found BF aborted"); DBUG_ASSERT(!thd->mdl_context.has_locks()); DBUG_ASSERT(!thd->get_stmt_da()->is_set()); @@ -1625,7 +1641,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (unlikely(thd->security_ctx->password_expired && command != COM_QUERY && command != COM_PING && - command != COM_QUIT)) + command != COM_QUIT && + command != COM_STMT_PREPARE && + command != COM_STMT_EXECUTE)) { my_error(ER_MUST_CHANGE_PASSWORD, MYF(0)); goto dispatch_end; @@ -2204,6 +2222,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; general_log_print(thd, command, NullS); status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_STATUS]); + *current_global_status_var= global_status_var; calc_sum_of_all_status(current_global_status_var); if (!(uptime= (ulong) (thd->start_time - server_start_time))) queries_per_second1000= 0; @@ -2384,12 +2403,7 @@ dispatch_end: WSREP_DEBUG("THD is killed at dispatch_end"); } wsrep_after_command_before_result(thd); - if (wsrep_current_error(thd) && - !(command == COM_STMT_PREPARE || - command == COM_STMT_FETCH || - command == COM_STMT_SEND_LONG_DATA || - command == COM_STMT_CLOSE - )) + if (wsrep_current_error(thd) && !wsrep_command_no_result(command)) { /* todo: Pass wsrep client state current error to override */ wsrep_override_error(thd, wsrep_current_error(thd), @@ -3294,6 +3308,146 @@ bool Sql_cmd_call::execute(THD *thd) /** + Check whether the SQL statement being processed is prepended by + SET STATEMENT clause and handle variables assignment if it is. + + @param thd thread handle + @param lex current lex + + @return false in case of success, true in case of error. +*/ + +bool run_set_statement_if_requested(THD *thd, LEX *lex) +{ + if (!lex->stmt_var_list.is_empty() && !thd->slave_thread) + { + Query_arena backup; + DBUG_PRINT("info", ("SET STATEMENT %d vars", lex->stmt_var_list.elements)); + + lex->old_var_list.empty(); + List_iterator_fast<set_var_base> it(lex->stmt_var_list); + set_var_base *var; + + if (lex->set_arena_for_set_stmt(&backup)) + return true; + + MEM_ROOT *mem_root= thd->mem_root; + while ((var= it++)) + { + DBUG_ASSERT(var->is_system()); + set_var *o= NULL, *v= (set_var*)var; + if (!v->var->is_set_stmt_ok()) + { + my_error(ER_SET_STATEMENT_NOT_SUPPORTED, MYF(0), v->var->name.str); + lex->reset_arena_for_set_stmt(&backup); + lex->old_var_list.empty(); + lex->free_arena_for_set_stmt(); + return true; + } + if (v->var->session_is_default(thd)) + o= new set_var(thd,v->type, v->var, &v->base, NULL); + else + { + switch (v->var->option.var_type & GET_TYPE_MASK) + { + case GET_BOOL: + case GET_INT: + case GET_LONG: + case GET_LL: + { + bool null_value; + longlong val= v->var->val_int(&null_value, thd, v->type, &v->base); + o= new set_var(thd, v->type, v->var, &v->base, + (null_value ? + (Item *) new (mem_root) Item_null(thd) : + (Item *) new (mem_root) Item_int(thd, val))); + } + break; + case GET_UINT: + case GET_ULONG: + case GET_ULL: + { + bool null_value; + ulonglong val= v->var->val_int(&null_value, thd, v->type, &v->base); + o= new set_var(thd, v->type, v->var, &v->base, + (null_value ? + (Item *) new (mem_root) Item_null(thd) : + (Item *) new (mem_root) Item_uint(thd, val))); + } + break; + case GET_DOUBLE: + { + bool null_value; + double val= v->var->val_real(&null_value, thd, v->type, &v->base); + o= new set_var(thd, v->type, v->var, &v->base, + (null_value ? + (Item *) new (mem_root) Item_null(thd) : + (Item *) new (mem_root) Item_float(thd, val, 1))); + } + break; + default: + case GET_NO_ARG: + case GET_DISABLED: + DBUG_ASSERT(0); + /* fall through */ + case 0: + case GET_FLAGSET: + case GET_ENUM: + case GET_SET: + case GET_STR: + case GET_STR_ALLOC: + { + char buff[STRING_BUFFER_USUAL_SIZE]; + String tmp(buff, sizeof(buff), v->var->charset(thd)),*val; + val= v->var->val_str(&tmp, thd, v->type, &v->base); + if (val) + { + Item_string *str= + new (mem_root) Item_string(thd, v->var->charset(thd), + val->ptr(), val->length()); + o= new set_var(thd, v->type, v->var, &v->base, str); + } + else + o= new set_var(thd, v->type, v->var, &v->base, + new (mem_root) Item_null(thd)); + } + break; + } + } + DBUG_ASSERT(o); + lex->old_var_list.push_back(o, thd->mem_root); + } + lex->reset_arena_for_set_stmt(&backup); + + if (lex->old_var_list.is_empty()) + lex->free_arena_for_set_stmt(); + + if (thd->is_error() || + sql_set_variables(thd, &lex->stmt_var_list, false)) + { + if (!thd->is_error()) + my_error(ER_WRONG_ARGUMENTS, MYF(0), "SET"); + lex->restore_set_statement_var(); + return true; + } + /* + The value of last_insert_id is remembered in THD to be written to binlog + when it's used *the first time* in the statement. But SET STATEMENT + must read the old value of last_insert_id to be able to restore it at + the end. This should not count at "reading of last_insert_id" and + should not remember last_insert_id for binlog. That is, it should clear + stmt_depends_on_first_successful_insert_id_in_prev_stmt flag. + */ + if (!thd->in_sub_stmt) + { + thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0; + } + } + return false; +} + + +/** Execute command saved in thd and lex->sql_command. @param thd Thread handle @@ -3338,7 +3492,10 @@ mysql_execute_command(THD *thd) first_table->for_insert_data); if (thd->security_ctx->password_expired && - lex->sql_command != SQLCOM_SET_OPTION) + lex->sql_command != SQLCOM_SET_OPTION && + lex->sql_command != SQLCOM_PREPARE && + lex->sql_command != SQLCOM_EXECUTE && + lex->sql_command != SQLCOM_DEALLOCATE_PREPARE) { my_error(ER_MUST_CHANGE_PASSWORD, MYF(0)); DBUG_RETURN(1); @@ -3521,6 +3678,11 @@ mysql_execute_command(THD *thd) Json_writer_object trace_command(thd); Json_writer_array trace_command_steps(thd, "steps"); + /* store old value of binlog format */ + enum_binlog_format orig_binlog_format,orig_current_stmt_binlog_format; + + thd->get_binlog_format(&orig_binlog_format, + &orig_current_stmt_binlog_format); #ifdef WITH_WSREP if (WSREP(thd)) { @@ -3572,133 +3734,13 @@ mysql_execute_command(THD *thd) DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table == FALSE); - /* store old value of binlog format */ - enum_binlog_format orig_binlog_format,orig_current_stmt_binlog_format; - - thd->get_binlog_format(&orig_binlog_format, - &orig_current_stmt_binlog_format); - - if (!lex->stmt_var_list.is_empty() && !thd->slave_thread) - { - Query_arena backup; - DBUG_PRINT("info", ("SET STATEMENT %d vars", lex->stmt_var_list.elements)); - - lex->old_var_list.empty(); - List_iterator_fast<set_var_base> it(lex->stmt_var_list); - set_var_base *var; - - if (lex->set_arena_for_set_stmt(&backup)) - goto error; - - MEM_ROOT *mem_root= thd->mem_root; - while ((var= it++)) - { - DBUG_ASSERT(var->is_system()); - set_var *o= NULL, *v= (set_var*)var; - if (!v->var->is_set_stmt_ok()) - { - my_error(ER_SET_STATEMENT_NOT_SUPPORTED, MYF(0), v->var->name.str); - lex->reset_arena_for_set_stmt(&backup); - lex->old_var_list.empty(); - lex->free_arena_for_set_stmt(); - goto error; - } - if (v->var->session_is_default(thd)) - o= new set_var(thd,v->type, v->var, &v->base, NULL); - else - { - switch (v->var->option.var_type & GET_TYPE_MASK) - { - case GET_BOOL: - case GET_INT: - case GET_LONG: - case GET_LL: - { - bool null_value; - longlong val= v->var->val_int(&null_value, thd, v->type, &v->base); - o= new set_var(thd, v->type, v->var, &v->base, - (null_value ? - (Item *) new (mem_root) Item_null(thd) : - (Item *) new (mem_root) Item_int(thd, val))); - } - break; - case GET_UINT: - case GET_ULONG: - case GET_ULL: - { - bool null_value; - ulonglong val= v->var->val_int(&null_value, thd, v->type, &v->base); - o= new set_var(thd, v->type, v->var, &v->base, - (null_value ? - (Item *) new (mem_root) Item_null(thd) : - (Item *) new (mem_root) Item_uint(thd, val))); - } - break; - case GET_DOUBLE: - { - bool null_value; - double val= v->var->val_real(&null_value, thd, v->type, &v->base); - o= new set_var(thd, v->type, v->var, &v->base, - (null_value ? - (Item *) new (mem_root) Item_null(thd) : - (Item *) new (mem_root) Item_float(thd, val, 1))); - } - break; - default: - case GET_NO_ARG: - case GET_DISABLED: - DBUG_ASSERT(0); - /* fall through */ - case 0: - case GET_FLAGSET: - case GET_ENUM: - case GET_SET: - case GET_STR: - case GET_STR_ALLOC: - { - char buff[STRING_BUFFER_USUAL_SIZE]; - String tmp(buff, sizeof(buff), v->var->charset(thd)),*val; - val= v->var->val_str(&tmp, thd, v->type, &v->base); - if (val) - { - Item_string *str= new (mem_root) Item_string(thd, v->var->charset(thd), - val->ptr(), val->length()); - o= new set_var(thd, v->type, v->var, &v->base, str); - } - else - o= new set_var(thd, v->type, v->var, &v->base, - new (mem_root) Item_null(thd)); - } - break; - } - } - DBUG_ASSERT(o); - lex->old_var_list.push_back(o, thd->mem_root); - } - lex->reset_arena_for_set_stmt(&backup); - if (lex->old_var_list.is_empty()) - lex->free_arena_for_set_stmt(); - if (thd->is_error() || - (res= sql_set_variables(thd, &lex->stmt_var_list, false))) - { - if (!thd->is_error()) - my_error(ER_WRONG_ARGUMENTS, MYF(0), "SET"); - lex->restore_set_statement_var(); - goto error; - } - /* - The value of last_insert_id is remembered in THD to be written to binlog - when it's used *the first time* in the statement. But SET STATEMENT - must read the old value of last_insert_id to be able to restore it at - the end. This should not count at "reading of last_insert_id" and - should not remember last_insert_id for binlog. That is, it should clear - stmt_depends_on_first_successful_insert_id_in_prev_stmt flag. - */ - if (!thd->in_sub_stmt) - { - thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0; - } - } + /* + Assign system variables with values specified by the clause + SET STATEMENT var1=value1 [, var2=value2, ...] FOR <statement> + if they are any. + */ + if (run_set_statement_if_requested(thd, lex)) + goto error; if (thd->lex->mi.connection_name.str == NULL) thd->lex->mi.connection_name= thd->variables.default_master_connection; @@ -3744,7 +3786,8 @@ mysql_execute_command(THD *thd) thd->transaction.stmt.mark_trans_did_ddl(); #ifdef WITH_WSREP /* Clean up the previous transaction on implicit commit */ - if (wsrep_thd_is_local(thd) && wsrep_after_statement(thd)) + if (WSREP_NNULL(thd) && wsrep_thd_is_local(thd) && + wsrep_after_statement(thd)) { goto error; } @@ -3818,7 +3861,7 @@ mysql_execute_command(THD *thd) Do not start transaction for stored procedures, it will be handled internally in SP processing. */ - if (WSREP(thd) && + if (WSREP_NNULL(thd) && wsrep_thd_is_local(thd) && lex->sql_command != SQLCOM_BEGIN && lex->sql_command != SQLCOM_CALL && @@ -5987,6 +6030,14 @@ mysql_execute_command(THD *thd) break; } case SQLCOM_XA_START: +#ifdef WITH_WSREP + if (WSREP(thd)) + { + my_error(ER_NOT_SUPPORTED_YET, MYF(0), + "XA transactions with Galera replication"); + break; + } +#endif /* WITH_WSREP */ if (trans_xa_start(thd)) goto error; my_ok(thd); @@ -6797,6 +6848,9 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, bool check_single_table_access(THD *thd, ulong privilege, TABLE_LIST *tables, bool no_errors) { + if (tables->derived) + return 0; + Switch_to_definer_security_ctx backup_sctx(thd, tables); const char *db_name; @@ -7503,9 +7557,14 @@ void THD::reset_for_next_command(bool do_clear_error) save_prep_leaf_list= false; - DBUG_PRINT("debug", - ("is_current_stmt_binlog_format_row(): %d", - is_current_stmt_binlog_format_row())); +#ifdef WITH_WSREP +#if !defined(DBUG_OFF) + if (mysql_bin_log.is_open()) +#endif +#endif + DBUG_PRINT("debug", + ("is_current_stmt_binlog_format_row(): %d", + is_current_stmt_binlog_format_row())); DBUG_VOID_RETURN; } @@ -8934,7 +8993,13 @@ push_new_name_resolution_context(THD *thd, left_op->first_leaf_for_name_resolution(); on_context->last_name_resolution_table= right_op->last_leaf_for_name_resolution(); - return thd->lex->push_context(on_context); + LEX *lex= thd->lex; + on_context->select_lex = lex->current_select; + st_select_lex *curr_select= lex->pop_select(); + st_select_lex *outer_sel= lex->select_stack_head(); + lex->push_select(curr_select); + on_context->outer_context = outer_sel ? &outer_sel->context : 0; + return lex->push_context(on_context); } @@ -9056,10 +9121,9 @@ struct find_thread_callback_arg }; -my_bool find_thread_callback(THD *thd, find_thread_callback_arg *arg) +static my_bool find_thread_callback(THD *thd, find_thread_callback_arg *arg) { - if (thd->get_command() != COM_DAEMON && - arg->id == (arg->query_id ? thd->query_id : (longlong) thd->thread_id)) + if (arg->id == (arg->query_id ? thd->query_id : (longlong) thd->thread_id)) { mysql_mutex_lock(&thd->LOCK_thd_kill); // Lock from delete arg->thd= thd; @@ -9076,27 +9140,6 @@ THD *find_thread_by_id(longlong id, bool query_id) return arg.thd; } -#ifdef WITH_WSREP -my_bool find_thread_with_thd_data_lock_callback(THD *thd, find_thread_callback_arg *arg) -{ - if (thd->get_command() != COM_DAEMON && - arg->id == (arg->query_id ? thd->query_id : (longlong) thd->thread_id)) - { - if (WSREP(thd)) mysql_mutex_lock(&thd->LOCK_thd_data); - mysql_mutex_lock(&thd->LOCK_thd_kill); // Lock from delete - arg->thd= thd; - return 1; - } - return 0; -} -THD *find_thread_by_id_with_thd_data_lock(longlong id, bool query_id) -{ - find_thread_callback_arg arg(id, query_id); - server_threads.iterate(find_thread_with_thd_data_lock_callback, &arg); - return arg.thd; -} -#endif - /** kill one thread. @@ -9113,11 +9156,11 @@ kill_one_thread(THD *thd, longlong id, killed_state kill_signal, killed_type typ uint error= (type == KILL_TYPE_QUERY ? ER_NO_SUCH_QUERY : ER_NO_SUCH_THREAD); DBUG_ENTER("kill_one_thread"); DBUG_PRINT("enter", ("id: %lld signal: %u", id, (uint) kill_signal)); -#ifdef WITH_WSREP - if (id && (tmp= find_thread_by_id_with_thd_data_lock(id, type == KILL_TYPE_QUERY))) -#else - if (id && (tmp= find_thread_by_id(id, type == KILL_TYPE_QUERY))) -#endif + tmp= find_thread_by_id(id, type == KILL_TYPE_QUERY); + if (!tmp) + DBUG_RETURN(error); + + if (tmp->get_command() != COM_DAEMON) { /* If we're SUPER, we can KILL anything, including system-threads. @@ -9140,6 +9183,7 @@ kill_one_thread(THD *thd, longlong id, killed_state kill_signal, killed_type typ faster and do a harder kill than KILL_SYSTEM_THREAD; */ + mysql_mutex_lock(&tmp->LOCK_thd_data); // for various wsrep* checks below #ifdef WITH_WSREP if (((thd->security_ctx->master_access & SUPER_ACL) || thd->security_ctx->user_matches(tmp->security_ctx)) && @@ -9161,8 +9205,8 @@ kill_one_thread(THD *thd, longlong id, killed_state kill_signal, killed_type typ else #endif /* WITH_WSREP */ { - WSREP_DEBUG("kill_one_thread %llu, victim: %llu wsrep_aborter %llu by signal %d", - thd->thread_id, id, tmp->wsrep_aborter, kill_signal); + WSREP_DEBUG("kill_one_thread %llu, victim: %llu wsrep_aborter %llu by signal %d", + thd->thread_id, id, tmp->wsrep_aborter, kill_signal); tmp->awake_no_mutex(kill_signal); WSREP_DEBUG("victim: %llu taken care of", id); error= 0; @@ -9171,11 +9215,9 @@ kill_one_thread(THD *thd, longlong id, killed_state kill_signal, killed_type typ else error= (type == KILL_TYPE_QUERY ? ER_KILL_QUERY_DENIED_ERROR : ER_KILL_DENIED_ERROR); -#ifdef WITH_WSREP - if (WSREP(tmp)) mysql_mutex_unlock(&tmp->LOCK_thd_data); -#endif - mysql_mutex_unlock(&tmp->LOCK_thd_kill); + mysql_mutex_unlock(&tmp->LOCK_thd_data); } + mysql_mutex_unlock(&tmp->LOCK_thd_kill); DBUG_PRINT("exit", ("%d", error)); DBUG_RETURN(error); } @@ -9221,8 +9263,8 @@ static my_bool kill_threads_callback(THD *thd, kill_threads_callback_arg *arg) return 1; if (!arg->threads_to_kill.push_back(thd, arg->thd->mem_root)) { - if (WSREP(thd)) mysql_mutex_lock(&thd->LOCK_thd_data); mysql_mutex_lock(&thd->LOCK_thd_kill); // Lock from delete + mysql_mutex_lock(&thd->LOCK_thd_data); } } } @@ -9265,7 +9307,7 @@ static uint kill_threads_for_user(THD *thd, LEX_USER *user, */ next_ptr= it2++; mysql_mutex_unlock(&ptr->LOCK_thd_kill); - if (WSREP(ptr)) mysql_mutex_unlock(&ptr->LOCK_thd_data); + mysql_mutex_unlock(&ptr->LOCK_thd_data); (*rows)++; } while ((ptr= next_ptr)); } diff --git a/sql/sql_parse.h b/sql/sql_parse.h index 1d25b898ca4..be37e3f6bb3 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -100,6 +100,7 @@ void mysql_init_multi_delete(LEX *lex); bool multi_delete_set_locks_and_link_aux_tables(LEX *lex); void create_table_set_open_action_and_adjust_tables(LEX *lex); int bootstrap(MYSQL_FILE *file); +bool run_set_statement_if_requested(THD *thd, LEX *lex); int mysql_execute_command(THD *thd); bool do_command(THD *thd); bool dispatch_command(enum enum_server_command command, THD *thd, diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 7ed1eb7aa52..43264e3e508 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -1505,7 +1505,7 @@ static bool check_list_constants(THD *thd, partition_info *part_info) List_iterator<part_elem_value> list_val_it2(part_def->list_val_list); while ((list_value= list_val_it2++)) { - calc_value= list_value->value - type_add; + calc_value= list_value->value ^ type_add; part_info->list_array[list_index].list_value= calc_value; part_info->list_array[list_index++].partition_id= i; } @@ -2388,6 +2388,8 @@ static int add_column_list_values(String *str, partition_info *part_info, */ if (create_info) { + const Column_derived_attributes + derived_attr(create_info->default_table_charset); Create_field *sql_field; if (!(sql_field= get_sql_field(field_name, @@ -2402,7 +2404,7 @@ static int add_column_list_values(String *str, partition_info *part_info, &need_cs_check)) return 1; if (need_cs_check) - field_cs= get_sql_field_charset(sql_field, create_info); + field_cs= sql_field->explicit_or_derived_charset(&derived_attr); else field_cs= NULL; } diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h index c9c75d07a6e..eb5532f5d59 100644 --- a/sql/sql_plugin.h +++ b/sql/sql_plugin.h @@ -22,10 +22,11 @@ that is defined in plugin.h */ #define SHOW_always_last SHOW_KEY_CACHE_LONG, \ - SHOW_LONG_STATUS, SHOW_DOUBLE_STATUS, \ SHOW_HAVE, SHOW_MY_BOOL, SHOW_HA_ROWS, SHOW_SYS, \ - SHOW_LONG_NOFLUSH, SHOW_LONGLONG_STATUS, SHOW_UINT32_STATUS, \ - SHOW_LEX_STRING, SHOW_ATOMIC_COUNTER_UINT32_T + SHOW_LONG_NOFLUSH, SHOW_LEX_STRING, SHOW_ATOMIC_COUNTER_UINT32_T, \ + /* SHOW_*_STATUS must be at the end, SHOW_LONG_STATUS being first */ \ + SHOW_LONG_STATUS, SHOW_DOUBLE_STATUS, SHOW_LONGLONG_STATUS, \ + SHOW_UINT32_STATUS #include "mariadb.h" #undef SHOW_always_last @@ -204,4 +205,3 @@ extern void sync_dynamic_session_variables(THD* thd, bool global_lock); extern void wsrep_plugins_pre_init(); extern void wsrep_plugins_post_init(); #endif /* WITH_WSREP */ - diff --git a/sql/sql_plugin_services.ic b/sql/sql_plugin_services.ic index 69e57de5c8b..740569dc76e 100644 --- a/sql/sql_plugin_services.ic +++ b/sql/sql_plugin_services.ic @@ -155,7 +155,6 @@ static struct wsrep_service_st wsrep_handler = { wsrep_thd_retry_counter, wsrep_thd_ignore_table, wsrep_thd_trx_seqno, - wsrep_thd_auto_increment_variables, wsrep_thd_is_aborting, wsrep_set_data_home_dir, wsrep_thd_is_BF, @@ -175,7 +174,9 @@ static struct wsrep_service_st wsrep_handler = { wsrep_commit_ordered, wsrep_thd_is_applying, wsrep_thd_set_wsrep_aborter, - wsrep_report_bf_lock_wait + wsrep_report_bf_lock_wait, + wsrep_thd_kill_LOCK, + wsrep_thd_kill_UNLOCK }; static struct thd_specifics_service_st thd_specifics_handler= @@ -245,4 +246,3 @@ static struct st_service_ref list_of_services[]= { "wsrep_service", VERSION_wsrep, &wsrep_handler }, { "json_service", VERSION_json, &json_handler } }; - diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index ee504028074..f0b8fc7309e 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2002, 2015, Oracle and/or its affiliates. - Copyright (c) 2008, 2019, MariaDB + Copyright (c) 2008, 2021, 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 @@ -118,8 +118,11 @@ When one supplies long data for a placeholder: #include <mysql.h> #else #include <mysql_com.h> +/* Constants defining bits in parameter type flags. Flags are read from high byte of short value */ +static const uint PARAMETER_FLAG_UNSIGNED= 128U << 8; #endif #include "lock.h" // MYSQL_OPEN_FORCE_SHARED_MDL +#include "log_event.h" // class Log_event #include "sql_handler.h" #include "transaction.h" // trans_rollback_implicit #ifdef WITH_WSREP @@ -127,9 +130,6 @@ When one supplies long data for a placeholder: #include "wsrep_trans_observer.h" #endif /* WITH_WSREP */ -/* Constants defining bits in parameter type flags. Flags are read from high byte of short value */ -static const uint PARAMETER_FLAG_UNSIGNED = 128U << 8; - /** A result class used to send cursor rows using the binary protocol. */ @@ -2488,6 +2488,16 @@ static bool check_prepared_statement(Prepared_statement *stmt) DBUG_RETURN(FALSE); } break; + case SQLCOM_SHOW_BINLOG_EVENTS: + case SQLCOM_SHOW_RELAYLOG_EVENTS: + { + List<Item> field_list; + Log_event::init_show_field_list(thd, &field_list); + + if ((res= send_stmt_metadata(thd, stmt, &field_list)) == 2) + DBUG_RETURN(FALSE); + } + break; #endif /* EMBEDDED_LIBRARY */ case SQLCOM_SHOW_CREATE_PROC: if ((res= mysql_test_show_create_routine(stmt, &sp_handler_procedure)) == 2) @@ -4207,6 +4217,15 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) thd->is_error() || init_param_array(this)); + if (thd->security_ctx->password_expired && + lex->sql_command != SQLCOM_SET_OPTION) + { + thd->restore_backup_statement(this, &stmt_backup); + thd->restore_active_arena(this, &stmt_backup); + thd->stmt_arena= old_stmt_arena; + my_error(ER_MUST_CHANGE_PASSWORD, MYF(0)); + DBUG_RETURN(true); + } lex->set_trg_event_type_for_tables(); /* @@ -4231,6 +4250,16 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) */ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); + /* + Set variables specified by + SET STATEMENT var1=value1 [, var2=value2, ...] FOR <statement> + clause for duration of prepare phase. Original values of variable + listed in the SET STATEMENT clause is restored right after return + from the function check_prepared_statement() + */ + if (likely(error == 0)) + error= run_set_statement_if_requested(thd, lex); + /* The only case where we should have items in the thd->free_list is after stmt->set_params_from_vars(), which may in some cases create @@ -4249,6 +4278,12 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_PREPARE; } + /* + Restore original values of variables modified on handling + SET STATEMENT clause. + */ + thd->lex->restore_set_statement_var(); + /* The order is important */ lex->unit.cleanup(); diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc index 5b4600ece9a..76fb9819fd5 100644 --- a/sql/sql_reload.cc +++ b/sql/sql_reload.cc @@ -416,6 +416,14 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options, { if (reinit_ssl()) result= 1; +#ifdef WITH_WSREP + if (!result && + WSREP_ON && wsrep_reload_ssl()) + { + my_message(ER_UNKNOWN_ERROR, "Failed to refresh WSREP SSL.", MYF(0)); + result= 1; + } +#endif } if (options & REFRESH_GENERIC) { diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 5203e0f52a5..6a6cfb2aa5f 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -3474,8 +3474,8 @@ static my_bool kill_callback(THD *thd, kill_callback_arg *arg) thd->variables.server_id == arg->slave_server_id) { arg->thd= thd; - if (WSREP(thd)) mysql_mutex_lock(&thd->LOCK_thd_data); mysql_mutex_lock(&thd->LOCK_thd_kill); // Lock from delete + mysql_mutex_lock(&thd->LOCK_thd_data); return 1; } return 0; @@ -3496,7 +3496,7 @@ void kill_zombie_dump_threads(uint32 slave_server_id) */ arg.thd->awake_no_mutex(KILL_SLAVE_SAME_ID); mysql_mutex_unlock(&arg.thd->LOCK_thd_kill); - if (WSREP(arg.thd)) mysql_mutex_unlock(&arg.thd->LOCK_thd_data); + mysql_mutex_unlock(&arg.thd->LOCK_thd_data); } } @@ -4685,5 +4685,22 @@ rpl_gtid_pos_update(THD *thd, char *str, size_t len) return false; } +int compare_log_name(const char *log_1, const char *log_2) { + int res= 1; + const char *ext1_str= strrchr(log_1, '.'); + const char *ext2_str= strrchr(log_2, '.'); + char file_name_1[255], file_name_2[255]; + strmake(file_name_1, log_1, (ext1_str - log_1)); + strmake(file_name_2, log_2, (ext2_str - log_2)); + char *endptr = NULL; + res= strcmp(file_name_1, file_name_2); + if (!res) + { + ulong ext1= strtoul(++ext1_str, &endptr, 10); + ulong ext2= strtoul(++ext2_str, &endptr, 10); + res= (ext1 > ext2 ? 1 : ((ext1 == ext2) ? 0 : -1)); + } + return res; +} #endif /* HAVE_REPLICATION */ diff --git a/sql/sql_repl.h b/sql/sql_repl.h index 18aa7ea3fce..95916e31abf 100644 --- a/sql/sql_repl.h +++ b/sql/sql_repl.h @@ -45,6 +45,7 @@ bool show_binlogs(THD* thd); extern int init_master_info(Master_info* mi); void kill_zombie_dump_threads(uint32 slave_server_id); int check_binlog_magic(IO_CACHE* log, const char** errmsg); +int compare_log_name(const char *log_1, const char *log_2); struct LOAD_FILE_IO_CACHE : public IO_CACHE { diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 9671880f1e0..52abaf29d05 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2016, Oracle and/or its affiliates. - Copyright (c) 2009, 2020, MariaDB Corporation. + Copyright (c) 2009, 2021, 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 @@ -300,6 +300,8 @@ void set_postjoin_aggr_write_func(JOIN_TAB *tab); static Item **get_sargable_cond(JOIN *join, TABLE *table); +bool is_eq_cond_injected_for_split_opt(Item_func_eq *eq_item); + #ifndef DBUG_OFF /* @@ -348,7 +350,31 @@ bool dbug_user_var_equals_int(THD *thd, const char *name, int value) } return FALSE; } -#endif +#endif /* DBUG_OFF */ + +/* + Intialize POSITION structure. +*/ + +POSITION::POSITION() +{ + table= 0; + records_read= cond_selectivity= read_time= 0.0; + prefix_record_count= 0.0; + key= 0; + use_join_buffer= 0; + sj_strategy= SJ_OPT_NONE; + n_sj_tables= 0; + spl_plan= 0; + range_rowid_filter_info= 0; + ref_depend_map= dups_producing_tables= 0; + inner_tables_handled_with_other_sjs= 0; + dups_weedout_picker.set_empty(); + firstmatch_picker.set_empty(); + loosescan_picker.set_empty(); + sjmat_picker.set_empty(); +} + static void trace_table_dependencies(THD *thd, JOIN_TAB *join_tabs, uint table_count) @@ -634,7 +660,16 @@ void remove_redundant_subquery_clauses(st_select_lex *subq_select_lex) { for (ORDER *ord= subq_select_lex->group_list.first; ord; ord= ord->next) { - (*ord->item)->walk(&Item::eliminate_subselect_processor, FALSE, NULL); + /* + Do not remove the item if it is used in select list and then referred + from GROUP BY clause by its name or number. Example: + + select (select ... ) as SUBQ ... group by SUBQ + + Here SUBQ cannot be removed. + */ + if (!ord->in_field_list) + (*ord->item)->walk(&Item::eliminate_subselect_processor, FALSE, NULL); } subq_select_lex->join->group_list= NULL; subq_select_lex->group_list.empty(); @@ -1166,22 +1201,6 @@ JOIN::prepare(TABLE_LIST *tables_init, FALSE, SELECT_ACL, SELECT_ACL, FALSE)) DBUG_RETURN(-1); - /* - Permanently remove redundant parts from the query if - 1) This is a subquery - 2) This is the first time this query is optimized (since the - transformation is permanent - 3) Not normalizing a view. Removal should take place when a - query involving a view is optimized, not when the view - is created - */ - if (select_lex->master_unit()->item && // 1) - select_lex->first_cond_optimization && // 2) - !thd->lex->is_view_context_analysis()) // 3) - { - remove_redundant_subquery_clauses(select_lex); - } - /* System Versioning: handle FOR SYSTEM_TIME clause. */ if (select_lex->vers_setup_conds(thd, tables_list) < 0) DBUG_RETURN(-1); @@ -1264,6 +1283,23 @@ JOIN::prepare(TABLE_LIST *tables_init, &hidden_group_fields, &select_lex->select_n_reserved)) DBUG_RETURN(-1); + + /* + Permanently remove redundant parts from the query if + 1) This is a subquery + 2) This is the first time this query is optimized (since the + transformation is permanent + 3) Not normalizing a view. Removal should take place when a + query involving a view is optimized, not when the view + is created + */ + if (select_lex->master_unit()->item && // 1) + select_lex->first_cond_optimization && // 2) + !thd->lex->is_view_context_analysis()) // 3) + { + remove_redundant_subquery_clauses(select_lex); + } + /* Resolve the ORDER BY that was skipped, then remove it. */ if (skip_order_by && select_lex != select_lex->master_unit()->global_parameters()) @@ -1584,10 +1620,11 @@ bool JOIN::build_explain() curr_tab->tracker= thd->lex->explain->get_union(select_nr)-> get_tmptable_read_tracker(); } - else + else if (select_nr < INT_MAX) { - curr_tab->tracker= thd->lex->explain->get_select(select_nr)-> - get_using_temporary_read_tracker(); + Explain_select *tmp= thd->lex->explain->get_select(select_nr); + if (tmp) + curr_tab->tracker= tmp->get_using_temporary_read_tracker(); } } DBUG_RETURN(0); @@ -1807,7 +1844,7 @@ int JOIN::init_join_caches() int JOIN::optimize_inner() { - DBUG_ENTER("JOIN::optimize"); + DBUG_ENTER("JOIN::optimize_inner"); subq_exit_fl= false; do_send_rows = (unit->select_limit_cnt) ? 1 : 0; @@ -1880,6 +1917,10 @@ JOIN::optimize_inner() table_count= select_lex->leaf_tables.elements; + if (select_lex->options & OPTION_SCHEMA_TABLE && + optimize_schema_tables_memory_usage(select_lex->leaf_tables)) + DBUG_RETURN(1); + if (setup_ftfuncs(select_lex)) /* should be after having->fix_fields */ DBUG_RETURN(-1); @@ -2093,7 +2134,7 @@ JOIN::optimize_inner() join->optimization_state == JOIN::OPTIMIZATION_PHASE_1_DONE && join->with_two_phase_optimization) continue; - /* + /* Do not push conditions from where into materialized inner tables of outer joins: this is not valid. */ @@ -2294,7 +2335,7 @@ setup_subq_exit: if (with_two_phase_optimization) optimization_state= JOIN::OPTIMIZATION_PHASE_1_DONE; else - { + { if (optimize_stage2()) DBUG_RETURN(1); } @@ -2314,7 +2355,7 @@ int JOIN::optimize_stage2() if (unlikely(thd->check_killed())) DBUG_RETURN(1); - + /* Generate an execution plan from the found optimal join order. */ if (get_best_combination()) DBUG_RETURN(1); @@ -3961,7 +4002,7 @@ bool JOIN::setup_subquery_caches() if (tmp_having) { DBUG_ASSERT(having == NULL); - if (!(tmp_having= + if (!(tmp_having= tmp_having->transform(thd, &Item::expr_cache_insert_transformer, NULL))) @@ -4648,6 +4689,9 @@ mysql_select(THD *thd, } else { + if (thd->lex->describe) + select_options|= SELECT_DESCRIBE; + /* When in EXPLAIN, delay deleting the joins so that they are still available when we're producing EXPLAIN EXTENDED warning text. @@ -4901,6 +4945,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, /* The following should be optimized to only clear critical things */ bzero((void*)stat, sizeof(JOIN_TAB)* table_count); + /* Initialize POSITION objects */ for (i=0 ; i <= table_count ; i++) (void) new ((char*) (join->positions + i)) POSITION; @@ -6889,7 +6934,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, Special treatment for ft-keys. */ -bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse, +bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse, bool skip_unprefixed_keyparts) { KEYUSE key_end, *prev, *save_pos, *use; @@ -8060,7 +8105,7 @@ best_access_path(JOIN *join, pos->use_join_buffer= best_uses_jbuf; pos->spl_plan= spl_plan; pos->range_rowid_filter_info= best_filter; - + loose_scan_opt.save_to_position(s, loose_scan_pos); if (!best_key && @@ -10204,7 +10249,7 @@ bool JOIN::check_two_phase_optimization(THD *thd) return true; return false; } - + bool JOIN::inject_cond_into_where(Item *injected_cond) { @@ -10235,7 +10280,7 @@ bool JOIN::inject_cond_into_where(Item *injected_cond) and_args->push_back(elem, thd->mem_root); } } - + return false; } @@ -13363,10 +13408,6 @@ void JOIN_TAB::cleanup() { DBUG_ENTER("JOIN_TAB::cleanup"); - if (tab_list && tab_list->is_with_table_recursive_reference() && - tab_list->with->is_cleaned()) - DBUG_VOID_RETURN; - DBUG_PRINT("enter", ("tab: %p table %s.%s", this, (table ? table->s->db.str : "?"), @@ -13539,10 +13580,12 @@ ha_rows JOIN_TAB::get_examined_rows() bool JOIN_TAB::preread_init() { TABLE_LIST *derived= table->pos_in_table_list; + DBUG_ENTER("JOIN_TAB::preread_init"); + if (!derived || !derived->is_materialized_derived()) { preread_init_done= TRUE; - return FALSE; + DBUG_RETURN(FALSE); } /* Materialize derived table/view. */ @@ -13551,7 +13594,7 @@ bool JOIN_TAB::preread_init() derived->get_unit()->uncacheable) && mysql_handle_single_derived(join->thd->lex, derived, DT_CREATE | DT_FILL)) - return TRUE; + DBUG_RETURN(TRUE); if (!(derived->get_unit()->uncacheable & UNCACHEABLE_DEPENDENT) || derived->is_nonrecursive_derived_with_rec_ref()) @@ -13569,9 +13612,9 @@ bool JOIN_TAB::preread_init() /* init ftfuns for just initialized derived table */ if (table->fulltext_searched) if (init_ftfuncs(join->thd, join->select_lex, MY_TEST(join->order))) - return TRUE; + DBUG_RETURN(TRUE); - return FALSE; + DBUG_RETURN(FALSE); } @@ -14136,6 +14179,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, { table_map order_tables=order->item[0]->used_tables(); if (order->item[0]->with_sum_func() || + order->item[0]->with_window_func || /* If the outer table of an outer join is const (either by itself or after applying WHERE condition), grouping on a field from such a @@ -14391,22 +14435,71 @@ return_zero_rows(JOIN *join, select_result *result, List<TABLE_LIST> &tables, DBUG_RETURN(0); } -/* - used only in JOIN::clear +/** + used only in JOIN::clear (always) and in do_select() + (if there where no matching rows) + + @param join JOIN + @param cleared_tables If not null, clear also const tables and mark all + cleared tables in the map. cleared_tables is only + set when called from do_select() when there is a + group function and there where no matching rows. */ -static void clear_tables(JOIN *join) + +static void clear_tables(JOIN *join, table_map *cleared_tables) { /* - must clear only the non-const tables, as const tables - are not re-calculated. + must clear only the non-const tables as const tables are not re-calculated. */ for (uint i= 0 ; i < join->table_count ; i++) { - if (!(join->table[i]->map & join->const_table_map)) - mark_as_null_row(join->table[i]); // All fields are NULL + TABLE *table= join->table[i]; + + if (table->null_row) + continue; // Nothing more to do + if (!(table->map & join->const_table_map) || cleared_tables) + { + if (cleared_tables) + { + (*cleared_tables)|= (((table_map) 1) << i); + if (table->s->null_bytes) + { + /* + Remember null bits for the record so that we can restore the + original const record in unclear_tables() + */ + memcpy(table->record[1], table->null_flags, table->s->null_bytes); + } + } + mark_as_null_row(table); // All fields are NULL + } + } +} + + +/** + Reverse null marking for tables and restore null bits. + + We have to do this because the tables may be re-used in a sub query + and the subquery will assume that the const tables contains the original + data before clear_tables(). +*/ + +static void unclear_tables(JOIN *join, table_map *cleared_tables) +{ + for (uint i= 0 ; i < join->table_count ; i++) + { + if ((*cleared_tables) & (((table_map) 1) << i)) + { + TABLE *table= join->table[i]; + if (table->s->null_bytes) + memcpy(table->null_flags, table->record[1], table->s->null_bytes); + unmark_as_null_row(table); + } } } + /***************************************************************************** Make som simple condition optimization: If there is a test 'field = const' change all refs to 'field' to 'const' @@ -15938,7 +16031,7 @@ static void update_const_equal_items(THD *thd, COND *cond, JOIN_TAB *tab, Item_func::COND_AND_FUNC)); } else if (cond->type() == Item::FUNC_ITEM && - ((Item_cond*) cond)->functype() == Item_func::MULT_EQUAL_FUNC) + ((Item_func*) cond)->functype() == Item_func::MULT_EQUAL_FUNC) { Item_equal *item_equal= (Item_equal *) cond; bool contained_const= item_equal->get_const() != NULL; @@ -16133,7 +16226,7 @@ propagate_cond_constants(THD *thd, I_List<COND_CMP> *save_list, (((Item_func*) cond)->functype() == Item_func::EQ_FUNC || ((Item_func*) cond)->functype() == Item_func::EQUAL_FUNC)) { - Item_func_eq *func=(Item_func_eq*) cond; + Item_bool_func2 *func= dynamic_cast<Item_bool_func2*>(cond); Item **args= func->arguments(); bool left_const= args[0]->const_item() && !args[0]->is_expensive(); bool right_const= args[1]->const_item() && !args[1]->is_expensive(); @@ -17073,7 +17166,7 @@ void propagate_new_equalities(THD *thd, Item *cond, } } else if (cond->type() == Item::FUNC_ITEM && - ((Item_cond*) cond)->functype() == Item_func::MULT_EQUAL_FUNC) + ((Item_func*) cond)->functype() == Item_func::MULT_EQUAL_FUNC) { Item_equal *equal_item; List_iterator<Item_equal> it(*new_equalities); @@ -17318,7 +17411,7 @@ Item_cond::remove_eq_conds(THD *thd, Item::cond_result *cond_value, } else if (and_level && new_item->type() == Item::FUNC_ITEM && - ((Item_cond*) new_item)->functype() == + ((Item_func*) new_item)->functype() == Item_func::MULT_EQUAL_FUNC) { li.remove(); @@ -17903,17 +17996,34 @@ Field *Item_field::create_tmp_field_ex(TABLE *table, src->set_field(field); if (!(result= create_tmp_field_from_item_field(table, NULL, param))) return NULL; - /* - Fields that are used as arguments to the DEFAULT() function already have - their data pointers set to the default value during name resolution. See - Item_default_value::fix_fields. - */ - if (type() != Item::DEFAULT_VALUE_ITEM && field->eq_def(result)) + if (field->eq_def(result)) src->set_default_field(field); return result; } +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)) + { + /* + We have to use a copy function when using a blob with default value + as the we have to calculate the default value before we can use it. + */ + get_tmp_field_src(src, param); + return tmp_table_field_from_field_type(table); + } + /* + Same code as in Item_field::create_tmp_field_ex, except no default field + handling + */ + src->set_field(field); + return create_tmp_field_from_item_field(table, NULL, param); +} + + Field *Item_ref::create_tmp_field_ex(TABLE *table, Tmp_field_src *src, const Tmp_field_param *param) @@ -18021,7 +18131,13 @@ Field *Item_func_sp::create_tmp_field_ex(TABLE *table, the record in the original table. If modify_item is 0 then fill_record() will update the temporary table - + @param table_cant_handle_bit_fields + Set to 1 if the temporary table cannot handle bit + fields. Only set for heap tables when the bit field + is part of an index. + @param make_copy_field + Set when using with rollup when we want to have + an exact copy of the field. @retval 0 on error @retval @@ -19885,6 +20001,7 @@ do_select(JOIN *join, Procedure *procedure) if (join->only_const_tables() && !join->need_tmp) { Next_select_func end_select= setup_end_select_func(join, NULL); + /* HAVING will be checked after processing aggregate functions, But WHERE should checked here (we alredy have read tables). @@ -19911,12 +20028,29 @@ do_select(JOIN *join, Procedure *procedure) } else if (join->send_row_on_empty_set()) { + table_map cleared_tables= (table_map) 0; + if (end_select == end_send_group) + { + /* + Was a grouping query but we did not find any rows. In this case + we clear all tables to get null in any referenced fields, + like in case of: + SELECT MAX(a) AS f1, a AS f2 FROM t1 WHERE VALUE(a) IS NOT NULL + */ + clear_tables(join, &cleared_tables); + } if (!join->having || join->having->val_int()) { List<Item> *columns_list= (procedure ? &join->procedure_fields_list : join->fields); rc= join->result->send_data(*columns_list) > 0; } + /* + We have to remove the null markings from the tables as this table + may be part of a sub query that is re-evaluated + */ + if (cleared_tables) + unclear_tables(join, &cleared_tables); } /* An error can happen when evaluating the conds @@ -20890,8 +21024,8 @@ join_read_const_table(THD *thd, JOIN_TAB *tab, POSITION *pos) if ((table->null_row= MY_TEST((*tab->on_expr_ref)->val_int() == 0))) mark_as_null_row(table); } - if (!table->null_row) - table->maybe_null=0; + if (!table->null_row && ! tab->join->mixed_implicit_grouping) + table->maybe_null= 0; { JOIN *join= tab->join; @@ -22398,6 +22532,21 @@ make_cond_for_table_from_pred(THD *thd, Item *root_cond, Item *cond, cond->marker=3; // Checked when read return (COND*) 0; } + /* + If cond is an equality injected for split optimization then + a. when retain_ref_cond == false : cond is removed unconditionally + (cond that supports ref access is removed by the preceding code) + b. when retain_ref_cond == true : cond is removed if it does not + support ref access + */ + if (left_item->type() == Item::FIELD_ITEM && + is_eq_cond_injected_for_split_opt((Item_func_eq *) cond) && + (!retain_ref_cond || + !test_if_ref(root_cond, (Item_field*) left_item,right_item))) + { + cond->marker=3; + return (COND*) 0; + } } cond->marker=2; cond->set_join_tab_idx(join_tab_idx_arg); @@ -24036,7 +24185,7 @@ bool cp_buffer_from_ref(THD *thd, TABLE *table, TABLE_REF *ref) { Check_level_instant_set check_level_save(thd, CHECK_FIELD_IGNORE); - my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set); + MY_BITMAP *old_map= dbug_tmp_use_all_columns(table, &table->write_set); bool result= 0; for (store_key **copy=ref->key_copy ; *copy ; copy++) @@ -24047,7 +24196,7 @@ cp_buffer_from_ref(THD *thd, TABLE *table, TABLE_REF *ref) break; } } - dbug_tmp_restore_column_map(table->write_set, old_map); + dbug_tmp_restore_column_map(&table->write_set, old_map); return result; } @@ -25086,8 +25235,8 @@ copy_fields(TMP_TABLE_PARAM *param) (*ptr->do_copy)(ptr); List_iterator_fast<Item> it(param->copy_funcs); - Item_copy_string *item; - while ((item = (Item_copy_string*) it++)) + Item_copy *item; + while ((item= (Item_copy*) it++)) item->copy(); } @@ -25718,7 +25867,7 @@ bool JOIN::rollup_init() { if (!(rollup.null_items[i]= new (thd->mem_root) Item_null_result(thd))) return true; - + List<Item> *rollup_fields= &rollup.fields[i]; rollup_fields->empty(); rollup.ref_pointer_arrays[i]= Ref_ptr_array(ref_array, all_fields.elements); @@ -26051,7 +26200,7 @@ int JOIN::rollup_write_data(uint idx, TMP_TABLE_PARAM *tmp_table_param_arg, TABL void JOIN::clear() { - clear_tables(this); + clear_tables(this, 0); copy_fields(&tmp_table_param); if (sum_funcs) @@ -26228,7 +26377,7 @@ bool JOIN_TAB::save_explain_data(Explain_table_access *eta, { JOIN_TAB *ctab= bush_children->start; /* table */ - size_t len= my_snprintf(table_name_buffer, + size_t len= my_snprintf(table_name_buffer, sizeof(table_name_buffer)-1, "<subquery%d>", ctab->emb_sj_nest->sj_subq_pred->get_identifier()); @@ -27403,7 +27552,7 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str, void st_select_lex::print(THD *thd, String *str, enum_query_type query_type) { DBUG_ASSERT(thd); - + if (tvc) { tvc->print(thd, str, query_type); @@ -28910,6 +29059,7 @@ select_handler *SELECT_LEX::find_select_handler(THD *thd) } + /** @} (end of group Query_Optimizer) */ diff --git a/sql/sql_select.h b/sql/sql_select.h index e14907d73bc..d21d1bcc305 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -698,8 +698,6 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), bool end_of_records); -struct st_position; - class Semi_join_strategy_picker { public: @@ -710,7 +708,7 @@ public: Update internal state after another table has been added to the join prefix */ - virtual void set_from_prev(struct st_position *prev) = 0; + virtual void set_from_prev(POSITION *prev) = 0; virtual bool check_qep(JOIN *join, uint idx, @@ -720,7 +718,7 @@ public: double *read_time, table_map *handled_fanout, sj_strategy_enum *strategy, - struct st_position *loose_scan_pos) = 0; + POSITION *loose_scan_pos) = 0; virtual void mark_used() = 0; @@ -751,7 +749,7 @@ public: first_dupsweedout_table= MAX_TABLES; is_used= FALSE; } - void set_from_prev(struct st_position *prev); + void set_from_prev(POSITION *prev); bool check_qep(JOIN *join, uint idx, @@ -761,7 +759,7 @@ public: double *read_time, table_map *handled_fanout, sj_strategy_enum *stratey, - struct st_position *loose_scan_pos); + POSITION *loose_scan_pos); void mark_used() { is_used= TRUE; } friend void fix_semijoin_strategies_for_picked_join_order(JOIN *join); @@ -797,7 +795,7 @@ public: is_used= FALSE; } - void set_from_prev(struct st_position *prev); + void set_from_prev(POSITION *prev); bool check_qep(JOIN *join, uint idx, table_map remaining_tables, @@ -806,7 +804,7 @@ public: double *read_time, table_map *handled_fanout, sj_strategy_enum *strategy, - struct st_position *loose_scan_pos); + POSITION *loose_scan_pos); void mark_used() { is_used= TRUE; } friend void fix_semijoin_strategies_for_picked_join_order(JOIN *join); @@ -815,6 +813,7 @@ public: class LooseScan_picker : public Semi_join_strategy_picker { +public: /* The first (i.e. driving) table we're doing loose scan for */ uint first_loosescan_table; /* @@ -833,14 +832,13 @@ class LooseScan_picker : public Semi_join_strategy_picker uint loosescan_parts; /* Number of keyparts to be kept distinct */ bool is_used; -public: void set_empty() { first_loosescan_table= MAX_TABLES; is_used= FALSE; } - void set_from_prev(struct st_position *prev); + void set_from_prev(POSITION *prev); bool check_qep(JOIN *join, uint idx, table_map remaining_tables, @@ -849,19 +847,19 @@ public: double *read_time, table_map *handled_fanout, sj_strategy_enum *strategy, - struct st_position *loose_scan_pos); + POSITION *loose_scan_pos); void mark_used() { is_used= TRUE; } friend class Loose_scan_opt; friend void best_access_path(JOIN *join, JOIN_TAB *s, table_map remaining_tables, - const struct st_position *join_positions, + const POSITION *join_positions, uint idx, bool disable_jbuf, double record_count, - struct st_position *pos, - struct st_position *loose_scan_pos); + POSITION *pos, + POSITION *loose_scan_pos); friend bool get_best_combination(JOIN *join); friend int setup_semijoin_loosescan(JOIN *join); friend void fix_semijoin_strategies_for_picked_join_order(JOIN *join); @@ -888,7 +886,7 @@ public: sjm_scan_last_inner= 0; is_used= FALSE; } - void set_from_prev(struct st_position *prev); + void set_from_prev(POSITION *prev); bool check_qep(JOIN *join, uint idx, table_map remaining_tables, @@ -897,7 +895,7 @@ public: double *read_time, table_map *handled_fanout, sj_strategy_enum *strategy, - struct st_position *loose_scan_pos); + POSITION *loose_scan_pos); void mark_used() { is_used= TRUE; } friend void fix_semijoin_strategies_for_picked_join_order(JOIN *join); @@ -912,8 +910,9 @@ class Rowid_filter; Information about a position of table within a join order. Used in join optimization. */ -typedef struct st_position +class POSITION { +public: /* The table that's put into join order */ JOIN_TAB *table; @@ -925,7 +924,7 @@ typedef struct st_position double records_read; /* The selectivity of the pushed down conditions */ - double cond_selectivity; + double cond_selectivity; /* Cost accessing the table in course of the entire complete join execution, @@ -934,8 +933,6 @@ typedef struct st_position */ double read_time; - /* Cumulative cost and record count for the join prefix */ - Cost_estimate prefix_cost; double prefix_record_count; /* @@ -944,29 +941,46 @@ typedef struct st_position */ KEYUSE *key; + /* Info on splitting plan used at this position */ + SplM_plan_info *spl_plan; + + /* Cost info for the range filter used at this position */ + Range_rowid_filter_cost_info *range_rowid_filter_info; + /* If ref-based access is used: bitmap of tables this table depends on */ table_map ref_depend_map; - + /* - TRUE <=> join buffering will be used. At the moment this is based on - *very* imprecise guesses made in best_access_path(). + Bitmap of semi-join inner tables that are in the join prefix and for + which there's no provision for how to eliminate semi-join duplicates + they produce. */ - bool use_join_buffer; - + table_map dups_producing_tables; + + table_map inner_tables_handled_with_other_sjs; + + Duplicate_weedout_picker dups_weedout_picker; + Firstmatch_picker firstmatch_picker; + LooseScan_picker loosescan_picker; + Sj_materialization_picker sjmat_picker; + + /* Cumulative cost and record count for the join prefix */ + Cost_estimate prefix_cost; + /* Current optimization state: Semi-join strategy to be used for this and preceding join tables. - + Join optimizer sets this for the *last* join_tab in the - duplicate-generating range. That is, in order to interpret this field, + duplicate-generating range. That is, in order to interpret this field, one needs to traverse join->[best_]positions array from right to left. When you see a join table with sj_strategy!= SJ_OPT_NONE, some other - field (depending on the strategy) tells how many preceding positions + field (depending on the strategy) tells how many preceding positions this applies to. The values of covered_preceding_positions->sj_strategy must be ignored. */ enum sj_strategy_enum sj_strategy; - + /* Valid only after fix_semijoin_strategies_for_picked_join_order() call: if sj_strategy!=SJ_OPT_NONE, this is the number of subsequent tables that @@ -975,26 +989,12 @@ typedef struct st_position uint n_sj_tables; /* - Bitmap of semi-join inner tables that are in the join prefix and for - which there's no provision for how to eliminate semi-join duplicates - they produce. + TRUE <=> join buffering will be used. At the moment this is based on + *very* imprecise guesses made in best_access_path(). */ - table_map dups_producing_tables; - - table_map inner_tables_handled_with_other_sjs; - - Duplicate_weedout_picker dups_weedout_picker; - Firstmatch_picker firstmatch_picker; - LooseScan_picker loosescan_picker; - Sj_materialization_picker sjmat_picker; - - /* Info on splitting plan used at this position */ - SplM_plan_info *spl_plan; - - /* Cost info for the range filter used at this position */ - Range_rowid_filter_cost_info *range_rowid_filter_info; - -} POSITION; + bool use_join_buffer; + POSITION(); +}; typedef Bounds_checked_array<Item_null_result*> Item_null_array; @@ -1590,6 +1590,7 @@ public: fields_list= fields_arg; non_agg_fields.empty(); bzero((char*) &keyuse,sizeof(keyuse)); + having_value= Item::COND_UNDEF; tmp_table_param.init(); tmp_table_param.end_write_records= HA_POS_ERROR; rollup.state= ROLLUP::STATE_NONE; @@ -1948,8 +1949,8 @@ class store_key_field: public store_key enum store_key_result copy_inner() { TABLE *table= copy_field.to_field->table; - my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, - table->write_set); + MY_BITMAP *old_map= dbug_tmp_use_all_columns(table, + &table->write_set); /* It looks like the next statement is needed only for a simplified @@ -1960,7 +1961,7 @@ class store_key_field: public store_key bzero(copy_field.to_ptr,copy_field.to_length); copy_field.do_copy(©_field); - dbug_tmp_restore_column_map(table->write_set, old_map); + dbug_tmp_restore_column_map(&table->write_set, old_map); null_key= to_field->is_null(); return err != 0 ? STORE_KEY_FATAL : STORE_KEY_OK; } @@ -1995,8 +1996,8 @@ public: enum store_key_result copy_inner() { TABLE *table= to_field->table; - my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, - table->write_set); + MY_BITMAP *old_map= dbug_tmp_use_all_columns(table, + &table->write_set); int res= FALSE; /* @@ -2017,7 +2018,7 @@ public: */ if (!res && table->in_use->is_error()) res= 1; /* STORE_KEY_FATAL */ - dbug_tmp_restore_column_map(table->write_set, old_map); + dbug_tmp_restore_column_map(&table->write_set, old_map); null_key= to_field->is_null() || item->null_value; return ((err != 0 || res < 0 || res > 2) ? STORE_KEY_FATAL : (store_key_result) res); @@ -2053,8 +2054,8 @@ protected: { inited=1; TABLE *table= to_field->table; - my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, - table->write_set); + MY_BITMAP *old_map= dbug_tmp_use_all_columns(table, + &table->write_set); if ((res= item->save_in_field(to_field, 1))) { if (!err) @@ -2066,7 +2067,7 @@ protected: */ if (!err && to_field->table->in_use->is_error()) err= 1; /* STORE_KEY_FATAL */ - dbug_tmp_restore_column_map(table->write_set, old_map); + dbug_tmp_restore_column_map(&table->write_set, old_map); } null_key= to_field->is_null() || item->null_value; return (err > 2 ? STORE_KEY_FATAL : (store_key_result) err); diff --git a/sql/sql_sequence.cc b/sql/sql_sequence.cc index a2bcfd5a4ff..83091cd67da 100644 --- a/sql/sql_sequence.cc +++ b/sql/sql_sequence.cc @@ -136,7 +136,7 @@ bool sequence_definition::check_and_adjust(bool set_reserved_until) void sequence_definition::read_fields(TABLE *table) { - my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); + MY_BITMAP *old_map= dbug_tmp_use_all_columns(table, &table->read_set); reserved_until= table->field[0]->val_int(); min_value= table->field[1]->val_int(); max_value= table->field[2]->val_int(); @@ -145,7 +145,7 @@ void sequence_definition::read_fields(TABLE *table) cache= table->field[5]->val_int(); cycle= table->field[6]->val_int(); round= table->field[7]->val_int(); - dbug_tmp_restore_column_map(table->read_set, old_map); + dbug_tmp_restore_column_map(&table->read_set, old_map); used_fields= ~(uint) 0; print_dbug(); } @@ -157,7 +157,7 @@ void sequence_definition::read_fields(TABLE *table) void sequence_definition::store_fields(TABLE *table) { - my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set); + MY_BITMAP *old_map= dbug_tmp_use_all_columns(table, &table->write_set); /* zero possible delete markers & null bits */ memcpy(table->record[0], table->s->default_values, table->s->null_bytes); @@ -170,7 +170,7 @@ void sequence_definition::store_fields(TABLE *table) table->field[6]->store((longlong) cycle != 0, 0); table->field[7]->store((longlong) round, 1); - dbug_tmp_restore_column_map(table->write_set, old_map); + dbug_tmp_restore_column_map(&table->write_set, old_map); print_dbug(); } @@ -527,12 +527,11 @@ int SEQUENCE::read_initial_values(TABLE *table) int SEQUENCE::read_stored_values(TABLE *table) { int error; - my_bitmap_map *save_read_set; DBUG_ENTER("SEQUENCE::read_stored_values"); - save_read_set= tmp_use_all_columns(table, table->read_set); + MY_BITMAP *save_read_set= tmp_use_all_columns(table, &table->read_set); error= table->file->ha_read_first_row(table->record[0], MAX_KEY); - tmp_restore_column_map(table->read_set, save_read_set); + tmp_restore_column_map(&table->read_set, save_read_set); if (unlikely(error)) { @@ -731,8 +730,8 @@ longlong SEQUENCE::next_value(TABLE *table, bool second_round, int *error) if (real_increment > 0) { - if (reserved_until + add_to > max_value || - reserved_until > max_value - add_to) + if (reserved_until > max_value - add_to || + reserved_until + add_to > max_value) { reserved_until= max_value + 1; out_of_values= res_value >= reserved_until; diff --git a/sql/sql_sequence.h b/sql/sql_sequence.h index 2d609d8591b..29c589e67cd 100644 --- a/sql/sql_sequence.h +++ b/sql/sql_sequence.h @@ -111,8 +111,8 @@ public: { if (real_increment > 0) { - if (value + real_increment > max_value || - value > max_value - real_increment) + if (value > max_value - real_increment || + value + real_increment > max_value) value= max_value + 1; else value+= real_increment; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 1fdd2d7c8d0..6f0c9761b6c 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2147,7 +2147,6 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, !foreign_db_mode; bool check_options= !(sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS) && !create_info_arg; - my_bitmap_map *old_map; handlerton *hton; int error= 0; DBUG_ENTER("show_create_table"); @@ -2214,7 +2213,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, We have to restore the read_set if we are called from insert in case of row based replication. */ - old_map= tmp_use_all_columns(table, table->read_set); + MY_BITMAP *old_map= tmp_use_all_columns(table, &table->read_set); bool not_the_first_field= false; for (ptr=table->field ; (field= *ptr); ptr++) @@ -2259,8 +2258,13 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, /* For string types dump collation name only if collation is not primary for the given charset + + For generated fields don't print the COLLATE clause if + the collation matches the expression's collation. */ - if (!(field->charset()->state & MY_CS_PRIMARY) && !field->vcol_info) + if (!(field->charset()->state & MY_CS_PRIMARY) && + (!field->vcol_info || + field->charset() != field->vcol_info->expr->collation.collation)) { packet->append(STRING_WITH_LEN(" COLLATE ")); packet->append(field->charset()->name); @@ -2516,7 +2520,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, } } #endif - tmp_restore_column_map(table->read_set, old_map); + tmp_restore_column_map(&table->read_set, old_map); DBUG_RETURN(error); } @@ -3252,11 +3256,8 @@ int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond) } DBUG_RETURN(bres); } - else - { - my_error(ER_NO_SUCH_THREAD, MYF(0), (ulong) thread_id); - DBUG_RETURN(1); - } + my_error(ER_NO_SUCH_THREAD, MYF(0), (ulong) thread_id); + DBUG_RETURN(1); } @@ -3832,6 +3833,9 @@ static bool show_status_array(THD *thd, const char *wild, if (show_type == SHOW_SYS) mysql_mutex_lock(&LOCK_global_system_variables); + else if (show_type >= SHOW_LONG_STATUS && scope == OPT_GLOBAL) + calc_sum_of_all_status_if_needed(status_var); + pos= get_one_variable(thd, var, scope, show_type, status_var, &charset, buff, &length); @@ -3889,7 +3893,6 @@ uint calc_sum_of_all_status(STATUS_VAR *to) calc_sum_callback_arg arg(to); DBUG_ENTER("calc_sum_of_all_status"); - *to= global_status_var; to->local_memory_used= 0; /* Add to this status from existing threads */ server_threads.iterate(calc_sum_callback, &arg); @@ -5099,7 +5102,8 @@ public: Sql_condition::enum_warning_level *level, const char* msg, Sql_condition ** cond_hdl) { - if (sql_errno == ER_TRG_NO_DEFINER || sql_errno == ER_TRG_NO_CREATION_CTX) + if (sql_errno == ER_TRG_NO_DEFINER || sql_errno == ER_TRG_NO_CREATION_CTX + || sql_errno == ER_PARSE_ERROR) return true; if (*level != Sql_condition::WARN_LEVEL_ERROR) @@ -5297,6 +5301,12 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) continue; } + if (thd->killed == ABORT_QUERY) + { + error= 0; + goto err; + } + DEBUG_SYNC(thd, "before_open_in_get_all_tables"); if (fill_schema_table_by_open(thd, &tmp_mem_root, FALSE, table, schema_table, @@ -5865,7 +5875,7 @@ static bool print_anchor_data_type(const Spvar_definition *def, Let's print it according to the current sql_mode. It will make output in line with the value in mysql.proc.param_list, so both I_S.XXX.DTD_IDENTIFIER and mysql.proc.param_list use the same notation: - default or Oracle, according to the sql_mode at the SP creation time. + default or Oracle, according to the sql_mode at the SP creation time. The caller must make sure to set thd->variables.sql_mode to the routine sql_mode. */ static bool print_anchor_dtd_identifier(THD *thd, const Spvar_definition *def, @@ -7086,8 +7096,7 @@ static bool store_trigger(THD *thd, Trigger *trigger, (my_time_t)(trigger->create_time/100)); /* timestamp is with 6 digits */ timestamp.second_part= (trigger->create_time % 100) * 10000; - ((Field_temporal_with_date*) table->field[16])->store_time_dec(×tamp, - 2); + table->field[16]->store_time_dec(×tamp, 2); } sql_mode_string_representation(thd, trigger->sql_mode, &sql_mode_rep); @@ -7969,10 +7978,7 @@ int fill_status(THD *thd, TABLE_LIST *tables, COND *cond) if (partial_cond) partial_cond->val_int(); - if (scope == OPT_GLOBAL) - { - calc_sum_of_all_status(&tmp); - } + tmp.local_memory_used= 0; // meaning tmp was not populated yet mysql_rwlock_rdlock(&LOCK_all_status_vars); res= show_status_array(thd, wild, @@ -8704,57 +8710,72 @@ end: } -static int optimize_schema_tables_memory_usage(TABLE_LIST *table_list) +bool optimize_schema_tables_memory_usage(List<TABLE_LIST> &tables) { - TABLE *table= table_list->table; - THD *thd=table->in_use; - if (!table->is_created()) - { - TMP_TABLE_PARAM *p= table_list->schema_table_param; - TMP_ENGINE_COLUMNDEF *from_recinfo, *to_recinfo; - DBUG_ASSERT(table->s->keys == 0); - DBUG_ASSERT(table->s->uniques == 0); - - uchar *cur= table->field[0]->ptr; - /* first recinfo could be a NULL bitmap, not an actual Field */ - from_recinfo= to_recinfo= p->start_recinfo + (cur != table->record[0]); - for (uint i=0; i < table->s->fields; i++, from_recinfo++) - { - Field *field= table->field[i]; - DBUG_ASSERT(field->vcol_info == 0); - DBUG_ASSERT(from_recinfo->length); - DBUG_ASSERT(from_recinfo->length == field->pack_length_in_rec()); - if (bitmap_is_set(table->read_set, i)) + DBUG_ENTER("optimize_schema_tables_memory_usage"); + + List_iterator<TABLE_LIST> tli(tables); + + while (TABLE_LIST *table_list= tli++) + { + if (!table_list->schema_table) + continue; + + TABLE *table= table_list->table; + THD *thd=table->in_use; + + if (!thd->fill_information_schema_tables()) + continue; + + if (!table->is_created()) + { + TMP_TABLE_PARAM *p= table_list->schema_table_param; + TMP_ENGINE_COLUMNDEF *from_recinfo, *to_recinfo; + DBUG_ASSERT(table->s->keys == 0); + DBUG_ASSERT(table->s->uniques == 0); + + uchar *cur= table->field[0]->ptr; + /* first recinfo could be a NULL bitmap, not an actual Field */ + from_recinfo= to_recinfo= p->start_recinfo + (cur != table->record[0]); + for (uint i=0; i < table->s->fields; i++, from_recinfo++) { - field->move_field(cur); - *to_recinfo++= *from_recinfo; - cur+= from_recinfo->length; + Field *field= table->field[i]; + DBUG_ASSERT(field->vcol_info == 0); + DBUG_ASSERT(from_recinfo->length); + DBUG_ASSERT(from_recinfo->length == field->pack_length_in_rec()); + if (bitmap_is_set(table->read_set, i)) + { + field->move_field(cur); + *to_recinfo++= *from_recinfo; + cur+= from_recinfo->length; + } + else + { + field= new (thd->mem_root) Field_string(cur, 0, field->null_ptr, + field->null_bit, Field::NONE, + &field->field_name, field->dtcollation()); + field->init(table); + field->field_index= i; + DBUG_ASSERT(field->pack_length_in_rec() == 0); + table->field[i]= field; + } } - else + if ((table->s->reclength= (ulong)(cur - table->record[0])) == 0) { - field= new (thd->mem_root) Field_string(cur, 0, field->null_ptr, - field->null_bit, Field::NONE, - &field->field_name, field->dtcollation()); - field->init(table); - field->field_index= i; - DBUG_ASSERT(field->pack_length_in_rec() == 0); - table->field[i]= field; + /* all fields were optimized away. Force a non-0-length row */ + table->s->reclength= to_recinfo->length= 1; + to_recinfo->type= FIELD_NORMAL; + to_recinfo++; } - } - if ((table->s->reclength= (ulong)(cur - table->record[0])) == 0) - { - /* all fields were optimized away. Force a non-0-length row */ - table->s->reclength= to_recinfo->length= 1; - to_recinfo++; - } - p->recinfo= to_recinfo; + p->recinfo= to_recinfo; - // TODO switch from Aria to Memory if all blobs were optimized away? - if (instantiate_tmp_table(table, p->keyinfo, p->start_recinfo, &p->recinfo, - table_list->select_lex->options | thd->variables.option_bits)) - return 1; + // TODO switch from Aria to Memory if all blobs were optimized away? + if (instantiate_tmp_table(table, p->keyinfo, p->start_recinfo, &p->recinfo, + table_list->select_lex->options | thd->variables.option_bits)) + DBUG_RETURN(1); + } } - return 0; + DBUG_RETURN(0); } @@ -8778,9 +8799,6 @@ bool optimize_schema_tables_reads(JOIN *join) TABLE_LIST *table_list= tab->table->pos_in_table_list; if (table_list->schema_table && thd->fill_information_schema_tables()) { - if (optimize_schema_tables_memory_usage(table_list)) - DBUG_RETURN(1); - /* A value of 0 indicates a dummy implementation */ if (table_list->schema_table->fill_table == 0) continue; @@ -8865,6 +8883,16 @@ bool get_schema_tables_result(JOIN *join, if (table_list->schema_table->fill_table == 0) continue; + /* + Do not fill in tables thare are marked as JT_CONST as these will never + be read and they also don't have a tab->read_record.table set! + This can happen with queries like + SELECT * FROM t1 LEFT JOIN (t1 AS t1b JOIN INFORMATION_SCHEMA.ROUTINES) + ON (t1b.a IS NULL); + */ + if (tab->type == JT_CONST) + continue; + /* skip I_S optimizations specific to get_all_tables */ if (lex->describe && (table_list->schema_table->fill_table != get_all_tables)) @@ -9861,7 +9889,7 @@ ST_FIELD_INFO check_constraints_fields_info[]= {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, {"CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, - {"CHECK_CLAUSE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, + {"CHECK_CLAUSE", MAX_FIELD_VARCHARLENGTH , MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; diff --git a/sql/sql_show.h b/sql/sql_show.h index 39cbc35230a..c1845d8c1b3 100644 --- a/sql/sql_show.h +++ b/sql/sql_show.h @@ -238,6 +238,7 @@ public: }; bool optimize_schema_tables_reads(JOIN *join); +bool optimize_schema_tables_memory_usage(List<TABLE_LIST> &tables); /* Handle the ignored database directories list for SHOW/I_S. */ bool ignore_db_dirs_init(); diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index 717deeabe18..042e86fbd86 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -1022,15 +1022,14 @@ public: void store_stat_fields() { - char buff[MAX_FIELD_WIDTH]; - String val(buff, sizeof(buff), &my_charset_bin); - my_bitmap_map *old_map; + StringBuffer<MAX_FIELD_WIDTH> val; - old_map= dbug_tmp_use_all_columns(stat_table, stat_table->read_set); + MY_BITMAP *old_map= dbug_tmp_use_all_columns(stat_table, &stat_table->read_set); for (uint i= COLUMN_STAT_MIN_VALUE; i <= COLUMN_STAT_HISTOGRAM; i++) { Field *stat_field= stat_table->field[i]; - if (table_field->collected_stats->is_null(i)) + Column_statistics *stats= table_field->collected_stats; + if (stats->is_null(i)) stat_field->set_null(); else { @@ -1038,10 +1037,10 @@ public: switch (i) { case COLUMN_STAT_MIN_VALUE: if (table_field->type() == MYSQL_TYPE_BIT) - stat_field->store(table_field->collected_stats->min_value->val_int(),true); + stat_field->store(stats->min_value->val_int(),true); else { - table_field->collected_stats->min_value->val_str(&val); + stats->min_value->val_str(&val); size_t length= Well_formed_prefix(val.charset(), val.ptr(), MY_MIN(val.length(), stat_field->field_length)).length(); stat_field->store(val.ptr(), length, &my_charset_bin); @@ -1049,42 +1048,38 @@ public: break; case COLUMN_STAT_MAX_VALUE: if (table_field->type() == MYSQL_TYPE_BIT) - stat_field->store(table_field->collected_stats->max_value->val_int(),true); + stat_field->store(stats->max_value->val_int(),true); else { - table_field->collected_stats->max_value->val_str(&val); + stats->max_value->val_str(&val); size_t length= Well_formed_prefix(val.charset(), val.ptr(), MY_MIN(val.length(), stat_field->field_length)).length(); stat_field->store(val.ptr(), length, &my_charset_bin); } break; case COLUMN_STAT_NULLS_RATIO: - stat_field->store(table_field->collected_stats->get_nulls_ratio()); + stat_field->store(stats->get_nulls_ratio()); break; case COLUMN_STAT_AVG_LENGTH: - stat_field->store(table_field->collected_stats->get_avg_length()); + stat_field->store(stats->get_avg_length()); break; case COLUMN_STAT_AVG_FREQUENCY: - stat_field->store(table_field->collected_stats->get_avg_frequency()); + stat_field->store(stats->get_avg_frequency()); break; case COLUMN_STAT_HIST_SIZE: - stat_field->store(table_field->collected_stats->histogram.get_size()); + stat_field->store(stats->histogram.get_size()); break; case COLUMN_STAT_HIST_TYPE: - stat_field->store(table_field->collected_stats->histogram.get_type() + - 1); + stat_field->store(stats->histogram.get_type() + 1); break; case COLUMN_STAT_HISTOGRAM: - const char * col_histogram= - (const char *) (table_field->collected_stats->histogram.get_values()); - stat_field->store(col_histogram, - table_field->collected_stats->histogram.get_size(), - &my_charset_bin); + stat_field->store((char *)stats->histogram.get_values(), + stats->histogram.get_size(), &my_charset_bin); break; } } } - dbug_tmp_restore_column_map(stat_table->read_set, old_map); + dbug_tmp_restore_column_map(&stat_table->read_set, old_map); } @@ -1134,16 +1129,30 @@ public: switch (i) { case COLUMN_STAT_MIN_VALUE: - table_field->read_stats->min_value->set_notnull(); - stat_field->val_str(&val); - table_field->read_stats->min_value->store(val.ptr(), val.length(), - &my_charset_bin); + table_field->read_stats->min_value->set_notnull(); + if (table_field->type() == MYSQL_TYPE_BIT) + table_field->read_stats->min_value->store(stat_field->val_int(), + true); + else + { + stat_field->val_str(&val); + table_field->read_stats->min_value->store(val.ptr(), + val.length(), + &my_charset_bin); + } break; case COLUMN_STAT_MAX_VALUE: - table_field->read_stats->max_value->set_notnull(); - stat_field->val_str(&val); - table_field->read_stats->max_value->store(val.ptr(), val.length(), - &my_charset_bin); + table_field->read_stats->max_value->set_notnull(); + if (table_field->type() == MYSQL_TYPE_BIT) + table_field->read_stats->max_value->store(stat_field->val_int(), + true); + else + { + stat_field->val_str(&val); + table_field->read_stats->max_value->store(val.ptr(), + val.length(), + &my_charset_bin); + } break; case COLUMN_STAT_NULLS_RATIO: table_field->read_stats->set_nulls_ratio(stat_field->val_real()); @@ -2097,20 +2106,24 @@ void create_min_max_statistical_fields_for_table_share(THD *thd, int alloc_statistics_for_table(THD* thd, TABLE *table) { Field **field_ptr; - uint fields; DBUG_ENTER("alloc_statistics_for_table"); + uint columns= 0; + for (field_ptr= table->field; *field_ptr; field_ptr++) + { + if (bitmap_is_set(table->read_set, (*field_ptr)->field_index)) + columns++; + } Table_statistics *table_stats= (Table_statistics *) alloc_root(&table->mem_root, sizeof(Table_statistics)); - fields= table->s->fields ; Column_statistics_collected *column_stats= (Column_statistics_collected *) alloc_root(&table->mem_root, sizeof(Column_statistics_collected) * - (fields+1)); + columns); uint keys= table->s->keys; Index_statistics *index_stats= @@ -2121,12 +2134,6 @@ int alloc_statistics_for_table(THD* thd, TABLE *table) ulonglong *idx_avg_frequency= (ulonglong*) alloc_root(&table->mem_root, sizeof(ulonglong) * key_parts); - uint columns= 0; - for (field_ptr= table->field; *field_ptr; field_ptr++) - { - if (bitmap_is_set(table->read_set, (*field_ptr)->field_index)) - columns++; - } uint hist_size= thd->variables.histogram_size; Histogram_type hist_type= (Histogram_type) (thd->variables.histogram_type); uchar *histogram= NULL; @@ -2148,19 +2155,17 @@ int alloc_statistics_for_table(THD* thd, TABLE *table) table_stats->idx_avg_frequency= idx_avg_frequency; table_stats->histograms= histogram; - memset(column_stats, 0, sizeof(Column_statistics) * (fields+1)); + memset(column_stats, 0, sizeof(Column_statistics) * columns); - for (field_ptr= table->field; *field_ptr; field_ptr++, column_stats++) + for (field_ptr= table->field; *field_ptr; field_ptr++) { - (*field_ptr)->collected_stats= column_stats; - (*field_ptr)->collected_stats->max_value= NULL; - (*field_ptr)->collected_stats->min_value= NULL; if (bitmap_is_set(table->read_set, (*field_ptr)->field_index)) { column_stats->histogram.set_size(hist_size); column_stats->histogram.set_type(hist_type); column_stats->histogram.set_values(histogram); histogram+= hist_size; + (*field_ptr)->collected_stats= column_stats++; } } @@ -2655,7 +2660,7 @@ int collect_statistics_for_table(THD *thd, TABLE *table) for (field_ptr= table->field; *field_ptr; field_ptr++) { table_field= *field_ptr; - if (!bitmap_is_set(table->read_set, table_field->field_index)) + if (!table_field->collected_stats) continue; table_field->collected_stats->init(thd, table_field); } @@ -2680,7 +2685,7 @@ int collect_statistics_for_table(THD *thd, TABLE *table) for (field_ptr= table->field; *field_ptr; field_ptr++) { table_field= *field_ptr; - if (!bitmap_is_set(table->read_set, table_field->field_index)) + if (!table_field->collected_stats) continue; if ((rc= table_field->collected_stats->add())) break; @@ -2710,7 +2715,7 @@ int collect_statistics_for_table(THD *thd, TABLE *table) for (field_ptr= table->field; *field_ptr; field_ptr++) { table_field= *field_ptr; - if (!bitmap_is_set(table->read_set, table_field->field_index)) + if (!table_field->collected_stats) continue; bitmap_set_bit(table->write_set, table_field->field_index); if (!rc) @@ -2814,7 +2819,7 @@ int update_statistics_for_table(THD *thd, TABLE *table) for (Field **field_ptr= table->field; *field_ptr; field_ptr++) { Field *table_field= *field_ptr; - if (!bitmap_is_set(table->read_set, table_field->field_index)) + if (!table_field->collected_stats) continue; restore_record(stat_table, s->default_values); column_stat.set_key_fields(table_field); @@ -3741,6 +3746,7 @@ double get_column_range_cardinality(Field *field, if (!table->stats_is_read) return tab_records; + THD *thd= table->in_use; double col_nulls= tab_records * col_stats->get_nulls_ratio(); double col_non_nulls= tab_records - col_nulls; @@ -3771,7 +3777,7 @@ double get_column_range_cardinality(Field *field, col_stats->min_max_values_are_provided()) { Histogram *hist= &col_stats->histogram; - if (hist->is_available()) + if (hist->is_usable(thd)) { store_key_image_to_rec(field, (uchar *) min_endp->key, field->key_length()); @@ -3815,10 +3821,10 @@ double get_column_range_cardinality(Field *field, max_mp_pos= 1.0; Histogram *hist= &col_stats->histogram; - if (!hist->is_available()) - sel= (max_mp_pos - min_mp_pos); - else + if (hist->is_usable(thd)) sel= hist->range_selectivity(min_mp_pos, max_mp_pos); + else + sel= (max_mp_pos - min_mp_pos); res= col_non_nulls * sel; set_if_bigger(res, col_stats->get_avg_frequency()); } diff --git a/sql/sql_statistics.h b/sql/sql_statistics.h index 20ecf06bfee..35b3aa33acc 100644 --- a/sql/sql_statistics.h +++ b/sql/sql_statistics.h @@ -239,6 +239,17 @@ public: bool is_available() { return get_size() > 0 && get_values(); } + /* + This function checks that histograms should be usable only when + 1) the level of optimizer_use_condition_selectivity > 3 + 2) histograms have been collected + */ + bool is_usable(THD *thd) + { + return thd->variables.optimizer_use_condition_selectivity > 3 && + is_available(); + } + void set_value(uint i, double val) { switch (type) { diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 6a52b7d418e..0249ae68cc3 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2000, 2019, Oracle and/or its affiliates. - Copyright (c) 2010, 2020, MariaDB + Copyright (c) 2010, 2021, 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 @@ -74,11 +74,12 @@ static int copy_data_between_tables(THD *, TABLE *,TABLE *, Alter_info::enum_enable_or_disable, Alter_table_ctx *); static int mysql_prepare_create_table(THD *, HA_CREATE_INFO *, Alter_info *, - uint *, handler *, KEY **, uint *, int); + uint *, handler *, KEY **, uint *, int, + const LEX_CSTRING db, + const LEX_CSTRING table_name); static uint blob_length_by_type(enum_field_types type); -static bool fix_constraints_names(THD *thd, List<Virtual_column_info> - *check_constraint_list, - const HA_CREATE_INFO *create_info); +static bool fix_constraints_names(THD *, List<Virtual_column_info> *, + const HA_CREATE_INFO *); /** @brief Helper function for explain_filename @@ -1825,7 +1826,7 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) if (mysql_prepare_create_table(lpt->thd, lpt->create_info, lpt->alter_info, &lpt->db_options, lpt->table->file, &lpt->key_info_buffer, &lpt->key_count, - C_ALTER_TABLE)) + C_ALTER_TABLE, lpt->db, lpt->table_name)) { DBUG_RETURN(TRUE); } @@ -2304,8 +2305,11 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, { bool is_trans= 0; bool table_creation_was_logged= 0; + bool real_table= FALSE; LEX_CSTRING db= table->db; handlerton *table_type= 0; + // reset error state for this table + error= 0; DBUG_PRINT("table", ("table_l: '%s'.'%s' table: %p s: %p", table->db.str, table->table_name.str, table->table, @@ -2321,9 +2325,35 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, thd->find_temporary_table(table) && table->mdl_request.ticket != NULL)); - if (table->open_type == OT_BASE_ONLY || !is_temporary_table(table) || - (drop_sequence && table->table->s->table_type != TABLE_TYPE_SEQUENCE)) + if (table->open_type == OT_BASE_ONLY || !is_temporary_table(table)) + real_table= TRUE; + else if (drop_sequence && + table->table->s->table_type != TABLE_TYPE_SEQUENCE) + { + was_table= (table->table->s->table_type == TABLE_TYPE_NORMAL); + was_view= (table->table->s->table_type == TABLE_TYPE_VIEW); + if (if_exists) + { + char buff[FN_REFLEN]; + String tbl_name(buff, sizeof(buff), system_charset_info); + tbl_name.length(0); + tbl_name.append(&db); + tbl_name.append('.'); + tbl_name.append(&table->table_name); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_NOT_SEQUENCE2, ER_THD(thd, ER_NOT_SEQUENCE2), + tbl_name.c_ptr_safe()); + + /* + Our job is done here. This statement was added to avoid executing + unnecessary code farther below which in some strange corner cases + caused the server to crash (see MDEV-17896). + */ + goto log_query; + } error= 1; + goto non_critical_err; + } else { table_creation_was_logged= table->table->s->table_creation_was_logged; @@ -2332,29 +2362,28 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, error= 1; goto err; } - error= 0; table->table= 0; } - if ((drop_temporary && if_exists) || !error) + if ((drop_temporary && if_exists) || !real_table) { /* This handles the case of temporary tables. We have the following cases: . "DROP TEMPORARY" was executed and a temporary table was affected - (i.e. drop_temporary && !error) or the if_exists was specified (i.e. - drop_temporary && if_exists). + (i.e. drop_temporary && !real_table) or the + if_exists was specified (i.e. drop_temporary && if_exists). . "DROP" was executed but a temporary table was affected (.i.e - !error). + !real_table). */ if (!dont_log_query && table_creation_was_logged) { /* - If there is an error, we don't know the type of the engine + If there is an real_table, we don't know the type of the engine at this point. So, we keep it in the trx-cache. */ - is_trans= error ? TRUE : is_trans; + is_trans= real_table ? TRUE : is_trans; if (is_trans) trans_tmp_table_deleted= TRUE; else @@ -2381,7 +2410,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, is no need to proceed with the code that tries to drop a regular table. */ - if (!error) continue; + if (!real_table) continue; } else if (!drop_temporary) { @@ -2397,7 +2426,6 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, reg_ext, 0); } DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table"); - error= 0; if (drop_temporary || (ha_table_exists(thd, &db, &alias, &table_type, &is_sequence) == 0 && table_type == 0) || @@ -2437,6 +2465,11 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, { non_tmp_error = (drop_temporary ? non_tmp_error : TRUE); error= 1; + /* + non critical error (only for this table), so we continue. + Next we write it to wrong_tables and continue this loop + The same as "goto non_critical_err". + */ } } else @@ -2530,7 +2563,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, } non_tmp_error|= MY_TEST(error); } - +non_critical_err: if (error) { if (wrong_tables.length()) @@ -2895,6 +2928,15 @@ bool check_duplicates_in_interval(const char *set_or_name, } +bool Column_definition:: + prepare_charset_for_string(const Column_derived_attributes *dattr) +{ + if (!charset) + charset= dattr->charset(); + return (flags & BINCMP_FLAG) && !(charset= find_bin_collation(charset)); +} + + bool Column_definition::prepare_stage2_blob(handler *file, ulonglong table_flags, uint field_flags) @@ -2976,38 +3018,6 @@ bool Column_definition::prepare_stage2(handler *file, } -/* - Get character set from field object generated by parser using - default values when not set. - - SYNOPSIS - get_sql_field_charset() - sql_field The sql_field object - create_info Info generated by parser - - RETURN VALUES - cs Character set -*/ - -CHARSET_INFO* get_sql_field_charset(Column_definition *sql_field, - HA_CREATE_INFO *create_info) -{ - CHARSET_INFO *cs= sql_field->charset; - - if (!cs) - cs= create_info->default_table_charset; - /* - table_charset is set only in ALTER TABLE t1 CONVERT TO CHARACTER SET csname - if we want change character set for all varchar/char columns. - But the table charset must not affect the BLOB fields, so don't - allow to change my_charset_bin to somethig else. - */ - if (create_info->table_charset && cs != &my_charset_bin) - cs= create_info->table_charset; - return cs; -} - - /** Modifies the first column definition whose SQL type is TIMESTAMP by adding the features DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP. @@ -3180,11 +3190,14 @@ bool Column_definition::prepare_stage1_bit(THD *thd, bool Column_definition::prepare_stage1(THD *thd, MEM_ROOT *mem_root, handler *file, - ulonglong table_flags) + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) { return type_handler()->Column_definition_prepare_stage1(thd, mem_root, this, file, - table_flags); + table_flags, + derived_attr); } @@ -3381,7 +3394,8 @@ static int mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, Alter_info *alter_info, uint *db_options, handler *file, KEY **key_info_buffer, - uint *key_count, int create_table_mode) + uint *key_count, int create_table_mode, + const LEX_CSTRING db, const LEX_CSTRING table_name) { const char *key_name; Create_field *sql_field,*dup_field; @@ -3396,7 +3410,11 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, uint total_uneven_bit_length= 0; int select_field_count= C_CREATE_SELECT(create_table_mode); bool tmp_table= create_table_mode == C_ALTER_TABLE; + const bool create_simple= thd->lex->create_simple(); bool is_hash_field_needed= false; + const Column_derived_attributes dattr(create_info->default_table_charset); + const Column_bulk_alter_attributes + battr(create_info->alter_table_convert_to_charset); DBUG_ENTER("mysql_prepare_create_table"); DBUG_EXECUTE_IF("test_pseudo_invisible",{ @@ -3453,26 +3471,27 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, for (field_no=0; (sql_field=it++) ; field_no++) { + /* Virtual fields are always NULL */ + if (sql_field->vcol_info) + sql_field->flags&= ~NOT_NULL_FLAG; + /* Initialize length from its original value (number of characters), which was set in the parser. This is necessary if we're executing a prepared statement for the second time. */ sql_field->length= sql_field->char_length; - /* Set field charset. */ - sql_field->charset= get_sql_field_charset(sql_field, create_info); - if ((sql_field->flags & BINCMP_FLAG) && - !(sql_field->charset= find_bin_collation(sql_field->charset))) - DBUG_RETURN(true); - /* Virtual fields are always NULL */ - if (sql_field->vcol_info) - sql_field->flags&= ~NOT_NULL_FLAG; + if (sql_field->bulk_alter(&dattr, &battr)) + DBUG_RETURN(true); if (sql_field->prepare_stage1(thd, thd->mem_root, - file, file->ha_table_flags())) + file, file->ha_table_flags(), + &dattr)) DBUG_RETURN(true); + DBUG_ASSERT(sql_field->charset); + if (sql_field->real_field_type() == MYSQL_TYPE_BIT && file->ha_table_flags() & HA_CAN_BIT_FIELD) total_uneven_bit_length+= sql_field->length & 7; @@ -3523,7 +3542,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (!(sql_field->flags & NOT_NULL_FLAG)) null_fields--; - if (sql_field->redefine_stage1(dup_field, file, create_info)) + if (sql_field->redefine_stage1(dup_field, file)) DBUG_RETURN(true); it2.remove(); // Remove first (create) definition @@ -4040,8 +4059,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, key_part_length= MY_MIN(max_key_length, file->max_key_part_length()); /* not a critical problem */ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_TOO_LONG_KEY, - ER_THD(thd, ER_TOO_LONG_KEY), + ER_TOO_LONG_KEY, ER_THD(thd, ER_TOO_LONG_KEY), key_part_length); /* Align key length to multibyte char boundary */ key_part_length-= key_part_length % sql_field->charset->mbmaxlen; @@ -4085,7 +4103,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, { key_part_length= file->max_key_part_length(); /* not a critical problem */ - push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_TOO_LONG_KEY, ER_THD(thd, ER_TOO_LONG_KEY), key_part_length); /* Align key length to multibyte char boundary */ @@ -4242,6 +4260,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, create_info->null_bits= null_fields; /* Check fields. */ + Item::Check_table_name_prm walk_prm(db, table_name); it.rewind(); while ((sql_field=it++)) { @@ -4296,6 +4315,37 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, sql_field->field_name.str); DBUG_RETURN(TRUE); } + + if (create_simple) + { + /* + NOTE: we cannot do this in check_vcol_func_processor() as there is already + no table name qualifier in expression. + */ + if (sql_field->vcol_info && sql_field->vcol_info->expr && + sql_field->vcol_info->expr->walk(&Item::check_table_name_processor, + false, (void *) &walk_prm)) + { + my_error(ER_BAD_FIELD_ERROR, MYF(0), walk_prm.field.c_ptr(), "GENERATED ALWAYS"); + DBUG_RETURN(TRUE); + } + + if (sql_field->default_value && + sql_field->default_value->expr->walk(&Item::check_table_name_processor, + false, (void *) &walk_prm)) + { + my_error(ER_BAD_FIELD_ERROR, MYF(0), walk_prm.field.c_ptr(), "DEFAULT"); + DBUG_RETURN(TRUE); + } + + if (sql_field->check_constraint && + sql_field->check_constraint->expr->walk(&Item::check_table_name_processor, + false, (void *) &walk_prm)) + { + my_error(ER_BAD_FIELD_ERROR, MYF(0), walk_prm.field.c_ptr(), "CHECK"); + DBUG_RETURN(TRUE); + } + } } /* Check table level constraints */ @@ -4305,6 +4355,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, Virtual_column_info *check; while ((check= c_it++)) { + if (create_simple && check->expr->walk(&Item::check_table_name_processor, false, + (void *) &walk_prm)) + { + my_error(ER_BAD_FIELD_ERROR, MYF(0), walk_prm.field.c_ptr(), "CHECK"); + DBUG_RETURN(TRUE); + } if (!check->name.length || check->automatic_name) continue; @@ -4315,8 +4371,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, const Virtual_column_info *dup_check; while ((dup_check= dup_it++) && dup_check != check) { - if (!dup_check->name.length || dup_check->automatic_name) - continue; if (!lex_string_cmp(system_charset_info, &check->name, &dup_check->name)) { @@ -4366,7 +4420,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, ER_ILLEGAL_HA_CREATE_OPTION, ER_THD(thd, ER_ILLEGAL_HA_CREATE_OPTION), file->engine_name()->str, - "TRANSACTIONAL=1"); + create_info->transactional == HA_CHOICE_YES + ? "TRANSACTIONAL=1" : "TRANSACTIONAL=0"); if (parse_option_list(thd, file->partition_ht(), &create_info->option_struct, &create_info->option_list, @@ -4540,7 +4595,9 @@ bool Column_definition::prepare_blob_field(THD *thd) bool Column_definition::sp_prepare_create_field(THD *thd, MEM_ROOT *mem_root) { - return prepare_stage1(thd, mem_root, NULL, HA_CAN_GEOMETRY) || + DBUG_ASSERT(charset); + const Column_derived_attributes dattr(&my_charset_bin); + return prepare_stage1(thd, mem_root, NULL, HA_CAN_GEOMETRY, &dattr) || prepare_stage2(NULL, HA_CAN_GEOMETRY); } @@ -4829,7 +4886,8 @@ handler *mysql_create_frm_image(THD *thd, const LEX_CSTRING &db, } if (mysql_prepare_create_table(thd, create_info, alter_info, &db_options, - file, key_info, key_count, create_table_mode)) + file, key_info, key_count, + create_table_mode, db, table_name)) goto err; create_info->table_options=db_options; @@ -5267,6 +5325,9 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, if (!opt_explicit_defaults_for_timestamp) promote_first_timestamp_column(&alter_info->create_list); + /* We can abort create table for any table type */ + thd->abort_on_warning= thd->is_strict_mode(); + if (mysql_create_table_no_lock(thd, &create_table->db, &create_table->table_name, create_info, alter_info, @@ -5304,6 +5365,8 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, } err: + thd->abort_on_warning= 0; + /* In RBR or readonly server we don't need to log CREATE TEMPORARY TABLE */ if (!result && create_info->tmp_table() && (thd->is_current_stmt_binlog_format_row() || (opt_readonly && !thd->slave_thread))) @@ -7300,13 +7363,15 @@ bool mysql_compare_tables(TABLE *table, Alter_info tmp_alter_info(*alter_info, thd->mem_root); uint db_options= 0; /* not used */ KEY *key_info_buffer= NULL; + LEX_CSTRING db= { table->s->db.str, table->s->db.length }; + LEX_CSTRING table_name= { table->s->db.str, table->s->table_name.length }; /* Create the prepared information. */ int create_table_mode= table->s->tmp_table == NO_TMP_TABLE ? C_ORDINARY_CREATE : C_ALTER_TABLE; if (mysql_prepare_create_table(thd, create_info, &tmp_alter_info, &db_options, table->file, &key_info_buffer, - &key_count, create_table_mode)) + &key_count, create_table_mode, db, table_name)) DBUG_RETURN(1); /* Some very basic checks. */ @@ -8298,7 +8363,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, def->real_field_type() == MYSQL_TYPE_NEWDATE || def->real_field_type() == MYSQL_TYPE_DATETIME || def->real_field_type() == MYSQL_TYPE_DATETIME2) && - !alter_ctx->datetime_field && + !alter_ctx->datetime_field && !def->field && !(~def->flags & (NO_DEFAULT_VALUE_FLAG | NOT_NULL_FLAG)) && thd->variables.sql_mode & MODE_NO_ZERO_DATE) { @@ -8431,6 +8496,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, long_hash_key= true; } const char *dropped_key_part= NULL; + bool user_keyparts= false; // some user-defined keyparts left KEY_PART_INFO *key_part= key_info->key_part; key_parts.empty(); bool delete_index_stat= FALSE; @@ -8506,6 +8572,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, key_parts.push_back(new (thd->mem_root) Key_part_spec(&cfield->field_name, key_part_length, true), thd->mem_root); + if (!(cfield->invisible == INVISIBLE_SYSTEM && cfield->vers_sys_field())) + user_keyparts= true; } if (table->s->tmp_table == NO_TMP_TABLE) { @@ -8516,6 +8584,14 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, (void) delete_statistics_for_index(thd, table, key_info, TRUE); } + if (!user_keyparts && key_parts.elements) + { + /* + If we dropped all user key-parts we also drop implicit system fields. + */ + key_parts.empty(); + } + if (key_parts.elements) { KEY_CREATE_INFO key_create_info; @@ -8650,37 +8726,28 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, } } - // NB: `check` is TABLE resident, we must keep it intact. - if (keep) - { - check= check->clone(thd); - if (!check) - { - my_error(ER_OUT_OF_RESOURCES, MYF(0)); - goto err; - } - } - if (share->period.constr_name.streq(check->name.str)) { - if (drop_period) - { - keep= false; - } - else if(!keep) + if (!drop_period && !keep) { my_error(ER_PERIOD_CONSTRAINT_DROP, MYF(0), check->name.str, share->period.name.str); goto err; } - else + keep= keep && !drop_period; + + DBUG_ASSERT(create_info->period_info.constr == NULL || drop_period); + + if (keep) { - DBUG_ASSERT(create_info->period_info.constr == NULL); + Item *expr_copy= check->expr->get_copy(thd); + check= new Virtual_column_info(); + check->name= share->period.constr_name; + check->automatic_name= true; + check->expr= expr_copy; create_info->period_info.constr= check; - create_info->period_info.constr->automatic_name= true; } } - /* see if the constraint depends on *only* on dropped fields */ if (keep && dropped_fields) { @@ -9563,7 +9630,8 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, If such table exists, there must be a corresponding TABLE_SHARE in THD::all_temp_tables list. */ - if (thd->find_tmp_table_share(alter_ctx.new_db.str, alter_ctx.new_name.str)) + if (thd->find_tmp_table_share(alter_ctx.new_db.str, + alter_ctx.new_name.str)) { my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alter_ctx.new_alias.str); DBUG_RETURN(true); @@ -9704,6 +9772,17 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, if (table->s->tmp_table == NO_TMP_TABLE) mysql_audit_alter_table(thd, table_list); + else if (table->s->table_creation_was_logged && mysql_bin_log.is_open()) + { + /* Protect against MDL error in binary logging */ + MDL_request mdl_request; + DBUG_ASSERT(!mdl_ticket); + mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT, + MDL_TRANSACTION); + if (thd->mdl_context.acquire_lock(&mdl_request, + thd->variables.lock_wait_timeout)) + DBUG_RETURN(true); + } THD_STAGE_INFO(thd, stage_setup); @@ -9783,9 +9862,12 @@ do_continue:; thd->get_stmt_da()->current_statement_warn_count()); my_ok(thd, 0L, 0L, alter_ctx.tmp_buff); - /* We don't replicate alter table statement on temporary tables */ + /* + We don't replicate alter table statement on temporary tables + For which we did not log the CREATE TEMPORARY TABLE statement. + */ if (table->s->tmp_table == NO_TMP_TABLE || - !thd->is_current_stmt_binlog_format_row()) + table->s->table_creation_was_logged) { if (write_bin_log(thd, true, thd->query(), thd->query_length())) DBUG_RETURN(true); @@ -10031,6 +10113,7 @@ do_continue:; tmp_disable_binlog(thd); create_info->options|=HA_CREATE_TMP_ALTER; + create_info->alias= alter_ctx.table_name; error= create_table_impl(thd, alter_ctx.db, alter_ctx.table_name, alter_ctx.new_db, alter_ctx.tmp_name, alter_ctx.get_tmp_path(), @@ -11305,8 +11388,8 @@ bool Sql_cmd_create_table_like::execute(THD *thd) { create_info.used_fields&= ~HA_CREATE_USED_CHARSET; create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET; - create_info.default_table_charset= create_info.table_charset; - create_info.table_charset= 0; + create_info.default_table_charset= create_info.alter_table_convert_to_charset; + create_info.alter_table_convert_to_charset= 0; } /* diff --git a/sql/sql_table.h b/sql/sql_table.h index 35bff0873ea..62b61684286 100644 --- a/sql/sql_table.h +++ b/sql/sql_table.h @@ -252,8 +252,6 @@ bool quick_rm_table(THD *thd, handlerton *base, const LEX_CSTRING *db, const char *table_path=0); void close_cached_table(THD *thd, TABLE *table); void sp_prepare_create_field(THD *thd, Column_definition *sql_field); -CHARSET_INFO* get_sql_field_charset(Column_definition *sql_field, - HA_CREATE_INFO *create_info); bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags); int write_bin_log(THD *thd, bool clear_error, char const *query, ulong query_length, diff --git a/sql/sql_test.cc b/sql/sql_test.cc index 08dc137bebe..5ea132c83d4 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -294,7 +294,6 @@ print_plan(JOIN* join, uint idx, double record_count, double read_time, double current_read_time, const char *info) { uint i; - POSITION pos; JOIN_TAB *join_table; JOIN_TAB **plan_nodes; TABLE* table; @@ -321,8 +320,8 @@ print_plan(JOIN* join, uint idx, double record_count, double read_time, fputs(" POSITIONS: ", DBUG_FILE); for (i= 0; i < idx ; i++) { - pos = join->positions[i]; - table= pos.table->table; + POSITION *pos= join->positions + i; + table= pos->table->table; if (table) fputs(table->s->table_name.str, DBUG_FILE); fputc(' ', DBUG_FILE); @@ -338,8 +337,8 @@ print_plan(JOIN* join, uint idx, double record_count, double read_time, fputs("BEST_POSITIONS: ", DBUG_FILE); for (i= 0; i < idx ; i++) { - pos= join->best_positions[i]; - table= pos.table->table; + POSITION *pos= join->best_positions + i; + table= pos->table->table; if (table) fputs(table->s->table_name.str, DBUG_FILE); fputc(' ', DBUG_FILE); @@ -565,6 +564,7 @@ void mysql_print_status() STATUS_VAR tmp; uint count; + tmp= global_status_var; count= calc_sum_of_all_status(&tmp); printf("\nStatus information:\n\n"); (void) my_getwd(current_dir, sizeof(current_dir),MYF(0)); @@ -616,8 +616,12 @@ Next alarm time: %lu\n", (ulong)alarm_info.next_alarm_time); #endif display_table_locks(); -#ifdef HAVE_MALLINFO +#if defined(HAVE_MALLINFO2) + struct mallinfo2 info = mallinfo2(); +#elif defined(HAVE_MALLINFO) struct mallinfo info= mallinfo(); +#endif +#if defined(HAVE_MALLINFO) || defined(HAVE_MALLINFO2) char llbuff[10][22]; printf("\nMemory status:\n\ Non-mmapped space allocated from system: %s\n\ diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc index c3d347307a2..c495a417961 100644 --- a/sql/sql_truncate.cc +++ b/sql/sql_truncate.cc @@ -416,20 +416,23 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref) bool hton_can_recreate; #ifdef WITH_WSREP - if (WSREP(thd)) + if (WSREP(thd) && wsrep_thd_is_local(thd)) { wsrep::key_array keys; - wsrep_append_fk_parent_table(thd, table_ref, &keys); - if (keys.empty()) + /* Do not start TOI if table is not found */ + if (!wsrep_append_fk_parent_table(thd, table_ref, &keys)) { - WSREP_TO_ISOLATION_BEGIN_IF(table_ref->db.str, table_ref->table_name.str, NULL) + if (keys.empty()) { - DBUG_RETURN(TRUE); - } - } else { - WSREP_TO_ISOLATION_BEGIN_FK_TABLES(NULL, NULL, table_ref, &keys) - { - DBUG_RETURN(TRUE); + WSREP_TO_ISOLATION_BEGIN_IF(table_ref->db.str, table_ref->table_name.str, NULL) + { + DBUG_RETURN(TRUE); + } + } else { + WSREP_TO_ISOLATION_BEGIN_FK_TABLES(NULL, NULL, table_ref, &keys) + { + DBUG_RETURN(TRUE); + } } } } @@ -462,6 +465,15 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref) */ error= handler_truncate(thd, table_ref, FALSE); + if (error == TRUNCATE_OK && thd->locked_tables_mode && + (table_ref->table->file->ht->flags & + HTON_REQUIRES_CLOSE_AFTER_TRUNCATE)) + { + thd->locked_tables_list.mark_table_for_reopen(thd, table_ref->table); + if (unlikely(thd->locked_tables_list.reopen_tables(thd, true))) + thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); + } + /* All effects of a TRUNCATE TABLE operation are committed even if truncation fails in the case of non transactional tables. Thus, the diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc index 916047c4b0f..63e9e76e135 100644 --- a/sql/sql_tvc.cc +++ b/sql/sql_tvc.cc @@ -47,7 +47,7 @@ bool fix_fields_for_tvc(THD *thd, List_iterator_fast<List_item> &li) while ((lst= li++)) { - List_iterator_fast<Item> it(*lst); + List_iterator<Item> it(*lst); Item *item; while ((item= it++)) @@ -59,7 +59,7 @@ bool fix_fields_for_tvc(THD *thd, List_iterator_fast<List_item> &li) while replacing their values to NAME_CONST()s. So fix only those that have not been. */ - if (item->fix_fields_if_needed(thd, 0) || + if (item->fix_fields_if_needed_for_scalar(thd, it.ref()) || item->check_is_evaluable_expression_or_error()) DBUG_RETURN(true); } @@ -341,6 +341,13 @@ int table_value_constr::save_explain_data_intern(THD *thd, if (select_lex->master_unit()->derived) explain->connection_type= Explain_node::EXPLAIN_NODE_DERIVED; + for (SELECT_LEX_UNIT *unit= select_lex->first_inner_unit(); + unit; + unit= unit->next_unit()) + { + explain->add_child(unit->first_select()->select_number); + } + output->add_node(explain); if (select_lex->is_top_level_node()) @@ -365,9 +372,14 @@ bool table_value_constr::optimize(THD *thd) thd->lex->explain && // for "SET" command in SPs. (!thd->lex->explain->get_select(select_lex->select_number))) { - return save_explain_data_intern(thd, thd->lex->explain); + if (save_explain_data_intern(thd, thd->lex->explain)) + return true; } - return 0; + + if (select_lex->optimize_unflattened_subqueries(true)) + return true; + + return false; } @@ -635,50 +647,69 @@ st_select_lex *wrap_tvc(THD *thd, st_select_lex *tvc_sl, st_select_lex *parent_select) { LEX *lex= thd->lex; - select_result *save_result= thd->lex->result; + select_result *save_result= lex->result; uint8 save_derived_tables= lex->derived_tables; thd->lex->result= NULL; Query_arena backup; Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup); + + Item *item; + SELECT_LEX *wrapper_sl; + SELECT_LEX_UNIT *derived_unit; + /* - Create SELECT_LEX of the select used in the result of transformation + Create SELECT_LEX wrapper_sl of the select used in the result + of the transformation */ - lex->current_select= tvc_sl; - if (mysql_new_select(lex, 0, NULL)) + if (!(wrapper_sl= new (thd->mem_root) SELECT_LEX())) goto err; - mysql_init_select(lex); - /* Create item list as '*' for the subquery SQ */ - Item *item; - SELECT_LEX *wrapper_sl; - wrapper_sl= lex->current_select; + wrapper_sl->select_number= ++thd->lex->stmt_lex->current_select_number; + wrapper_sl->parent_lex= lex; /* Used in init_query. */ + wrapper_sl->init_query(); + wrapper_sl->init_select(); + + wrapper_sl->nest_level= tvc_sl->nest_level; + wrapper_sl->parsing_place= tvc_sl->parsing_place; wrapper_sl->set_linkage(tvc_sl->get_linkage()); - wrapper_sl->parsing_place= SELECT_LIST; + wrapper_sl->exclude_from_table_unique_test= + tvc_sl->exclude_from_table_unique_test; + + lex->current_select= wrapper_sl; item= new (thd->mem_root) Item_field(thd, &wrapper_sl->context, NULL, NULL, &star_clex_str); if (item == NULL || add_item_to_list(thd, item)) goto err; (wrapper_sl->with_wild)++; - - /* Exclude SELECT with TVC */ - tvc_sl->exclude(); + + /* Include the newly created select into the global list of selects */ + wrapper_sl->include_global((st_select_lex_node**)&lex->all_selects_list); + + /* Substitute select node used of TVC for the newly created select */ + tvc_sl->substitute_in_tree(wrapper_sl); + /* - Create derived table DT that will wrap TVC in the result of transformation + Create a unit for the substituted select used for TVC and attach it + to the the wrapper select wrapper_sl as the only unit. The created + unit is the unit for the derived table tvc_x of the transformation. */ - SELECT_LEX *tvc_select; // select for tvc - SELECT_LEX_UNIT *derived_unit; // unit for tvc_select - if (mysql_new_select(lex, 1, tvc_sl)) + if (!(derived_unit= new (thd->mem_root) SELECT_LEX_UNIT())) goto err; - tvc_select= lex->current_select; - derived_unit= tvc_select->master_unit(); - tvc_select->set_linkage(DERIVED_TABLE_TYPE); + derived_unit->init_query(); + derived_unit->thd= thd; + derived_unit->include_down(wrapper_sl); - lex->current_select= wrapper_sl; + /* + Attach the select used of TVC as the only slave to the unit for + the derived table tvc_x of the transformation + */ + derived_unit->add_slave(tvc_sl); + tvc_sl->set_linkage(DERIVED_TABLE_TYPE); /* - Create the name of the wrapping derived table and - add it to the FROM list of the wrapper - */ + Generate the name of the derived table created for TVC and + add it to the FROM list of the wrapping select + */ Table_ident *ti; LEX_CSTRING alias; TABLE_LIST *derived_tab; @@ -697,19 +728,15 @@ st_select_lex *wrap_tvc(THD *thd, st_select_lex *tvc_sl, wrapper_sl->table_list.first->derived_type= DTYPE_TABLE | DTYPE_MATERIALIZE; lex->derived_tables|= DERIVED_SUBQUERY; - wrapper_sl->where= 0; - wrapper_sl->set_braces(false); - derived_unit->set_with_clause(0); - if (arena) thd->restore_active_arena(arena, &backup); - thd->lex->result= save_result; + lex->result= save_result; return wrapper_sl; err: if (arena) thd->restore_active_arena(arena, &backup); - thd->lex->result= save_result; + lex->result= save_result; lex->derived_tables= save_derived_tables; return 0; } @@ -778,11 +805,12 @@ st_select_lex *wrap_tvc_with_tail(THD *thd, st_select_lex *tvc_sl) SELECT * FROM (VALUES (v1), ... (vn)) tvc_x and replaces the subselect with the result of the transformation. - @retval false if successfull - true otherwise + @retval wrapping select if successful + 0 otherwise */ -bool Item_subselect::wrap_tvc_into_select(THD *thd, st_select_lex *tvc_sl) +st_select_lex * +Item_subselect::wrap_tvc_into_select(THD *thd, st_select_lex *tvc_sl) { LEX *lex= thd->lex; /* SELECT_LEX object where the transformation is performed */ @@ -792,14 +820,9 @@ bool Item_subselect::wrap_tvc_into_select(THD *thd, st_select_lex *tvc_sl) { if (engine->engine_type() == subselect_engine::SINGLE_SELECT_ENGINE) ((subselect_single_select_engine *) engine)->change_select(wrapper_sl); - lex->current_select= wrapper_sl; - return false; - } - else - { - lex->current_select= parent_select; - return true; } + lex->current_select= parent_select; + return wrapper_sl; } diff --git a/sql/sql_type.cc b/sql/sql_type.cc index 36f128a3a0b..e92dc97771e 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -2671,9 +2671,12 @@ bool Type_handler:: MEM_ROOT *mem_root, Column_definition *def, handler *file, - ulonglong table_flags) const + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const { - def->create_length_to_internal_length_simple(); + def->prepare_stage1_simple(&my_charset_bin); return false; } @@ -2682,8 +2685,12 @@ bool Type_handler_null:: MEM_ROOT *mem_root, Column_definition *def, handler *file, - ulonglong table_flags) const + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const { + def->prepare_charset_for_string(derived_attr); def->create_length_to_internal_length_null(); return false; } @@ -2693,19 +2700,56 @@ bool Type_handler_row:: MEM_ROOT *mem_root, Column_definition *def, handler *file, - ulonglong table_flags) const + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const { + def->charset= &my_charset_bin; def->create_length_to_internal_length_null(); return false; } +bool Type_handler_temporal_result:: + Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *def, + handler *file, + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const +{ + def->prepare_stage1_simple(&my_charset_numeric); + return false; +} + + +bool Type_handler_numeric:: + Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *def, + handler *file, + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const +{ + def->prepare_stage1_simple(&my_charset_numeric); + return false; +} + bool Type_handler_newdecimal:: Column_definition_prepare_stage1(THD *thd, MEM_ROOT *mem_root, Column_definition *def, handler *file, - ulonglong table_flags) const + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const { + def->charset= &my_charset_numeric; def->create_length_to_internal_length_newdecimal(); return false; } @@ -2715,8 +2759,12 @@ bool Type_handler_bit:: MEM_ROOT *mem_root, Column_definition *def, handler *file, - ulonglong table_flags) const + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const { + def->charset= &my_charset_numeric; return def->prepare_stage1_bit(thd, mem_root, file, table_flags); } @@ -2725,9 +2773,13 @@ bool Type_handler_typelib:: MEM_ROOT *mem_root, Column_definition *def, handler *file, - ulonglong table_flags) const + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const { - return def->prepare_stage1_typelib(thd, mem_root, file, table_flags); + return def->prepare_charset_for_string(derived_attr) || + def->prepare_stage1_typelib(thd, mem_root, file, table_flags); } @@ -2736,20 +2788,31 @@ bool Type_handler_string_result:: MEM_ROOT *mem_root, Column_definition *def, handler *file, - ulonglong table_flags) const + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const { - return def->prepare_stage1_string(thd, mem_root, file, table_flags); + return def->prepare_charset_for_string(derived_attr) || + def->prepare_stage1_string(thd, mem_root, file, table_flags); } #ifdef HAVE_SPATIAL +#if MYSQL_VERSION_ID > 100500 +#error The below method is in sql/sql_type_geom.cc starting from 10.5 +#endif bool Type_handler_geometry:: Column_definition_prepare_stage1(THD *thd, MEM_ROOT *mem_root, Column_definition *def, handler *file, - ulonglong table_flags) const + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const { + def->charset= &my_charset_bin; def->create_length_to_internal_length_string(); return def->prepare_blob_field(thd); } @@ -2758,14 +2821,38 @@ bool Type_handler_geometry:: /*************************************************************************/ +bool Type_handler_general_purpose_string:: + Column_definition_bulk_alter(Column_definition *def, + const Column_derived_attributes + *derived_attr, + const Column_bulk_alter_attributes + *bulk_alter_attr) + const +{ + if (!bulk_alter_attr->alter_table_convert_to_charset()) + return false; // No "CONVERT TO" clause. + CHARSET_INFO *defcs= def->explicit_or_derived_charset(derived_attr); + DBUG_ASSERT(defcs); + /* + Handle 'ALTER TABLE t1 CONVERT TO CHARACTER SET csname'. + Change character sets for all varchar/char/text columns, + but do not touch varbinary/binary/blob columns. + */ + if (defcs != &my_charset_bin) + def->charset= bulk_alter_attr->alter_table_convert_to_charset(); + return false; +}; + + +/*************************************************************************/ + bool Type_handler:: Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const { - def->redefine_stage1_common(dup, file, schema); + def->redefine_stage1_common(dup, file); def->create_length_to_internal_length_simple(); return false; } @@ -2774,11 +2861,10 @@ bool Type_handler:: bool Type_handler_null:: Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const { - def->redefine_stage1_common(dup, file, schema); + def->redefine_stage1_common(dup, file); def->create_length_to_internal_length_null(); return false; } @@ -2787,11 +2873,10 @@ bool Type_handler_null:: bool Type_handler_newdecimal:: Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const { - def->redefine_stage1_common(dup, file, schema); + def->redefine_stage1_common(dup, file); def->create_length_to_internal_length_newdecimal(); return false; } @@ -2800,11 +2885,10 @@ bool Type_handler_newdecimal:: bool Type_handler_string_result:: Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const { - def->redefine_stage1_common(dup, file, schema); + def->redefine_stage1_common(dup, file); def->set_compression_method(dup->compression_method()); def->create_length_to_internal_length_string(); return false; @@ -2814,11 +2898,10 @@ bool Type_handler_string_result:: bool Type_handler_typelib:: Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const { - def->redefine_stage1_common(dup, file, schema); + def->redefine_stage1_common(dup, file); def->create_length_to_internal_length_typelib(); return false; } @@ -2827,11 +2910,10 @@ bool Type_handler_typelib:: bool Type_handler_bit:: Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const { - def->redefine_stage1_common(dup, file, schema); + def->redefine_stage1_common(dup, file); /* If we are replacing a field with a BIT field, we need to initialize pack_flag. @@ -8633,11 +8715,18 @@ LEX_CSTRING Charset::collation_specific_name() const for character sets and collations, so a collation name not necessarily starts with the character set name. */ + LEX_CSTRING retval; size_t csname_length= strlen(m_charset->csname); if (strncmp(m_charset->name, m_charset->csname, csname_length)) - return {NULL, 0}; + { + retval.str= NULL; + retval.length= 0; + return retval; + } const char *ptr= m_charset->name + csname_length; - return {ptr, strlen(ptr) }; + retval.str= ptr; + retval.length= strlen(ptr); + return retval; } diff --git a/sql/sql_type.h b/sql/sql_type.h index f8ebc269788..7a514643bf6 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -76,7 +76,6 @@ class Spvar_definition; struct st_value; class Protocol; class handler; -struct Schema_specification_st; struct TABLE; struct SORT_FIELD_ATTR; class Vers_history_point; @@ -110,6 +109,53 @@ enum scalar_comparison_op }; +/* + A helper class to store column attributes that are inherited + by columns (from the table level) when not specified explicitly. +*/ +class Column_derived_attributes +{ + /* + Table level CHARACTER SET and COLLATE value: + + CREATE TABLE t1 (a VARCHAR(1), b CHAR(2)) CHARACTER SET latin1; + + All character string columns (CHAR, VARCHAR, TEXT) + inherit CHARACTER SET from the table level. + */ + CHARSET_INFO *m_charset; +public: + explicit Column_derived_attributes(CHARSET_INFO *cs) + :m_charset(cs) + { } + CHARSET_INFO *charset() const { return m_charset; } +}; + + +/* + A helper class to store requests for changes + in multiple column data types during ALTER. +*/ +class Column_bulk_alter_attributes +{ + /* + Target CHARACTER SET specification in ALTER .. CONVERT, e.g. + + ALTER TABLE t1 CONVERT TO CHARACTER SET utf8; + + All character string columns (CHAR, VARCHAR, TEXT) + get converted to the "CONVERT TO CHARACTER SET". + */ + CHARSET_INFO *m_alter_table_convert_to_charset; +public: + explicit Column_bulk_alter_attributes(CHARSET_INFO *convert) + :m_alter_table_convert_to_charset(convert) + { } + CHARSET_INFO *alter_table_convert_to_charset() const + { return m_alter_table_convert_to_charset; } +}; + + class Native: public Binary_string { public: @@ -3597,7 +3643,17 @@ public: MEM_ROOT *mem_root, Column_definition *c, handler *file, - ulonglong table_flags) const; + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const; + virtual bool Column_definition_bulk_alter(Column_definition *c, + const Column_derived_attributes + *derived_attr, + const Column_bulk_alter_attributes + *bulk_alter_attr) + const + { return false; } /* This method is called on queries like: CREATE TABLE t2 (a INT) AS SELECT a FROM t1; @@ -3616,9 +3672,7 @@ public: */ virtual bool Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st * - schema) + const handler *file) const; virtual bool Column_definition_prepare_stage2(Column_definition *c, handler *file, @@ -3655,8 +3709,6 @@ public: const Type_std_attributes *item, SORT_FIELD_ATTR *attr) const= 0; - virtual bool is_packable() const { return false; } - virtual uint32 max_display_length(const Item *item) const= 0; virtual uint32 Item_decimal_notation_int_digits(const Item *item) const { return 0; } virtual uint32 calc_pack_length(uint32 length) const= 0; @@ -4010,11 +4062,13 @@ public: MEM_ROOT *mem_root, Column_definition *c, handler *file, - ulonglong table_flags) const; + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const; bool Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const { DBUG_ASSERT(0); @@ -4296,6 +4350,14 @@ class Type_handler_numeric: public Type_handler { public: String *print_item_value(THD *thd, Item *item, String *str) const; + bool Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *c, + handler *file, + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const; double Item_func_min_max_val_real(Item_func_min_max *) const; longlong Item_func_min_max_val_int(Item_func_min_max *) const; my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, @@ -4736,6 +4798,14 @@ public: void sortlength(THD *thd, const Type_std_attributes *item, SORT_FIELD_ATTR *attr) const; + bool Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *c, + handler *file, + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const; bool Item_const_eq(const Item_const *a, const Item_const *b, bool binary_cmp) const; bool Item_param_set_from_value(THD *thd, @@ -4816,19 +4886,19 @@ public: const Type_std_attributes *item, SORT_FIELD_ATTR *attr) const; - bool is_packable()const { return true; } - bool union_element_finalize(const Item * item) const; bool Column_definition_prepare_stage1(THD *thd, MEM_ROOT *mem_root, Column_definition *c, handler *file, - ulonglong table_flags) const; + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const; bool Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const; uint32 max_display_length(const Item *item) const; /* @@ -4939,6 +5009,12 @@ class Type_handler_general_purpose_string: public Type_handler_string_result public: bool is_general_purpose_string_type() const { return true; } bool Vers_history_point_resolve_unit(THD *thd, Vers_history_point *p) const; + bool Column_definition_bulk_alter(Column_definition *c, + const Column_derived_attributes + *derived_attr, + const Column_bulk_alter_attributes + *bulk_alter_attr) + const; }; @@ -5295,11 +5371,13 @@ public: MEM_ROOT *mem_root, Column_definition *c, handler *file, - ulonglong table_flags) const; + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const; bool Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const; bool Column_definition_prepare_stage2(Column_definition *c, handler *file, @@ -5979,11 +6057,13 @@ public: MEM_ROOT *mem_root, Column_definition *c, handler *file, - ulonglong table_flags) const; + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const; bool Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const; bool Column_definition_prepare_stage2(Column_definition *c, handler *file, @@ -6025,11 +6105,13 @@ public: MEM_ROOT *mem_root, Column_definition *c, handler *file, - ulonglong table_flags) const; + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const; bool Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const; bool Column_definition_prepare_stage2(Column_definition *c, handler *file, @@ -6352,7 +6434,10 @@ public: MEM_ROOT *mem_root, Column_definition *c, handler *file, - ulonglong table_flags) const; + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const; bool Column_definition_prepare_stage2(Column_definition *c, handler *file, ulonglong table_flags) const; @@ -6428,11 +6513,13 @@ public: MEM_ROOT *mem_root, Column_definition *c, handler *file, - ulonglong table_flags) const; + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const; bool Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const; void Item_param_set_param_func(Item_param *param, uchar **pos, ulong len) const; diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 027d5882722..b295112b70d 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -405,7 +405,10 @@ select_union_recursive::create_result_table(THD *thd_arg, hidden)) return true; - if (! (incr_table= create_tmp_table(thd_arg, &tmp_table_param, *column_types, + incr_table_param.init(); + incr_table_param.field_count= column_types->elements; + incr_table_param.bit_fields_as_long= bit_fields_as_long; + if (! (incr_table= create_tmp_table(thd_arg, &incr_table_param, *column_types, (ORDER*) 0, false, 1, options, HA_POS_ERROR, &empty_clex_str, true, keep_row_order))) @@ -415,20 +418,6 @@ select_union_recursive::create_result_table(THD *thd_arg, for (uint i=0; i < table->s->fields; i++) incr_table->field[i]->flags &= ~(PART_KEY_FLAG | PART_INDIRECT_KEY_FLAG); - TABLE *rec_table= 0; - if (! (rec_table= create_tmp_table(thd_arg, &tmp_table_param, *column_types, - (ORDER*) 0, false, 1, - options, HA_POS_ERROR, alias, - true, keep_row_order))) - return true; - - rec_table->keys_in_use_for_query.clear_all(); - for (uint i=0; i < table->s->fields; i++) - rec_table->field[i]->flags &= ~(PART_KEY_FLAG | PART_INDIRECT_KEY_FLAG); - - if (rec_tables.push_back(rec_table)) - return true; - return false; } @@ -466,23 +455,25 @@ void select_union_recursive::cleanup() free_tmp_table(thd, incr_table); } - List_iterator<TABLE> it(rec_tables); - TABLE *tab; - while ((tab= it++)) + List_iterator<TABLE_LIST> it(rec_table_refs); + TABLE_LIST *tbl; + while ((tbl= it++)) { + TABLE *tab= tbl->table; if (tab->is_created()) { tab->file->extra(HA_EXTRA_RESET_STATE); tab->file->ha_delete_all_rows(); } - /* + /* The table will be closed later in close_thread_tables(), because it might be used in the statements like ANALYZE WITH r AS (...) SELECT * from r - where r is defined through recursion. + where r is defined through recursion. */ tab->next= thd->rec_tables; thd->rec_tables= tab; + tbl->derived_result= 0; } } @@ -1138,9 +1129,33 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg, goto err; if (!derived_arg->table) { - derived_arg->table= with_element->rec_result->rec_tables.head(); - if (derived_arg->derived_result) - derived_arg->derived_result->table= derived_arg->table; + bool res= false; + + if ((!derived_arg->is_with_table_recursive_reference() || + !derived_arg->derived_result) && + !(derived_arg->derived_result= + new (thd->mem_root) select_unit(thd))) + goto err; // out of memory + thd->create_tmp_table_for_derived= TRUE; + + res= derived_arg->derived_result->create_result_table(thd, + &types, + FALSE, + create_options, + &derived_arg->alias, + FALSE, FALSE, + FALSE, 0); + thd->create_tmp_table_for_derived= FALSE; + if (res) + goto err; + derived_arg->derived_result->set_unit(this); + derived_arg->table= derived_arg->derived_result->table; + if (derived_arg->is_with_table_recursive_reference()) + { + /* Here 'derived_arg' is the primary recursive table reference */ + derived_arg->with->rec_result-> + rec_table_refs.push_back(derived_arg); + } } with_element->mark_as_with_prepared_anchor(); is_rec_result_table_created= true; @@ -1783,11 +1798,11 @@ bool st_select_lex_unit::exec_recursive() TABLE *incr_table= with_element->rec_result->incr_table; st_select_lex *end= NULL; bool is_unrestricted= with_element->is_unrestricted(); - List_iterator_fast<TABLE> li(with_element->rec_result->rec_tables); + List_iterator_fast<TABLE_LIST> li(with_element->rec_result->rec_table_refs); TMP_TABLE_PARAM *tmp_table_param= &with_element->rec_result->tmp_table_param; ha_rows examined_rows= 0; bool was_executed= executed; - TABLE *rec_table; + TABLE_LIST *rec_tbl; DBUG_ENTER("st_select_lex_unit::exec_recursive"); @@ -1865,8 +1880,9 @@ bool st_select_lex_unit::exec_recursive() else with_element->level++; - while ((rec_table= li++)) + while ((rec_tbl= li++)) { + TABLE *rec_table= rec_tbl->table; saved_error= incr_table->insert_all_rows_into_tmp_table(thd, rec_table, tmp_table_param, diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 9514b193407..41ea52ef3c3 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2016, Oracle and/or its affiliates. - Copyright (c) 2011, 2020, MariaDB + Copyright (c) 2011, 2021, 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 @@ -52,8 +52,9 @@ compare_record(TABLE*). */ bool records_are_comparable(const TABLE *table) { - return ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) == 0) || - bitmap_is_subset(table->write_set, table->read_set); + return !table->versioned(VERS_TRX_ID) && + (((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) == 0) || + bitmap_is_subset(table->write_set, table->read_set)); } @@ -1081,20 +1082,9 @@ update_begin: } else if (likely(!error)) { - if (has_vers_fields && table->versioned()) - { - if (table->versioned(VERS_TIMESTAMP)) - { - store_record(table, record[2]); - table->mark_columns_per_binlog_row_image(); - error= vers_insert_history_row(table); - restore_record(table, record[2]); - } - if (likely(!error)) - rows_inserted++; - } - if (likely(!error)) - updated++; + if (has_vers_fields && table->versioned(VERS_TRX_ID)) + rows_inserted++; + updated++; } if (likely(!error) && !record_was_same && table_list->has_period()) @@ -1110,6 +1100,20 @@ update_begin: if (unlikely(error) && (!ignore || table->file->is_fatal_error(error, HA_CHECK_ALL))) { + goto error; + } + } + + if (likely(!error) && has_vers_fields && table->versioned(VERS_TIMESTAMP)) + { + store_record(table, record[2]); + table->mark_columns_per_binlog_row_image(); + table->clone_handler_for_update(); + error= vers_insert_history_row(table); + restore_record(table, record[2]); + if (unlikely(error)) + { +error: /* If (ignore && error is ignorable) we don't have to do anything; otherwise... @@ -1120,10 +1124,11 @@ update_begin: flags|= ME_FATAL; /* Other handler errors are fatal */ prepare_record_for_error_message(error, table); - table->file->print_error(error,MYF(flags)); - error= 1; - break; - } + table->file->print_error(error,MYF(flags)); + error= 1; + break; + } + rows_inserted++; } if (table->triggers && @@ -1705,12 +1710,8 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) call in setup_tables()). */ - if (setup_tables_and_check_access(thd, &select_lex->context, - &select_lex->top_join_list, table_list, select_lex->leaf_tables, - FALSE, UPDATE_ACL, SELECT_ACL, FALSE)) - DBUG_RETURN(1); - - if (select_lex->handle_derived(thd->lex, DT_MERGE)) + if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, + table_list, select_lex->leaf_tables, FALSE, TRUE)) DBUG_RETURN(1); List<Item> *fields= &lex->first_select_lex()->item_list; @@ -1848,7 +1849,11 @@ int mysql_multi_update_prepare(THD *thd) During prepare phase acquire only S metadata locks instead of SW locks to keep prepare of multi-UPDATE compatible with concurrent LOCK TABLES WRITE and global read lock. + + Don't evaluate any subqueries even if constant, because + tables aren't locked yet. */ + lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED; if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI) { if (open_tables(thd, &table_list, &table_count, @@ -1871,6 +1876,9 @@ int mysql_multi_update_prepare(THD *thd) { DBUG_RETURN(TRUE); } + + lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED; + (void) read_statistics_for_tables_if_needed(thd, table_list); /* @todo: downgrade the metadata locks here. */ @@ -1929,9 +1937,16 @@ bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, List<Item> *fields, DBUG_RETURN(TRUE); } + if ((*result)->init(thd)) + DBUG_RETURN(1); + thd->abort_on_warning= !ignore && thd->is_strict_mode(); List<Item> total_list; + if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, + table_list, select_lex->leaf_tables, FALSE, FALSE)) + DBUG_RETURN(1); + if (select_lex->vers_setup_conds(thd, table_list)) DBUG_RETURN(1); @@ -1973,6 +1988,24 @@ multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list, } +bool multi_update::init(THD *thd) +{ + table_map tables_to_update= get_table_map(fields); + List_iterator_fast<TABLE_LIST> li(*leaves); + TABLE_LIST *tbl; + while ((tbl =li++)) + { + if (tbl->is_jtbm()) + continue; + if (!(tbl->table->map & tables_to_update)) + continue; + if (updated_leaves.push_back(tbl, thd->mem_root)) + return true; + } + return false; +} + + /* Connect fields with tables and create list of tables that are updated */ @@ -1989,7 +2022,7 @@ int multi_update::prepare(List<Item> ¬_used_values, List_iterator_fast<Item> value_it(*values); uint i, max_fields; uint leaf_table_count= 0; - List_iterator<TABLE_LIST> ti(*leaves); + List_iterator<TABLE_LIST> ti(updated_leaves); DBUG_ENTER("multi_update::prepare"); if (prepared) @@ -2477,6 +2510,7 @@ int multi_update::send_data(List<Item> ¬_used_values) for (cur_table= update_tables; cur_table; cur_table= cur_table->next_local) { + int error= 0; TABLE *table= cur_table->table; uint offset= cur_table->shared; /* @@ -2520,7 +2554,6 @@ int multi_update::send_data(List<Item> ¬_used_values) found++; if (!can_compare_record || compare_record(table)) { - int error; if ((error= cur_table->view_check_option(thd, ignore)) != VIEW_CHECK_OK) @@ -2547,20 +2580,7 @@ int multi_update::send_data(List<Item> ¬_used_values) updated--; if (!ignore || table->file->is_fatal_error(error, HA_CHECK_ALL)) - { - /* - If (ignore && error == is ignorable) we don't have to - do anything; otherwise... - */ - myf flags= 0; - - if (table->file->is_fatal_error(error, HA_CHECK_ALL)) - flags|= ME_FATAL; /* Other handler errors are fatal */ - - prepare_record_for_error_message(error, table); - table->file->print_error(error,MYF(flags)); - DBUG_RETURN(1); - } + goto error; } else { @@ -2569,19 +2589,8 @@ int multi_update::send_data(List<Item> ¬_used_values) error= 0; updated--; } - else if (has_vers_fields && table->versioned()) + else if (has_vers_fields && table->versioned(VERS_TRX_ID)) { - if (table->versioned(VERS_TIMESTAMP)) - { - store_record(table, record[2]); - if (vers_insert_history_row(table)) - { - restore_record(table, record[2]); - error= 1; - break; - } - restore_record(table, record[2]); - } updated_sys_ver++; } /* non-transactional or transactional table got modified */ @@ -2595,6 +2604,18 @@ int multi_update::send_data(List<Item> ¬_used_values) } } } + if (has_vers_fields && table->versioned(VERS_TIMESTAMP)) + { + store_record(table, record[2]); + table->clone_handler_for_update(); + if (unlikely(error= vers_insert_history_row(table))) + { + restore_record(table, record[2]); + goto error; + } + restore_record(table, record[2]); + updated_sys_ver++; + } if (table->triggers && unlikely(table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, TRG_ACTION_AFTER, TRUE))) @@ -2602,7 +2623,6 @@ int multi_update::send_data(List<Item> ¬_used_values) } else { - int error; TABLE *tmp_table= tmp_tables[offset]; if (copy_funcs(tmp_table_param[offset].items_to_copy, thd)) DBUG_RETURN(1); @@ -2637,7 +2657,22 @@ int multi_update::send_data(List<Item> ¬_used_values) } } } - } + continue; +error: + DBUG_ASSERT(error > 0); + /* + If (ignore && error == is ignorable) we don't have to + do anything; otherwise... + */ + myf flags= 0; + + if (table->file->is_fatal_error(error, HA_CHECK_ALL)) + flags|= ME_FATAL; /* Other handler errors are fatal */ + + prepare_record_for_error_message(error, table); + table->file->print_error(error,MYF(flags)); + DBUG_RETURN(1); + } // for (cur_table) DBUG_RETURN(0); } @@ -3034,14 +3069,18 @@ bool multi_update::send_eof() DBUG_ASSERT(trans_safe || !updated || thd->transaction.stmt.modified_non_trans_table); - if (likely(local_error != 0)) - error_handled= TRUE; // to force early leave from ::abort_result_set() - - if (unlikely(local_error > 0)) // if the above log write did not fail ... + if (unlikely(local_error)) { - /* Safety: If we haven't got an error before (can happen in do_updates) */ - my_message(ER_UNKNOWN_ERROR, "An error occurred in multi-table update", - MYF(0)); + error_handled= TRUE; // to force early leave from ::abort_result_set() + if (thd->killed == NOT_KILLED && !thd->get_stmt_da()->is_set()) + { + /* + No error message was sent and query was not killed (in which case + mysql_execute_command() will send the error mesage). + */ + my_message(ER_UNKNOWN_ERROR, "An error occurred in multi-table update", + MYF(0)); + } DBUG_RETURN(TRUE); } diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 0a18a852832..126db90656c 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -292,6 +292,8 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view, { for (tbl= sl->get_table_list(); tbl; tbl= tbl->next_local) { + if (!tbl->with && tbl->select_lex) + tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl); /* Ensure that we have some privileges on this table, more strict check will be done on column level after preparation, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 47102c3c6aa..9b2355e53c5 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -829,7 +829,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); /* We should not introduce any further shift/reduce conflicts. */ -%expect 54 +%expect 68 /* Comments for TOKENS. @@ -1808,7 +1808,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %type <type_handler> int_type real_type -%type <Lex_field_type> type_with_opt_collate field_type +%type <Lex_field_type> field_type field_type_all qualified_field_type field_type_numeric field_type_string @@ -1879,7 +1879,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %type <item> literal insert_ident order_ident temporal_literal simple_ident expr sum_expr in_sum_expr - variable variable_aux bool_pri + variable variable_aux predicate bit_expr parenthesized_expr table_wild simple_expr column_default_non_parenthesized_expr udf_expr primary_expr string_factor_expr mysql_concatenation_expr @@ -3392,7 +3392,7 @@ sp_param_name: ; sp_param_name_and_type: - sp_param_name type_with_opt_collate + sp_param_name field_type { if (unlikely(Lex->sp_param_fill_definition($$= $1))) MYSQL_YYABORT; @@ -3522,7 +3522,7 @@ row_field_name: ; row_field_definition: - row_field_name type_with_opt_collate + row_field_name field_type ; row_field_definition_list: @@ -3551,7 +3551,7 @@ sp_decl_idents_init_vars: sp_decl_variable_list: sp_decl_idents_init_vars - type_with_opt_collate + field_type sp_opt_default { if (unlikely(Lex->sp_variable_declarations_finalize(thd, $1, @@ -6888,19 +6888,26 @@ column_default_expr: } ; +field_type: field_type_all + { + Lex->map_data_type(Lex_ident_sys(), &($$= $1)); + Lex->last_field->set_attributes($$, Lex->charset); + } + ; + qualified_field_type: - field_type + field_type_all { Lex->map_data_type(Lex_ident_sys(), &($$= $1)); } - | sp_decl_ident '.' field_type + | sp_decl_ident '.' field_type_all { if (Lex->map_data_type($1, &($$= $3))) MYSQL_YYABORT; } ; -field_type: +field_type_all: field_type_numeric | field_type_temporal | field_type_string @@ -7363,20 +7370,6 @@ with_or_without_system: ; -type_with_opt_collate: - field_type opt_collate - { - Lex->map_data_type(Lex_ident_sys(), &($$= $1)); - - if ($2) - { - if (unlikely(!(Lex->charset= merge_charset_and_collation(Lex->charset, $2)))) - MYSQL_YYABORT; - } - Lex->last_field->set_attributes($$, Lex->charset); - } - ; - charset: CHAR_SYM SET {} | CHARSET {} @@ -7450,6 +7443,12 @@ charset_or_alias: } ; +collate: COLLATE_SYM collation_name_or_default + { + Lex->charset= $2; + } + ; + opt_binary: /* empty */ { bincmp_collation(NULL, false); } | binary {} @@ -7460,6 +7459,13 @@ binary: | charset_or_alias opt_bin_mod { bincmp_collation($1, $2); } | BINARY { bincmp_collation(NULL, true); } | BINARY charset_or_alias { bincmp_collation($2, true); } + | charset_or_alias collate + { + if (!my_charset_same(Lex->charset, $1)) + my_yyabort_error((ER_COLLATION_CHARSET_MISMATCH, MYF(0), + Lex->charset->name, $1->csname)); + } + | collate { } ; opt_bin_mod: @@ -10064,23 +10070,19 @@ expr: if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bool_pri - ; - -bool_pri: - bool_pri EQUAL_SYM predicate %prec EQUAL_SYM + | expr EQUAL_SYM predicate %prec EQUAL_SYM { $$= new (thd->mem_root) Item_func_equal(thd, $1, $3); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bool_pri comp_op predicate %prec '=' + | expr comp_op predicate %prec '=' { $$= (*$2)(0)->create(thd, $1, $3); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bool_pri comp_op all_or_any '(' subselect ')' %prec '=' + | expr comp_op all_or_any '(' subselect ')' %prec '=' { $$= all_any_subquery_creator(thd, $1, $2, $3, $5); if (unlikely($$ == NULL)) @@ -14993,7 +14995,7 @@ kill: lex->sql_command= SQLCOM_KILL; lex->kill_type= KILL_TYPE_ID; } - kill_type kill_option kill_expr + kill_type kill_option { Lex->kill_signal= (killed_state) ($3 | $4); } @@ -15006,16 +15008,21 @@ kill_type: ; kill_option: - /* empty */ { $$= (int) KILL_CONNECTION; } - | CONNECTION_SYM { $$= (int) KILL_CONNECTION; } - | QUERY_SYM { $$= (int) KILL_QUERY; } - | QUERY_SYM ID_SYM + opt_connection kill_expr { $$= (int) KILL_CONNECTION; } + | QUERY_SYM kill_expr { $$= (int) KILL_QUERY; } + | QUERY_SYM ID_SYM expr { $$= (int) KILL_QUERY; Lex->kill_type= KILL_TYPE_QUERY; + Lex->value_list.push_front($3, thd->mem_root); } ; +opt_connection: + /* empty */ { } + | CONNECTION_SYM { } + ; + kill_expr: expr { @@ -15028,7 +15035,6 @@ kill_expr: } ; - shutdown: SHUTDOWN { Lex->sql_command= SQLCOM_SHUTDOWN; } shutdown_option {} @@ -18028,7 +18034,7 @@ sf_return_type: &empty_clex_str, thd->variables.collation_database); } - type_with_opt_collate + field_type { if (unlikely(Lex->sphead->fill_field_definition(thd, Lex->last_field))) diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index ac4929468b1..cb58c4aff43 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -305,7 +305,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); /* We should not introduce any further shift/reduce conflicts. */ -%expect 57 +%expect 70 /* Comments for TOKENS. @@ -1288,9 +1288,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %type <type_handler> int_type real_type -%type <Lex_field_type> type_with_opt_collate field_type +%type <Lex_field_type> field_type field_type_all qualified_field_type - sp_param_type_with_opt_collate + sp_param_type sp_param_field_type sp_param_field_type_string field_type_numeric @@ -1362,7 +1362,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %type <item> literal insert_ident order_ident temporal_literal simple_ident expr sum_expr in_sum_expr - variable variable_aux bool_pri + variable variable_aux predicate bit_expr parenthesized_expr table_wild simple_expr column_default_non_parenthesized_expr udf_expr primary_expr string_factor_expr mysql_concatenation_expr @@ -3194,7 +3194,7 @@ sp_param_name: ; sp_param_name_and_type: - sp_param_name sp_param_type_with_opt_collate + sp_param_name sp_param_type { if (unlikely(Lex->sp_param_fill_definition($$= $1))) MYSQL_YYABORT; @@ -3238,7 +3238,7 @@ sp_pdparams: ; sp_pdparam: - sp_param_name sp_opt_inout sp_param_type_with_opt_collate + sp_param_name sp_opt_inout sp_param_type { $1->mode= $2; if (unlikely(Lex->sp_param_fill_definition($1))) @@ -3407,7 +3407,7 @@ row_field_name: ; row_field_definition: - row_field_name type_with_opt_collate + row_field_name field_type ; row_field_definition_list: @@ -3436,7 +3436,7 @@ sp_decl_idents_init_vars: sp_decl_vars: sp_decl_idents_init_vars - type_with_opt_collate + field_type sp_opt_default { if (unlikely(Lex->sp_variable_declarations_finalize(thd, $1, @@ -6891,19 +6891,26 @@ column_default_expr: } ; +field_type: field_type_all + { + Lex->map_data_type(Lex_ident_sys(), &($$= $1)); + Lex->last_field->set_attributes($$, Lex->charset); + } + ; + qualified_field_type: - field_type + field_type_all { Lex->map_data_type(Lex_ident_sys(), &($$= $1)); } - | sp_decl_ident '.' field_type + | sp_decl_ident '.' field_type_all { if (Lex->map_data_type($1, &($$= $3))) MYSQL_YYABORT; } ; -field_type: +field_type_all: field_type_numeric | field_type_temporal | field_type_string @@ -7445,30 +7452,10 @@ with_or_without_system: ; -type_with_opt_collate: - field_type opt_collate - { - Lex->map_data_type(Lex_ident_sys(), &($$= $1)); - - if ($2) - { - if (unlikely(!(Lex->charset= merge_charset_and_collation(Lex->charset, $2)))) - MYSQL_YYABORT; - } - Lex->last_field->set_attributes($$, Lex->charset); - } - ; - -sp_param_type_with_opt_collate: - sp_param_field_type opt_collate +sp_param_type: + sp_param_field_type { Lex->map_data_type(Lex_ident_sys(), &($$= $1)); - - if ($2) - { - if (unlikely(!(Lex->charset= merge_charset_and_collation(Lex->charset, $2)))) - MYSQL_YYABORT; - } Lex->last_field->set_attributes($$, Lex->charset); } ; @@ -7546,6 +7533,12 @@ charset_or_alias: } ; +collate: COLLATE_SYM collation_name_or_default + { + Lex->charset= $2; + } + ; + opt_binary: /* empty */ { bincmp_collation(NULL, false); } | binary {} @@ -7556,6 +7549,13 @@ binary: | charset_or_alias opt_bin_mod { bincmp_collation($1, $2); } | BINARY { bincmp_collation(NULL, true); } | BINARY charset_or_alias { bincmp_collation($2, true); } + | charset_or_alias collate + { + if (!my_charset_same(Lex->charset, $1)) + my_yyabort_error((ER_COLLATION_CHARSET_MISMATCH, MYF(0), + Lex->charset->name, $1->csname)); + } + | collate { } ; opt_bin_mod: @@ -10168,23 +10168,19 @@ expr: if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bool_pri - ; - -bool_pri: - bool_pri EQUAL_SYM predicate %prec EQUAL_SYM + | expr EQUAL_SYM predicate %prec EQUAL_SYM { $$= new (thd->mem_root) Item_func_equal(thd, $1, $3); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bool_pri comp_op predicate %prec '=' + | expr comp_op predicate %prec '=' { $$= (*$2)(0)->create(thd, $1, $3); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bool_pri comp_op all_or_any '(' subselect ')' %prec '=' + | expr comp_op all_or_any '(' subselect ')' %prec '=' { $$= all_any_subquery_creator(thd, $1, $2, $3, $5); if (unlikely($$ == NULL)) @@ -15117,7 +15113,7 @@ kill: lex->sql_command= SQLCOM_KILL; lex->kill_type= KILL_TYPE_ID; } - kill_type kill_option kill_expr + kill_type kill_option { Lex->kill_signal= (killed_state) ($3 | $4); } @@ -15130,16 +15126,21 @@ kill_type: ; kill_option: - /* empty */ { $$= (int) KILL_CONNECTION; } - | CONNECTION_SYM { $$= (int) KILL_CONNECTION; } - | QUERY_SYM { $$= (int) KILL_QUERY; } - | QUERY_SYM ID_SYM + opt_connection kill_expr { $$= (int) KILL_CONNECTION; } + | QUERY_SYM kill_expr { $$= (int) KILL_QUERY; } + | QUERY_SYM ID_SYM expr { $$= (int) KILL_QUERY; Lex->kill_type= KILL_TYPE_QUERY; + Lex->value_list.push_front($3, thd->mem_root); } ; +opt_connection: + /* empty */ { } + | CONNECTION_SYM { } + ; + kill_expr: expr { @@ -15152,7 +15153,6 @@ kill_expr: } ; - shutdown: SHUTDOWN { Lex->sql_command= SQLCOM_SHUTDOWN; } shutdown_option {} @@ -18240,7 +18240,7 @@ sf_return_type: &empty_clex_str, thd->variables.collation_database); } - sp_param_type_with_opt_collate + sp_param_type { if (unlikely(Lex->sphead->fill_field_definition(thd, Lex->last_field))) diff --git a/sql/strfunc.cc b/sql/strfunc.cc index 99ff9c50588..58647c21e44 100644 --- a/sql/strfunc.cc +++ b/sql/strfunc.cc @@ -79,14 +79,17 @@ ulonglong find_set(TYPELIB *lib, const char *str, size_t length, CHARSET_INFO *c var_len= (uint) (pos - start); uint find= cs ? find_type2(lib, start, var_len, cs) : find_type(lib, start, var_len, (bool) 0); - if (unlikely(!find && *err_len == 0)) + if (unlikely(!find)) { - // report the first error with length > 0 - *err_pos= (char*) start; - *err_len= var_len; - *set_warning= 1; + if (*err_len == 0) + { + // report the first error with length > 0 + *err_pos= (char*) start; + *err_len= var_len; + *set_warning= 1; + } } - else + else if (find <= sizeof(longlong) * 8) found|= 1ULL << (find - 1); if (pos >= end) break; @@ -400,4 +403,3 @@ const char *flagset_to_string(THD *thd, LEX_CSTRING *result, ulonglong set, return result->str; } - diff --git a/sql/structs.h b/sql/structs.h index 28c0cb6e1a2..215fe7b60ea 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -92,7 +92,7 @@ class engine_option_value; struct ha_index_option_struct; typedef struct st_key { - uint key_length; /* Tot length of key */ + uint key_length; /* total length of user defined key parts */ ulong flags; /* dupp key and pack flags */ uint user_defined_key_parts; /* How many key_parts */ uint usable_key_parts; /* Should normally be = user_defined_key_parts */ diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 4c3028b0a00..c52a8f742a8 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -2352,7 +2352,7 @@ static Sys_var_ulong Sys_max_sort_length( "the first max_sort_length bytes of each value are used; the rest " "are ignored)", SESSION_VAR(max_sort_length), CMD_LINE(REQUIRED_ARG), - VALID_RANGE(8, 8192*1024L), DEFAULT(1024), BLOCK_SIZE(1)); + VALID_RANGE(64, 8192*1024L), DEFAULT(1024), BLOCK_SIZE(1)); static Sys_var_ulong Sys_max_sp_recursion_depth( "max_sp_recursion_depth", @@ -4570,11 +4570,11 @@ static Sys_var_ulong Sys_default_week_format( SESSION_VAR(default_week_format), CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 7), DEFAULT(0), BLOCK_SIZE(1)); -static Sys_var_ulonglong Sys_group_concat_max_len( +static Sys_var_uint Sys_group_concat_max_len( "group_concat_max_len", "The maximum length of the result of function GROUP_CONCAT()", SESSION_VAR(group_concat_max_len), CMD_LINE(REQUIRED_ARG), - VALID_RANGE(4, SIZE_T_MAX), DEFAULT(1024*1024), BLOCK_SIZE(1)); + VALID_RANGE(4, UINT_MAX32), DEFAULT(1024*1024), BLOCK_SIZE(1)); static char *glob_hostname_ptr; static Sys_var_charptr Sys_hostname( @@ -4834,6 +4834,24 @@ static Sys_var_have Sys_have_symlink( "--skip-symbolic-links option.", READ_ONLY GLOBAL_VAR(have_symlink), NO_CMD_LINE); +#if defined(__SANITIZE_ADDRESS__) || defined(WITH_UBSAN) + +#ifdef __SANITIZE_ADDRESS__ +#define SANITIZER_MODE "ASAN" +#else +#define SANITIZER_MODE "UBSAN" +#endif /* __SANITIZE_ADDRESS__ */ + +static char *have_sanitizer; +static Sys_var_charptr Sys_have_santitizer( + "have_sanitizer", + "If the server is compiled with sanitize (compiler option), this " + "variable is set to the sanitizer mode used. Possible values are " + "ASAN (Address sanitizer) or UBSAN (The Undefined Behavior Sanitizer).", + READ_ONLY GLOBAL_VAR(have_sanitizer), NO_CMD_LINE, + IN_FS_CHARSET, DEFAULT(SANITIZER_MODE)); +#endif /* defined(__SANITIZE_ADDRESS__) || defined(WITH_UBSAN) */ + static bool fix_log_state(sys_var *self, THD *thd, enum_var_type type); static Sys_var_mybool Sys_general_log( @@ -5418,7 +5436,7 @@ static Sys_var_tz Sys_time_zone( static Sys_var_charptr Sys_wsrep_provider( "wsrep_provider", "Path to replication provider library", - PREALLOCATED GLOBAL_VAR(wsrep_provider), CMD_LINE(REQUIRED_ARG), + PREALLOCATED READ_ONLY GLOBAL_VAR(wsrep_provider), CMD_LINE(REQUIRED_ARG), IN_FS_CHARSET, DEFAULT(WSREP_NONE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(wsrep_provider_check), ON_UPDATE(wsrep_provider_update)); @@ -5445,13 +5463,12 @@ static Sys_var_charptr Sys_wsrep_cluster_name( ON_CHECK(wsrep_cluster_name_check), ON_UPDATE(wsrep_cluster_name_update)); -static PolyLock_mutex PLock_wsrep_cluster_config(&LOCK_wsrep_cluster_config); static Sys_var_charptr Sys_wsrep_cluster_address ( "wsrep_cluster_address", "Address to initially connect to cluster", PREALLOCATED GLOBAL_VAR(wsrep_cluster_address), CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET, DEFAULT(""), - &PLock_wsrep_cluster_config, NOT_IN_BINLOG, + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(wsrep_cluster_address_check), ON_UPDATE(wsrep_cluster_address_update)); @@ -5482,7 +5499,7 @@ static Sys_var_ulong Sys_wsrep_slave_threads( "wsrep_slave_threads", "Number of slave appliers to launch", GLOBAL_VAR(wsrep_slave_threads), CMD_LINE(REQUIRED_ARG), VALID_RANGE(1, 512), DEFAULT(1), BLOCK_SIZE(1), - &PLock_wsrep_cluster_config, NOT_IN_BINLOG, + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(wsrep_slave_threads_update)); @@ -5635,7 +5652,7 @@ static Sys_var_ulong Sys_wsrep_max_ws_rows ( static Sys_var_charptr Sys_wsrep_notify_cmd( "wsrep_notify_cmd", "", - GLOBAL_VAR(wsrep_notify_cmd),CMD_LINE(REQUIRED_ARG), + READ_ONLY GLOBAL_VAR(wsrep_notify_cmd), CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET, DEFAULT("")); static Sys_var_mybool Sys_wsrep_certify_nonPK( diff --git a/sql/table.cc b/sql/table.cc index 084b441e4c6..12299271ab3 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1127,7 +1127,7 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table, thd->stmt_arena= table->expr_arena; thd->update_charset(&my_charset_utf8mb4_general_ci, table->s->table_charset); expr_str.append(&parse_vcol_keyword); - thd->variables.sql_mode &= ~MODE_NO_BACKSLASH_ESCAPES; + thd->variables.sql_mode &= ~(MODE_NO_BACKSLASH_ESCAPES | MODE_EMPTY_STRING_IS_NULL); while (pos < end) { @@ -2730,7 +2730,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, else key_part->key_part_flag|= HA_VAR_LENGTH_PART; key_part->store_length+=HA_KEY_BLOB_LENGTH; - keyinfo->key_length+= HA_KEY_BLOB_LENGTH; + if (i < keyinfo->user_defined_key_parts) + keyinfo->key_length+= HA_KEY_BLOB_LENGTH; } if (field->type() == MYSQL_TYPE_BIT) key_part->key_part_flag|= HA_BIT_PART; @@ -2827,7 +2828,6 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, set_if_bigger(share->max_key_length,keyinfo->key_length+ keyinfo->user_defined_key_parts); - share->total_key_length+= keyinfo->key_length; /* MERGE tables do not have unique indexes. But every key could be an unique index on the underlying MyISAM table. (Bug #10400) @@ -3210,9 +3210,8 @@ ret: if (unlikely(thd->is_error() || error)) { thd->clear_error(); - my_error(ER_SQL_DISCOVER_ERROR, MYF(0), - plugin_name(db_plugin)->str, db.str, table_name.str, - sql_copy); + my_error(ER_SQL_DISCOVER_ERROR, MYF(0), hton_name(hton)->str, + db.str, table_name.str, sql_copy); DBUG_RETURN(HA_ERR_GENERIC); } /* Treat the table as normal table from binary logging point of view */ @@ -4480,7 +4479,7 @@ void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table) create_info->row_type= share->row_type; create_info->key_block_size= share->key_block_size; create_info->default_table_charset= share->table_charset; - create_info->table_charset= 0; + create_info->alter_table_convert_to_charset= 0; create_info->comment= share->comment; create_info->transactional= share->transactional; create_info->page_checksum= share->page_checksum; diff --git a/sql/table.h b/sql/table.h index 6073e35fa85..4a739ed1f9f 100644 --- a/sql/table.h +++ b/sql/table.h @@ -806,7 +806,7 @@ struct TABLE_SHARE uint rec_buff_length; /* Size of table->record[] buffer */ uint keys, key_parts; uint ext_key_parts; /* Total number of key parts in extended keys */ - uint max_key_length, max_unique_length, total_key_length; + uint max_key_length, max_unique_length; uint uniques; /* Number of UNIQUE index */ uint db_create_options; /* Create options from database */ uint db_options_in_use; /* Options in use */ @@ -2577,8 +2577,12 @@ struct TABLE_LIST Indicates what triggers we need to pre-load for this TABLE_LIST when opening an associated TABLE. This is filled after the parsed tree is created. + + slave_fk_event_map is filled on the slave side with bitmaps value + representing row-based event operation to help find and prelock + possible FK constrain-related child tables. */ - uint8 trg_event_map; + uint8 trg_event_map, slave_fk_event_map; /* TRUE <=> this table is a const one and was optimized away. */ bool optimized_away; @@ -3083,25 +3087,25 @@ typedef struct st_open_table_list{ } OPEN_TABLE_LIST; -static inline my_bitmap_map *tmp_use_all_columns(TABLE *table, - MY_BITMAP *bitmap) +static inline MY_BITMAP *tmp_use_all_columns(TABLE *table, + MY_BITMAP **bitmap) { - my_bitmap_map *old= bitmap->bitmap; - bitmap->bitmap= table->s->all_set.bitmap; + MY_BITMAP *old= *bitmap; + *bitmap= &table->s->all_set; return old; } -static inline void tmp_restore_column_map(MY_BITMAP *bitmap, - my_bitmap_map *old) +static inline void tmp_restore_column_map(MY_BITMAP **bitmap, + MY_BITMAP *old) { - bitmap->bitmap= old; + *bitmap= old; } /* The following is only needed for debugging */ -static inline my_bitmap_map *dbug_tmp_use_all_columns(TABLE *table, - MY_BITMAP *bitmap) +static inline MY_BITMAP *dbug_tmp_use_all_columns(TABLE *table, + MY_BITMAP **bitmap) { #ifdef DBUG_ASSERT_EXISTS return tmp_use_all_columns(table, bitmap); @@ -3110,8 +3114,8 @@ static inline my_bitmap_map *dbug_tmp_use_all_columns(TABLE *table, #endif } -static inline void dbug_tmp_restore_column_map(MY_BITMAP *bitmap, - my_bitmap_map *old) +static inline void dbug_tmp_restore_column_map(MY_BITMAP **bitmap, + MY_BITMAP *old) { #ifdef DBUG_ASSERT_EXISTS tmp_restore_column_map(bitmap, old); @@ -3124,22 +3128,22 @@ static inline void dbug_tmp_restore_column_map(MY_BITMAP *bitmap, Provide for the possiblity of the read set being the same as the write set */ static inline void dbug_tmp_use_all_columns(TABLE *table, - my_bitmap_map **save, - MY_BITMAP *read_set, - MY_BITMAP *write_set) + MY_BITMAP **save, + MY_BITMAP **read_set, + MY_BITMAP **write_set) { #ifdef DBUG_ASSERT_EXISTS - save[0]= read_set->bitmap; - save[1]= write_set->bitmap; + save[0]= *read_set; + save[1]= *write_set; (void) tmp_use_all_columns(table, read_set); (void) tmp_use_all_columns(table, write_set); #endif } -static inline void dbug_tmp_restore_column_maps(MY_BITMAP *read_set, - MY_BITMAP *write_set, - my_bitmap_map **old) +static inline void dbug_tmp_restore_column_maps(MY_BITMAP **read_set, + MY_BITMAP **write_set, + MY_BITMAP **old) { #ifdef DBUG_ASSERT_EXISTS tmp_restore_column_map(read_set, old[0]); @@ -3241,6 +3245,12 @@ inline void mark_as_null_row(TABLE *table) bfill(table->null_flags,table->s->null_bytes,255); } +inline void unmark_as_null_row(TABLE *table) +{ + table->null_row=0; + table->status= STATUS_NO_RECORD; +} + bool is_simple_order(ORDER *order); class Open_tables_backup; diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc index f2b27b7056b..5e4b5f7a713 100644 --- a/sql/temporary_tables.cc +++ b/sql/temporary_tables.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, 2019, MariaDB Corporation. + Copyright (c) 2016, 2021, 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 @@ -875,10 +875,12 @@ void THD::restore_tmp_table_share(TMP_TABLE_SHARE *share) inline bool THD::has_temporary_tables() { DBUG_ENTER("THD::has_temporary_tables"); - bool result= (rgi_slave - ? (rgi_slave->rli->save_temporary_tables && - !rgi_slave->rli->save_temporary_tables->is_empty()) - : (has_thd_temporary_tables())); + bool result= +#ifdef HAVE_REPLICATION + rgi_slave ? (rgi_slave->rli->save_temporary_tables && + !rgi_slave->rli->save_temporary_tables->is_empty()) : +#endif + has_thd_temporary_tables(); DBUG_RETURN(result); } @@ -1508,12 +1510,14 @@ bool THD::lock_temporary_tables() DBUG_RETURN(false); } +#ifdef HAVE_REPLICATION if (rgi_slave) { mysql_mutex_lock(&rgi_slave->rli->data_lock); temporary_tables= rgi_slave->rli->save_temporary_tables; m_tmp_tables_locked= true; } +#endif DBUG_RETURN(m_tmp_tables_locked); } @@ -1534,6 +1538,7 @@ void THD::unlock_temporary_tables() DBUG_VOID_RETURN; } +#ifdef HAVE_REPLICATION if (rgi_slave) { rgi_slave->rli->save_temporary_tables= temporary_tables; @@ -1541,6 +1546,7 @@ void THD::unlock_temporary_tables() mysql_mutex_unlock(&rgi_slave->rli->data_lock); m_tmp_tables_locked= false; } +#endif DBUG_VOID_RETURN; } diff --git a/sql/unireg.cc b/sql/unireg.cc index 17222efe791..8340901175a 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -57,13 +57,7 @@ static bool make_empty_rec(THD *, uchar *, uint, List<Create_field> &, uint, */ static uchar *extra2_write_len(uchar *pos, size_t len) { - /* TODO: should be - if (len > 0 && len <= 255) - *pos++= (uchar)len; - ... - because extra2_read_len() uses 0 for 2-byte lengths. - extra2_str_size() must be fixed too. - */ + DBUG_ASSERT(len); if (len <= 255) *pos++= (uchar)len; else @@ -1028,6 +1022,8 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options, TABLE table; TABLE_SHARE share; Create_field *field; + Check_level_instant_set old_count_cuted_fields(thd, CHECK_FIELD_WARN); + Abort_on_warning_instant_set old_abort_on_warning(thd, 0); DBUG_ENTER("make_empty_rec"); /* We need a table to generate columns for default values */ @@ -1046,7 +1042,6 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options, null_pos= buff; List_iterator<Create_field> it(create_fields); - Check_level_instant_set check_level_save(thd, CHECK_FIELD_WARN); while ((field=it++)) { Record_addr addr(buff + field->offset + data_offset, diff --git a/sql/upgrade_conf_file.cc b/sql/upgrade_conf_file.cc index 4e167f0263f..b40cce0bbd7 100644 --- a/sql/upgrade_conf_file.cc +++ b/sql/upgrade_conf_file.cc @@ -75,6 +75,7 @@ static const char *removed_variables[] = "innodb_ibuf_accel_rate", "innodb_ibuf_active_contract", "innodb_ibuf_max_size", +"innodb_idle_flush_pct", "innodb_import_table_from_xtrabackup", "innodb_instrument_semaphores", "innodb_kill_idle_transaction", diff --git a/sql/win_tzname_data.h b/sql/win_tzname_data.h index 03197227f8e..792cdbc7a13 100644 --- a/sql/win_tzname_data.h +++ b/sql/win_tzname_data.h @@ -12,6 +12,7 @@ {L"US Mountain Standard Time","America/Phoenix"}, {L"Mountain Standard Time (Mexico)","America/Chihuahua"}, {L"Mountain Standard Time","America/Denver"}, +{L"Yukon Standard Time","America/Whitehorse"}, {L"Central America Standard Time","America/Guatemala"}, {L"Central Standard Time","America/Chicago"}, {L"Easter Island Standard Time","Pacific/Easter"}, diff --git a/sql/wsrep_check_opts.cc b/sql/wsrep_check_opts.cc index 935bacffffc..e5a0dcb2ede 100644 --- a/sql/wsrep_check_opts.cc +++ b/sql/wsrep_check_opts.cc @@ -63,7 +63,7 @@ int wsrep_check_opts() else { // non-mysqldump SST requires wsrep_cluster_address on startup - if (!wsrep_cluster_address || !wsrep_cluster_address[0]) + if (!wsrep_cluster_address_exists()) { WSREP_ERROR ("%s SST method requires wsrep_cluster_address to be " "configured on startup.", wsrep_sst_method); diff --git a/sql/wsrep_client_service.cc b/sql/wsrep_client_service.cc index 934a9701b41..89621619a23 100644 --- a/sql/wsrep_client_service.cc +++ b/sql/wsrep_client_service.cc @@ -69,20 +69,13 @@ bool Wsrep_client_service::interrupted( wsrep::unique_lock<wsrep::mutex>& lock WSREP_UNUSED) const { DBUG_ASSERT(m_thd == current_thd); - /* Underlying mutex in lock object points to LOCK_thd_data, which - protects m_thd->wsrep_trx(), LOCK_thd_kill protects m_thd->killed. - Locking order is: - 1) LOCK_thd_data - 2) LOCK_thd_kill */ - mysql_mutex_assert_owner(static_cast<mysql_mutex_t*>(lock.mutex().native())); - mysql_mutex_lock(&m_thd->LOCK_thd_kill); + mysql_mutex_assert_owner(static_cast<mysql_mutex_t*>(lock.mutex()->native())); bool ret= (m_thd->killed != NOT_KILLED); if (ret) { WSREP_DEBUG("wsrep state is interrupted, THD::killed %d trx state %d", m_thd->killed, m_thd->wsrep_trx().state()); } - mysql_mutex_unlock(&m_thd->LOCK_thd_kill); return ret; } diff --git a/sql/wsrep_condition_variable.h b/sql/wsrep_condition_variable.h index 4412154e67b..6ad53a3086c 100644 --- a/sql/wsrep_condition_variable.h +++ b/sql/wsrep_condition_variable.h @@ -44,7 +44,7 @@ public: void wait(wsrep::unique_lock<wsrep::mutex>& lock) { - mysql_mutex_t* mutex= static_cast<mysql_mutex_t*>(lock.mutex().native()); + mysql_mutex_t* mutex= static_cast<mysql_mutex_t*>(lock.mutex()->native()); mysql_cond_wait(&m_cond, mutex); } private: diff --git a/sql/wsrep_dummy.cc b/sql/wsrep_dummy.cc index c4dbd8c450f..129df8e1577 100644 --- a/sql/wsrep_dummy.cc +++ b/sql/wsrep_dummy.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2014, 2020, MariaDB +/* Copyright (C) 2014, 2021, 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 @@ -59,6 +59,12 @@ void wsrep_thd_LOCK(const THD *) void wsrep_thd_UNLOCK(const THD *) { } +void wsrep_thd_kill_LOCK(const THD *) +{ } + +void wsrep_thd_kill_UNLOCK(const THD *) +{ } + const char *wsrep_thd_conflict_state_str(THD *) { return 0; } @@ -101,14 +107,6 @@ const char* wsrep_thd_client_state_str(const THD*) const char* wsrep_thd_client_mode_str(const THD*) { return 0; } -void wsrep_thd_auto_increment_variables(THD *thd, - unsigned long long *offset, - unsigned long long *increment) -{ - *offset= thd->variables.auto_increment_offset; - *increment= thd->variables.auto_increment_increment; -} - const char* wsrep_thd_transaction_state_str(const THD*) { return 0; } diff --git a/sql/wsrep_high_priority_service.cc b/sql/wsrep_high_priority_service.cc index 1adbb312ac0..0da71c3eda5 100644 --- a/sql/wsrep_high_priority_service.cc +++ b/sql/wsrep_high_priority_service.cc @@ -673,6 +673,17 @@ int Wsrep_replayer_service::apply_write_set(const wsrep::ws_meta& ws_meta, DBUG_ASSERT(thd->wsrep_trx().active()); DBUG_ASSERT(thd->wsrep_trx().state() == wsrep::transaction::s_replaying); + /* Allow tests to block the replayer thread using the DBUG facilities */ + DBUG_EXECUTE_IF("sync.wsrep_replay_cb", + { + const char act[]= + "now " + "SIGNAL sync.wsrep_replay_cb_reached " + "WAIT_FOR signal.wsrep_replay_cb"; + DBUG_ASSERT(!debug_sync_set_action(thd, + STRING_WITH_LEN(act))); + };); + wsrep_setup_uk_and_fk_checks(thd); int ret= 0; diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index 7732dc0eefe..0f0ef95492b 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -46,6 +46,7 @@ #include <cstdlib> #include <string> #include "log_event.h" +#include "sql_connect.h" #include <sstream> @@ -131,6 +132,18 @@ uint wsrep_ignore_apply_errors= 0; */ /* + * Cached variables + */ + +// Whether the Galera write-set replication provider is set +// wsrep_provider && strcmp(wsrep_provider, WSREP_NONE) +bool WSREP_PROVIDER_EXISTS_; + +// Whether the Galera write-set replication is enabled +// global_system_variables.wsrep_on && WSREP_PROVIDER_EXISTS_ +bool WSREP_ON_; + +/* * Other wsrep global variables. */ @@ -315,29 +328,31 @@ wsp::node_status local_status; */ Wsrep_schema *wsrep_schema= 0; -static void wsrep_log_cb(wsrep::log::level level, const char *msg) +static void wsrep_log_cb(wsrep::log::level level, + const char*, const char *msg) { /* Silence all wsrep related logging from lib and provider if wsrep is not enabled. */ - if (WSREP_ON) - { - switch (level) { - case wsrep::log::info: - sql_print_information("WSREP: %s", msg); - break; - case wsrep::log::warning: - sql_print_warning("WSREP: %s", msg); - break; - case wsrep::log::error: - sql_print_error("WSREP: %s", msg); + if (!WSREP_ON) return; + + switch (level) { + case wsrep::log::info: + WSREP_INFO("%s", msg); + break; + case wsrep::log::warning: + WSREP_WARN("%s", msg); + break; + case wsrep::log::error: + WSREP_ERROR("%s", msg); + break; + case wsrep::log::debug: + WSREP_DEBUG("%s", msg); + break; + case wsrep::log::unknown: + WSREP_UNKNOWN("%s", msg); break; - case wsrep::log::debug: - if (wsrep_debug) sql_print_information ("[Debug] WSREP: %s", msg); - default: - break; - } } } @@ -875,13 +890,13 @@ void wsrep_init_startup (bool sst_first) if (!strcmp(wsrep_provider, WSREP_NONE)) return; /* Skip replication start if no cluster address */ - if (!wsrep_cluster_address || wsrep_cluster_address[0] == 0) return; + if (!wsrep_cluster_address_exists()) return; /* Read value of wsrep_new_cluster before wsrep_start_replication(), the value is reset to FALSE inside wsrep_start_replication. */ - if (!wsrep_start_replication()) unireg_abort(1); + if (!wsrep_start_replication(wsrep_cluster_address)) unireg_abort(1); wsrep_create_rollbacker(); wsrep_create_appliers(1); @@ -1031,7 +1046,7 @@ void wsrep_shutdown_replication() my_pthread_setspecific_ptr(THR_THD, NULL); } -bool wsrep_start_replication() +bool wsrep_start_replication(const char *wsrep_cluster_address) { int rcode; WSREP_DEBUG("wsrep_start_replication"); @@ -1046,12 +1061,7 @@ bool wsrep_start_replication() return true; } - if (!wsrep_cluster_address || wsrep_cluster_address[0]== 0) - { - // if provider is non-trivial, but no address is specified, wait for address - WSREP_DEBUG("wsrep_start_replication exit due to empty address"); - return true; - } + DBUG_ASSERT(wsrep_cluster_address[0]); bool const bootstrap(TRUE == wsrep_new_cluster); wsrep_new_cluster= FALSE; @@ -1181,10 +1191,17 @@ void wsrep_keys_free(wsrep_key_arr_t* key_arr) key_arr->keys_len= 0; } -void +/*! + * @param thd thread + * @param tables list of tables + * @param keys prepared keys + + * @return true if parent table append was successfull, otherwise false. +*/ +bool wsrep_append_fk_parent_table(THD* thd, TABLE_LIST* tables, wsrep::key_array* keys) { - if (!WSREP(thd) || !WSREP_CLIENT(thd)) return; + bool fail= false; TABLE_LIST *table; thd->release_transactional_locks(); @@ -1195,6 +1212,8 @@ wsrep_append_fk_parent_table(THD* thd, TABLE_LIST* tables, wsrep::key_array* key open_tables(thd, &tables, &counter, MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL)) { WSREP_DEBUG("unable to open table for FK checks for %s", thd->query()); + fail= true; + goto exit; } for (table= tables; table; table= table->next_local) @@ -1216,14 +1235,44 @@ wsrep_append_fk_parent_table(THD* thd, TABLE_LIST* tables, wsrep::key_array* key } } +exit: /* close the table and release MDL locks */ close_thread_tables(thd); thd->mdl_context.rollback_to_savepoint(mdl_savepoint); for (table= tables; table; table= table->next_local) { table->table= NULL; + table->next_global= NULL; table->mdl_request.ticket= NULL; } + + return fail; +} + +bool wsrep_reload_ssl() +{ + try + { + std::string opts= Wsrep_server_state::instance().provider().options(); + if (opts.find("socket.ssl_reload") == std::string::npos) + { + WSREP_DEBUG("Option `socket.ssl_reload` not found in parameters."); + return false; + } + const std::string reload_ssl_param("socket.ssl_reload=1"); + enum wsrep::provider::status ret= Wsrep_server_state::instance().provider().options(reload_ssl_param); + if (ret) + { + WSREP_ERROR("Set options returned %d", ret); + return true; + } + return false; + } + catch (...) + { + WSREP_ERROR("Failed to get provider options"); + return true; + } } /*! @@ -1962,7 +2011,7 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table, { DBUG_ASSERT(thd->variables.wsrep_OSU_method == WSREP_OSU_TOI); - WSREP_DEBUG("TOI Begin"); + WSREP_DEBUG("TOI Begin for %s", WSREP_QUERY(thd)); if (wsrep_can_run_in_toi(thd, db, table, table_list) == false) { WSREP_DEBUG("No TOI for %s", WSREP_QUERY(thd)); @@ -2052,22 +2101,20 @@ static void wsrep_TOI_end(THD *thd) { wsrep_to_isolation--; wsrep::client_state& client_state(thd->wsrep_cs()); DBUG_ASSERT(wsrep_thd_is_local_toi(thd)); - WSREP_DEBUG("TO END: %lld: %s", client_state.toi_meta().seqno().get(), - WSREP_QUERY(thd)); - if (wsrep_thd_is_local_toi(thd)) + wsrep_set_SE_checkpoint(client_state.toi_meta().gtid()); + + int ret= client_state.leave_toi_local(wsrep::mutable_buffer()); + + if (!ret) { - wsrep_set_SE_checkpoint(client_state.toi_meta().gtid()); - int ret= client_state.leave_toi_local(wsrep::mutable_buffer()); - if (!ret) - { - WSREP_DEBUG("TO END: %lld", client_state.toi_meta().seqno().get()); - } - else - { - WSREP_WARN("TO isolation end failed for: %d, schema: %s, sql: %s", - ret, (thd->db.str ? thd->db.str : "(null)"), WSREP_QUERY(thd)); - } + WSREP_DEBUG("TO END: %lld: %s", + client_state.toi_meta().seqno().get(), WSREP_QUERY(thd)); + } + else + { + WSREP_WARN("TO isolation end failed for: %d, sql: %s", + ret, WSREP_QUERY(thd)); } } @@ -2381,18 +2428,7 @@ static void wsrep_close_thread(THD *thd) thd->set_killed(KILL_CONNECTION); MYSQL_CALLBACK(thread_scheduler, post_kill_notification, (thd)); mysql_mutex_lock(&thd->LOCK_thd_kill); - if (thd->mysys_var) - { - thd->mysys_var->abort=1; - mysql_mutex_lock(&thd->mysys_var->mutex); - if (thd->mysys_var->current_cond) - { - mysql_mutex_lock(thd->mysys_var->current_mutex); - mysql_cond_broadcast(thd->mysys_var->current_cond); - mysql_mutex_unlock(thd->mysys_var->current_mutex); - } - mysql_mutex_unlock(&thd->mysys_var->mutex); - } + thd->abort_current_cond_wait(true); mysql_mutex_unlock(&thd->LOCK_thd_kill); } @@ -2441,10 +2477,12 @@ static my_bool kill_remaining_threads(THD *thd, THD *caller_thd) if (is_client_connection(thd) && !abort_replicated(thd) && !is_replaying_connection(thd) && + thd_is_connection_alive(thd) && thd != caller_thd) { + WSREP_INFO("killing local connection: %lld", (longlong) thd->thread_id); - close_connection(thd, 0); + close_connection(thd); } #endif return 0; diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h index b0050a2ebae..db6910030c8 100644 --- a/sql/wsrep_mysqld.h +++ b/sql/wsrep_mysqld.h @@ -20,6 +20,7 @@ #ifdef WITH_WSREP extern bool WSREP_ON_; +extern bool WSREP_PROVIDER_EXISTS_; #include <mysql/plugin.h> #include "mysql/service_wsrep.h" @@ -203,7 +204,7 @@ extern void wsrep_close_applier_threads(int count); /* new defines */ extern void wsrep_stop_replication(THD *thd); -extern bool wsrep_start_replication(); +extern bool wsrep_start_replication(const char *wsrep_cluster_address); extern void wsrep_shutdown_replication(); extern bool wsrep_must_sync_wait (THD* thd, uint mask= WSREP_SYNC_WAIT_BEFORE_READ); extern bool wsrep_sync_wait (THD* thd, uint mask= WSREP_SYNC_WAIT_BEFORE_READ); @@ -212,7 +213,8 @@ wsrep_sync_wait_upto (THD* thd, wsrep_gtid_t* upto, int timeout); extern void wsrep_last_committed_id (wsrep_gtid_t* gtid); extern int wsrep_check_opts(); extern void wsrep_prepend_PATH (const char* path); -void wsrep_append_fk_parent_table(THD* thd, TABLE_LIST* table, wsrep::key_array* keys); +extern bool wsrep_append_fk_parent_table(THD* thd, TABLE_LIST* table, wsrep::key_array* keys); +extern bool wsrep_reload_ssl(); /* Other global variables */ extern wsrep_seqno_t wsrep_locked_seqno; @@ -221,7 +223,8 @@ extern wsrep_seqno_t wsrep_locked_seqno; /* use xxxxxx_NNULL macros when thd pointer is guaranteed to be non-null to * avoid compiler warnings (GCC 6 and later) */ -#define WSREP_NNULL(thd) (WSREP_ON && thd->variables.wsrep_on) +#define WSREP_NNULL(thd) \ + (WSREP_PROVIDER_EXISTS_ && thd->variables.wsrep_on) #define WSREP(thd) \ (thd && WSREP_NNULL(thd)) @@ -251,34 +254,40 @@ void WSREP_LOG(void (*fun)(const char* fmt, ...), const char* fmt, ...); #define WSREP_INFO(...) WSREP_LOG(sql_print_information, ##__VA_ARGS__) #define WSREP_WARN(...) WSREP_LOG(sql_print_warning, ##__VA_ARGS__) #define WSREP_ERROR(...) WSREP_LOG(sql_print_error, ##__VA_ARGS__) +#define WSREP_UNKNOWN(fmt, ...) WSREP_ERROR("UNKNOWN: " fmt, ##__VA_ARGS__) #define WSREP_LOG_CONFLICT_THD(thd, role) \ - WSREP_LOG(sql_print_information, \ - "%s: \n " \ - " THD: %lu, mode: %s, state: %s, conflict: %s, seqno: %lld\n " \ - " SQL: %s", \ - role, \ - thd_get_thread_id(thd), \ - wsrep_thd_client_mode_str(thd), \ - wsrep_thd_client_state_str(thd), \ - wsrep_thd_transaction_state_str(thd), \ - wsrep_thd_trx_seqno(thd), \ - wsrep_thd_query(thd) \ + WSREP_INFO("%s: \n " \ + " THD: %lu, mode: %s, state: %s, conflict: %s, seqno: %lld\n " \ + " SQL: %s", \ + role, \ + thd_get_thread_id(thd), \ + wsrep_thd_client_mode_str(thd), \ + wsrep_thd_client_state_str(thd), \ + wsrep_thd_transaction_state_str(thd), \ + wsrep_thd_trx_seqno(thd), \ + wsrep_thd_query(thd) \ ); #define WSREP_LOG_CONFLICT(bf_thd, victim_thd, bf_abort) \ if (wsrep_debug || wsrep_log_conflicts) \ { \ - WSREP_LOG(sql_print_information, "cluster conflict due to %s for threads:", \ - (bf_abort) ? "high priority abort" : "certification failure" \ + WSREP_INFO("cluster conflict due to %s for threads:", \ + (bf_abort) ? "high priority abort" : "certification failure" \ ); \ if (bf_thd) WSREP_LOG_CONFLICT_THD(bf_thd, "Winning thread"); \ if (victim_thd) WSREP_LOG_CONFLICT_THD(victim_thd, "Victim thread"); \ - WSREP_LOG(sql_print_information, "context: %s:%d", __FILE__, __LINE__); \ + WSREP_INFO("context: %s:%d", __FILE__, __LINE__); \ } -#define WSREP_PROVIDER_EXISTS \ - (wsrep_provider && strncasecmp(wsrep_provider, WSREP_NONE, FN_REFLEN)) +#define WSREP_PROVIDER_EXISTS (WSREP_PROVIDER_EXISTS_) + +static inline bool wsrep_cluster_address_exists() +{ + if (mysqld_server_started) + mysql_mutex_assert_owner(&LOCK_global_system_variables); + return wsrep_cluster_address && wsrep_cluster_address[0]; +} #define WSREP_QUERY(thd) (thd->query()) @@ -501,6 +510,7 @@ wsrep::key wsrep_prepare_key_for_toi(const char* db, const char* table, #define wsrep_thr_deinit() do {} while(0) #define wsrep_init_globals() do {} while(0) #define wsrep_create_appliers(X) do {} while(0) +#define wsrep_cluster_address_exists() (false) #endif /* WITH_WSREP */ diff --git a/sql/wsrep_notify.cc b/sql/wsrep_notify.cc index 1d6d13ea6d2..d2d08e92ae7 100644 --- a/sql/wsrep_notify.cc +++ b/sql/wsrep_notify.cc @@ -27,10 +27,12 @@ void wsrep_notify_status(enum wsrep::server_state::state status, return; } - char cmd_buf[1 << 16]; // this can be long - long cmd_len= sizeof(cmd_buf) - 1; - char* cmd_ptr= cmd_buf; - long cmd_off= 0; + const long cmd_len = (1 << 16) - 1; + char* cmd_ptr = (char*) my_malloc(cmd_len + 1, MYF(MY_WME)); + long cmd_off = 0; + + if (!cmd_ptr) + return; // the warning is in the log cmd_off += snprintf (cmd_ptr + cmd_off, cmd_len - cmd_off, "%s", wsrep_notify_cmd); @@ -73,6 +75,7 @@ void wsrep_notify_status(enum wsrep::server_state::state status, { WSREP_ERROR("Notification buffer too short (%ld). Aborting notification.", cmd_len); + my_free(cmd_ptr); return; } @@ -86,5 +89,6 @@ void wsrep_notify_status(enum wsrep::server_state::state status, WSREP_ERROR("Notification command failed: %d (%s): \"%s\"", err, strerror(err), cmd_ptr); } + my_free(cmd_ptr); } diff --git a/sql/wsrep_priv.h b/sql/wsrep_priv.h index e480331ba65..fb8467adc9d 100644 --- a/sql/wsrep_priv.h +++ b/sql/wsrep_priv.h @@ -42,7 +42,7 @@ extern wsrep_seqno_t local_seqno; extern Wsrep_schema* wsrep_schema; // a helper function -void wsrep_sst_received(THD*, const wsrep_uuid_t&, wsrep_seqno_t, +bool wsrep_sst_received(THD*, const wsrep_uuid_t&, wsrep_seqno_t, const void*, size_t); void wsrep_notify_status(enum wsrep::server_state::state status, diff --git a/sql/wsrep_schema.cc b/sql/wsrep_schema.cc index 8a47bbec9b0..5ee6468e9c1 100644 --- a/sql/wsrep_schema.cc +++ b/sql/wsrep_schema.cc @@ -159,6 +159,24 @@ private: THD *m_cur_thd; }; +class sql_safe_updates +{ +public: + sql_safe_updates(THD* thd) + : m_thd(thd) + , m_option_bits(thd->variables.option_bits) + { + thd->variables.option_bits&= ~OPTION_SAFE_UPDATES; + } + ~sql_safe_updates() + { + m_thd->variables.option_bits= m_option_bits; + } +private: + THD* m_thd; + ulonglong m_option_bits; +}; + static int execute_SQL(THD* thd, const char* sql, uint length) { DBUG_ENTER("Wsrep_schema::execute_SQL()"); int err= 0; @@ -230,6 +248,7 @@ static int open_table(THD* thd, tables.init_one_table(schema_name, table_name, NULL, lock_type); + thd->lex->query_tables_own_last= 0; if (!open_n_lock_single_table(thd, &tables, tables.lock_type, flags)) { if (thd->is_error()) { @@ -565,14 +584,24 @@ static int end_index_scan(TABLE* table) { return 0; } -static void make_key(TABLE* table, uchar* key, key_part_map* map, int parts) { +static void make_key(TABLE* table, uchar** key, key_part_map* map, int parts) { uint prefix_length= 0; KEY_PART_INFO* key_part= table->key_info->key_part; + for (int i=0; i < parts; i++) prefix_length += key_part[i].store_length; + *map= make_prev_keypart_map(parts); - key_copy(key, table->record[0], table->key_info, prefix_length); + + if (!(*key= (uchar *) my_malloc(prefix_length + 1, MYF(MY_WME)))) + { + WSREP_ERROR("Failed to allocate memory for key prefix_length %u", prefix_length); + assert(0); + } + + key_copy(*key, table->record[0], table->key_info, prefix_length); } + } /* namespace Wsrep_schema_impl */ @@ -592,13 +621,15 @@ static void wsrep_init_thd_for_schema(THD *thd) thd->prior_thr_create_utime= thd->start_utime= thd->thr_create_utime; - /* */ - thd->variables.wsrep_on = 0; + /* No Galera replication */ + thd->variables.wsrep_on= 0; /* No binlogging */ - thd->variables.sql_log_bin = 0; - thd->variables.option_bits &= ~OPTION_BIN_LOG; + thd->variables.sql_log_bin= 0; + thd->variables.option_bits&= ~OPTION_BIN_LOG; + /* No safe updates */ + thd->variables.option_bits&= ~OPTION_SAFE_UPDATES; /* No general log */ - thd->variables.option_bits |= OPTION_LOG_OFF; + thd->variables.option_bits|= OPTION_LOG_OFF; /* Read committed isolation to avoid gap locking */ thd->variables.tx_isolation= ISO_READ_COMMITTED; wsrep_assign_from_threadvars(thd); @@ -653,6 +684,7 @@ int Wsrep_schema::store_view(THD* thd, const Wsrep_view& view) Wsrep_schema_impl::wsrep_off wsrep_off(thd); Wsrep_schema_impl::binlog_off binlog_off(thd); + Wsrep_schema_impl::sql_safe_updates sql_safe_updates(thd); /* Clean up cluster table and members table. @@ -899,13 +931,22 @@ int Wsrep_schema::append_fragment(THD* thd, thd->thread_id, os.str().c_str(), transaction_id.get()); + /* use private query table list for the duration of fragment storing, + populated query table list from "parent DML" may cause problems .e.g + for virtual column handling + */ + Query_tables_list query_tables_list_backup; + thd->lex->reset_n_backup_query_tables_list(&query_tables_list_backup); + Wsrep_schema_impl::binlog_off binlog_off(thd); + Wsrep_schema_impl::sql_safe_updates sql_safe_updates(thd); Wsrep_schema_impl::init_stmt(thd); TABLE* frag_table= 0; if (Wsrep_schema_impl::open_for_write(thd, sr_table_str.c_str(), &frag_table)) { trans_rollback_stmt(thd); + thd->lex->restore_backup_query_tables_list(&query_tables_list_backup); DBUG_RETURN(1); } @@ -919,9 +960,11 @@ int Wsrep_schema::append_fragment(THD* thd, if ((error= Wsrep_schema_impl::insert(frag_table))) { WSREP_ERROR("Failed to write to frag table: %d", error); trans_rollback_stmt(thd); + thd->lex->restore_backup_query_tables_list(&query_tables_list_backup); DBUG_RETURN(1); } Wsrep_schema_impl::finish_stmt(thd); + thd->lex->restore_backup_query_tables_list(&query_tables_list_backup); DBUG_RETURN(0); } @@ -938,15 +981,24 @@ int Wsrep_schema::update_fragment_meta(THD* thd, ws_meta.seqno().get()); DBUG_ASSERT(ws_meta.seqno().is_undefined() == false); + /* use private query table list for the duration of fragment storing, + populated query table list from "parent DML" may cause problems .e.g + for virtual column handling + */ + Query_tables_list query_tables_list_backup; + thd->lex->reset_n_backup_query_tables_list(&query_tables_list_backup); + Wsrep_schema_impl::binlog_off binlog_off(thd); + Wsrep_schema_impl::sql_safe_updates sql_safe_updates(thd); int error; - uchar key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; + uchar *key=NULL; key_part_map key_map= 0; TABLE* frag_table= 0; Wsrep_schema_impl::init_stmt(thd); if (Wsrep_schema_impl::open_for_write(thd, sr_table_str.c_str(), &frag_table)) { + thd->lex->restore_backup_query_tables_list(&query_tables_list_backup); DBUG_RETURN(1); } @@ -954,7 +1006,7 @@ int Wsrep_schema::update_fragment_meta(THD* thd, Wsrep_schema_impl::store(frag_table, 0, ws_meta.server_id()); Wsrep_schema_impl::store(frag_table, 1, ws_meta.transaction_id().get()); Wsrep_schema_impl::store(frag_table, 2, -1); - Wsrep_schema_impl::make_key(frag_table, key, &key_map, 3); + Wsrep_schema_impl::make_key(frag_table, &key, &key_map, 3); if ((error= Wsrep_schema_impl::init_for_index_scan(frag_table, key, key_map))) @@ -967,9 +1019,12 @@ int Wsrep_schema::update_fragment_meta(THD* thd, error); } Wsrep_schema_impl::finish_stmt(thd); + thd->lex->restore_backup_query_tables_list(&query_tables_list_backup); + my_free(key); DBUG_RETURN(1); } + my_free(key); /* Copy the original record to frag_table->record[1] */ store_record(frag_table, record[1]); @@ -982,11 +1037,13 @@ int Wsrep_schema::update_fragment_meta(THD* thd, frag_table->s->table_name.str, error); Wsrep_schema_impl::finish_stmt(thd); + thd->lex->restore_backup_query_tables_list(&query_tables_list_backup); DBUG_RETURN(1); } int ret= Wsrep_schema_impl::end_index_scan(frag_table); Wsrep_schema_impl::finish_stmt(thd); + thd->lex->restore_backup_query_tables_list(&query_tables_list_backup); DBUG_RETURN(ret); } @@ -1002,7 +1059,7 @@ static int remove_fragment(THD* thd, seqno.get()); int ret= 0; int error; - uchar key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; + uchar *key= NULL; key_part_map key_map= 0; DBUG_ASSERT(server_id.is_undefined() == false); @@ -1016,7 +1073,7 @@ static int remove_fragment(THD* thd, Wsrep_schema_impl::store(frag_table, 0, server_id); Wsrep_schema_impl::store(frag_table, 1, transaction_id.get()); Wsrep_schema_impl::store(frag_table, 2, seqno.get()); - Wsrep_schema_impl::make_key(frag_table, key, &key_map, 3); + Wsrep_schema_impl::make_key(frag_table, &key, &key_map, 3); if ((error= Wsrep_schema_impl::init_for_index_scan(frag_table, key, @@ -1038,6 +1095,8 @@ static int remove_fragment(THD* thd, ret= 1; } + if (key) + my_free(key); Wsrep_schema_impl::end_index_scan(frag_table); return ret; } @@ -1053,6 +1112,7 @@ int Wsrep_schema::remove_fragments(THD* thd, WSREP_DEBUG("Removing %zu fragments", fragments.size()); Wsrep_schema_impl::wsrep_off wsrep_off(thd); Wsrep_schema_impl::binlog_off binlog_off(thd); + Wsrep_schema_impl::sql_safe_updates sql_safe_updates(thd); Query_tables_list query_tables_list_backup; Open_tables_backup open_tables_backup; @@ -1120,12 +1180,13 @@ int Wsrep_schema::replay_transaction(THD* orig_thd, Wsrep_schema_impl::wsrep_off wsrep_off(&thd); Wsrep_schema_impl::binlog_off binlog_off(&thd); + Wsrep_schema_impl::sql_safe_updates sql_safe_updates(&thd); Wsrep_schema_impl::thd_context_switch thd_context_switch(orig_thd, &thd); int ret= 1; int error; TABLE* frag_table= 0; - uchar key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; + uchar *key=NULL; key_part_map key_map= 0; for (std::vector<wsrep::seqno>::const_iterator i= fragments.begin(); @@ -1142,7 +1203,7 @@ int Wsrep_schema::replay_transaction(THD* orig_thd, Wsrep_schema_impl::store(frag_table, 0, ws_meta.server_id()); Wsrep_schema_impl::store(frag_table, 1, ws_meta.transaction_id().get()); Wsrep_schema_impl::store(frag_table, 2, i->get()); - Wsrep_schema_impl::make_key(frag_table, key, &key_map, 3); + Wsrep_schema_impl::make_key(frag_table, &key, &key_map, 3); int error= Wsrep_schema_impl::init_for_index_scan(frag_table, key, @@ -1189,6 +1250,7 @@ int Wsrep_schema::replay_transaction(THD* orig_thd, Wsrep_schema_impl::finish_stmt(&thd); DBUG_RETURN(1); } + error= Wsrep_schema_impl::init_for_index_scan(frag_table, key, key_map); @@ -1202,6 +1264,7 @@ int Wsrep_schema::replay_transaction(THD* orig_thd, } error= Wsrep_schema_impl::delete_row(frag_table); + if (error) { WSREP_WARN("Could not delete row from streaming log table: %d", error); @@ -1211,8 +1274,12 @@ int Wsrep_schema::replay_transaction(THD* orig_thd, } Wsrep_schema_impl::end_index_scan(frag_table); Wsrep_schema_impl::finish_stmt(&thd); + my_free(key); + key= NULL; } + if (key) + my_free(key); DBUG_RETURN(ret); } @@ -1228,6 +1295,7 @@ int Wsrep_schema::recover_sr_transactions(THD *orig_thd) Wsrep_storage_service storage_service(&storage_thd); Wsrep_schema_impl::binlog_off binlog_off(&storage_thd); Wsrep_schema_impl::wsrep_off wsrep_off(&storage_thd); + Wsrep_schema_impl::sql_safe_updates sql_safe_updates(&storage_thd); Wsrep_schema_impl::thd_context_switch thd_context_switch(orig_thd, &storage_thd); Wsrep_server_state& server_state(Wsrep_server_state::instance()); diff --git a/sql/wsrep_server_service.cc b/sql/wsrep_server_service.cc index da021d4a7eb..19259a43925 100644 --- a/sql/wsrep_server_service.cc +++ b/sql/wsrep_server_service.cc @@ -40,6 +40,7 @@ static void init_service_thd(THD* thd, char* thread_stack) thd->prior_thr_create_utime= thd->start_utime= microsecond_interval_timer(); thd->set_command(COM_SLEEP); thd->reset_for_next_command(true); + server_threads.insert(thd); // as wsrep_innobase_kill_one_trx() uses find_thread_by_id() } Wsrep_storage_service* @@ -79,6 +80,7 @@ void Wsrep_server_service::release_storage_service( static_cast<Wsrep_storage_service*>(storage_service); THD* thd= ss->m_thd; wsrep_reset_threadvars(thd); + server_threads.erase(thd); delete ss; delete thd; } @@ -92,7 +94,8 @@ wsrep_create_streaming_applier(THD *orig_thd, const char *ctx) streaming transaction is BF aborted and streaming applier is created from BF aborter context. */ Wsrep_threadvars saved_threadvars(wsrep_save_threadvars()); - wsrep_reset_threadvars(saved_threadvars.cur_thd); + if (saved_threadvars.cur_thd) + wsrep_reset_threadvars(saved_threadvars.cur_thd); THD *thd= 0; Wsrep_applier_service *ret= 0; if (!wsrep_create_threadvars() && @@ -109,7 +112,8 @@ wsrep_create_streaming_applier(THD *orig_thd, const char *ctx) } /* Restore original thread local storage state before returning. */ wsrep_restore_threadvars(saved_threadvars); - wsrep_store_threadvars(saved_threadvars.cur_thd); + if (saved_threadvars.cur_thd) + wsrep_store_threadvars(saved_threadvars.cur_thd); return ret; } @@ -138,6 +142,7 @@ void Wsrep_server_service::release_high_priority_service(wsrep::high_priority_se THD* thd= hps->m_thd; delete hps; wsrep_store_threadvars(thd); + server_threads.erase(thd); delete thd; wsrep_delete_threadvars(); } @@ -162,16 +167,19 @@ void Wsrep_server_service::log_message(enum wsrep::log::level level, switch (level) { case wsrep::log::debug: - sql_print_information("debug: %s", message); + WSREP_DEBUG("%s", message); break; case wsrep::log::info: - sql_print_information("%s", message); + WSREP_INFO("%s", message); break; case wsrep::log::warning: - sql_print_warning("%s", message); + WSREP_WARN("%s", message); break; case wsrep::log::error: - sql_print_error("%s", message); + WSREP_ERROR("%s", message); + break; + case wsrep::log::unknown: + WSREP_UNKNOWN("%s", message); break; } } diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc index c024f08dd22..09b388f0868 100644 --- a/sql/wsrep_sst.cc +++ b/sql/wsrep_sst.cc @@ -265,6 +265,12 @@ static bool sst_auth_real_set (const char* value) if (wsrep_sst_auth) { my_free((void*) wsrep_sst_auth); } wsrep_sst_auth= my_strdup(WSREP_SST_AUTH_MASK, MYF(0)); } + else + { + if (wsrep_sst_auth) { my_free((void*) wsrep_sst_auth); } + wsrep_sst_auth= NULL; + } + return 0; } return 1; @@ -308,12 +314,40 @@ bool wsrep_before_SE() } // Signal end of SST -static void wsrep_sst_complete (THD* thd, - int const rcode) +static bool wsrep_sst_complete (THD* thd, + int const rcode, + wsrep::gtid const sst_gtid) { Wsrep_client_service client_service(thd, thd->wsrep_cs()); - Wsrep_server_state::instance().sst_received(client_service, rcode); + Wsrep_server_state& server_state= Wsrep_server_state::instance(); + enum wsrep::server_state::state state= server_state.state(); + bool failed= false; + char start_pos_buf[FN_REFLEN]; + ssize_t len= wsrep::print_to_c_str(sst_gtid, start_pos_buf, FN_REFLEN-1); + start_pos_buf[len]='\0'; + + // Do not call sst_received if we are not in joiner or + // initialized state on server. This is because it + // assumes we are on those states. Give error if we are + // in incorrect state. + if ((state == Wsrep_server_state::s_joiner || + state == Wsrep_server_state::s_initialized)) + { + Wsrep_server_state::instance().sst_received(client_service, + rcode); + WSREP_INFO("SST succeeded for position %s", start_pos_buf); + } + else + { + WSREP_ERROR("SST failed for position %s initialized %d server_state %s", + start_pos_buf, + server_state.is_initialized(), + wsrep::to_c_string(state)); + failed= true; + } + wsrep_joiner_monitor_end(); + return failed; } /* @@ -325,13 +359,15 @@ static void wsrep_sst_complete (THD* thd, @param seqno [IN] Initial state sequence number @param state [IN] Always NULL, also ignored by wsrep provider (?) @param state_len [IN] Always 0, also ignored by wsrep provider (?) + @return true when successful, false if error */ -void wsrep_sst_received (THD* thd, +bool wsrep_sst_received (THD* thd, const wsrep_uuid_t& uuid, wsrep_seqno_t const seqno, const void* const state, size_t const state_len) { + bool error= false; /* To keep track of whether the local uuid:seqno should be updated. Also, note that local state (uuid:seqno) is updated/checkpointed only after we get an @@ -371,8 +407,10 @@ void wsrep_sst_received (THD* thd, if (WSREP_ON) { int const rcode(seqno < 0 ? seqno : 0); - wsrep_sst_complete(thd,rcode); + error= wsrep_sst_complete(thd,rcode, sst_gtid); } + + return error; } static int sst_scan_uuid_seqno (const char* str, @@ -653,7 +691,7 @@ err: /* Read committed isolation to avoid gap locking */ thd->variables.tx_isolation= ISO_READ_COMMITTED; - wsrep_sst_complete (thd, -err); + wsrep_sst_complete (thd, -err, ret_gtid); delete thd; my_thread_end(); @@ -732,8 +770,20 @@ static size_t estimate_cmd_len (bool* extra_args) char c; while ((c = *arg++) != 0) { - /* A whitespace or a single quote requires double quotation marks: */ - if (isspace(c) || c == '\'') + /* + Space, single quote, ampersand, and I/O redirection characters + require text to be enclosed in double quotes: + */ + if (isspace(c) || c == '\'' || c == '&' || c == '|' || +#ifdef __WIN__ + c == '>' || c == '<') +#else + /* + The semicolon is used to separate shell commands, so it must be + enclosed in double quotes as well: + */ + c == '>' || c == '<' || c == ';') +#endif { quotation= true; } @@ -756,10 +806,19 @@ static size_t estimate_cmd_len (bool* extra_args) while ((c = *arg++) != 0) { /* - A whitespace or a single quote requires double - quotation marks: + Space, single quote, ampersand, and I/O redirection characters + require text to be enclosed in double quotes: */ - if (isspace(c) || c == '\'') + if (isspace(c) || c == '\'' || c == '&' || c == '|' || +#ifdef __WIN__ + c == '>' || c == '<') +#else + /* + The semicolon is used to separate shell commands, so it must be + enclosed in double quotes as well: + */ + c == '>' || c == '<' || c == ';') +#endif { quotation= true; } @@ -840,8 +899,20 @@ static void copy_orig_argv (char* cmd_str) char c; while ((c = *arg_scan++) != 0) { - /* A whitespace or a single quote requires double quotation marks: */ - if (isspace(c) || c == '\'') + /* + Space, single quote, ampersand, and I/O redirection characters + require text to be enclosed in double quotes: + */ + if (isspace(c) || c == '\'' || c == '&' || c == '|' || +#ifdef __WIN__ + c == '>' || c == '<') +#else + /* + The semicolon is used to separate shell commands, so it must be + enclosed in double quotes as well: + */ + c == '>' || c == '<' || c == ';') +#endif { quotation= true; } @@ -915,10 +986,19 @@ static void copy_orig_argv (char* cmd_str) while ((c = *arg_scan++) != 0) { /* - A whitespace or a single quote requires double - quotation marks: + Space, single quote, ampersand, and I/O redirection characters + require text to be enclosed in double quotes: */ - if (isspace(c) || c == '\'') + if (isspace(c) || c == '\'' || c == '&' || c == '|' || +#ifdef __WIN__ + c == '>' || c == '<') +#else + /* + The semicolon is used to separate shell commands, so it must be + enclosed in double quotes as well: + */ + c == '>' || c == '<' || c == ';') +#endif { quotation= true; } @@ -1170,6 +1250,19 @@ static ssize_t sst_prepare_mysqldump (const char* addr_in, *addr_out= addr_in; } + pthread_t monitor; + ret = mysql_thread_create (key_wsrep_sst_joiner_monitor, &monitor, NULL, wsrep_sst_joiner_monitor_thread, NULL); + + if (ret) + { + WSREP_ERROR("sst_prepare_other(): mysql_thread_create() failed: %d (%s)", + ret, strerror(ret)); + return -ret; + } + + sst_joiner_completed= false; + pthread_detach (monitor); + return ret; } diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc index 0f72c132d84..023da27c3c1 100644 --- a/sql/wsrep_thd.cc +++ b/sql/wsrep_thd.cc @@ -22,7 +22,6 @@ #include "rpl_rli.h" #include "log_event.h" #include "sql_parse.h" -#include "sql_base.h" // close_thread_tables() #include "mysqld.h" // start_wsrep_THD(); #include "wsrep_applier.h" // start_wsrep_THD(); #include "mysql/service_wsrep.h" @@ -126,11 +125,7 @@ bool wsrep_create_appliers(long threads, bool mutex_protected) return false; } - if (!wsrep_cluster_address || wsrep_cluster_address[0]== 0) - { - WSREP_DEBUG("wsrep_create_appliers exit due to empty address"); - return false; - } + DBUG_ASSERT(wsrep_cluster_address[0]); long wsrep_threads=0; @@ -285,16 +280,14 @@ static void wsrep_rollback_process(THD *rollbacker, void wsrep_create_rollbacker() { - if (wsrep_cluster_address && wsrep_cluster_address[0] != 0) - { - Wsrep_thd_args* args(new Wsrep_thd_args(wsrep_rollback_process, - WSREP_ROLLBACKER_THREAD, - pthread_self())); - - /* create rollbacker */ - if (create_wsrep_THD(args, false)) - WSREP_WARN("Can't create thread to manage wsrep rollback"); - } + DBUG_ASSERT(wsrep_cluster_address[0]); + Wsrep_thd_args* args(new Wsrep_thd_args(wsrep_rollback_process, + WSREP_ROLLBACKER_THREAD, + pthread_self())); + + /* create rollbacker */ + if (create_wsrep_THD(args, false)) + WSREP_WARN("Can't create thread to manage wsrep rollback"); } /* @@ -375,25 +368,6 @@ bool wsrep_bf_abort(const THD* bf_thd, THD* victim_thd) return ret; } -/* - Get auto increment variables for THD. Use global settings for - applier threads. - */ -void wsrep_thd_auto_increment_variables(THD* thd, - unsigned long long* offset, - unsigned long long* increment) -{ - if (wsrep_thd_is_applying(thd) && - thd->wsrep_trx().state() != wsrep::transaction::s_replaying) - { - *offset= global_system_variables.auto_increment_offset; - *increment= global_system_variables.auto_increment_increment; - return; - } - *offset= thd->variables.auto_increment_offset; - *increment= thd->variables.auto_increment_increment; -} - int wsrep_create_threadvars() { int ret= 0; diff --git a/sql/wsrep_trans_observer.h b/sql/wsrep_trans_observer.h index 05970e8b12f..bb9bd54b02f 100644 --- a/sql/wsrep_trans_observer.h +++ b/sql/wsrep_trans_observer.h @@ -407,8 +407,10 @@ static inline void wsrep_after_apply(THD* thd) static inline void wsrep_open(THD* thd) { DBUG_ENTER("wsrep_open"); - if (WSREP(thd)) + if (WSREP_ON_) { + /* WSREP_PROVIDER_EXISTS_ cannot be set if WSREP_ON_ is not set */ + DBUG_ASSERT(WSREP_PROVIDER_EXISTS_); thd->wsrep_cs().open(wsrep::client_id(thd->thread_id)); thd->wsrep_cs().debug_log_level(wsrep_debug); if (!thd->wsrep_applier && thd->variables.wsrep_trx_fragment_size) @@ -431,6 +433,16 @@ static inline void wsrep_close(THD* thd) DBUG_VOID_RETURN; } +static inline void wsrep_cleanup(THD* thd) +{ + DBUG_ENTER("wsrep_cleanup"); + if (thd->wsrep_cs().state() != wsrep::client_state::s_none) + { + thd->wsrep_cs().cleanup(); + } + DBUG_VOID_RETURN; +} + static inline void wsrep_wait_rollback_complete_and_acquire_ownership(THD *thd) { @@ -442,11 +454,17 @@ wsrep_wait_rollback_complete_and_acquire_ownership(THD *thd) DBUG_VOID_RETURN; } -static inline int wsrep_before_command(THD* thd) +static inline int wsrep_before_command(THD* thd, bool keep_command_error) { return (thd->wsrep_cs().state() != wsrep::client_state::s_none ? - thd->wsrep_cs().before_command() : 0); + thd->wsrep_cs().before_command(keep_command_error) : 0); +} + +static inline int wsrep_before_command(THD* thd) +{ + return wsrep_before_command(thd, false); } + /* Called after each command. diff --git a/sql/wsrep_var.cc b/sql/wsrep_var.cc index 5336bc9f508..e4cfd0d89c9 100644 --- a/sql/wsrep_var.cc +++ b/sql/wsrep_var.cc @@ -25,6 +25,7 @@ #include <my_dir.h> #include <cstdio> #include <cstdlib> +#include "wsrep_trans_observer.h" ulong wsrep_reject_queries; @@ -88,10 +89,11 @@ static bool refresh_provider_options() } } -static void wsrep_set_wsrep_on() +void wsrep_set_wsrep_on() { - WSREP_ON_= global_system_variables.wsrep_on && wsrep_provider && - strcmp(wsrep_provider, WSREP_NONE); + WSREP_PROVIDER_EXISTS_= wsrep_provider && + strncasecmp(wsrep_provider, WSREP_NONE, FN_REFLEN); + WSREP_ON_= global_system_variables.wsrep_on && WSREP_PROVIDER_EXISTS_; } /* This is intentionally declared as a weak global symbol, so that @@ -102,7 +104,8 @@ struct handlerton* innodb_hton_ptr __attribute__((weak)); bool wsrep_on_update (sys_var *self, THD* thd, enum_var_type var_type) { - if (var_type == OPT_GLOBAL) { + if (var_type == OPT_GLOBAL) + { my_bool saved_wsrep_on= global_system_variables.wsrep_on; thd->variables.wsrep_on= global_system_variables.wsrep_on; @@ -110,15 +113,15 @@ bool wsrep_on_update (sys_var *self, THD* thd, enum_var_type var_type) // If wsrep has not been inited we need to do it now if (global_system_variables.wsrep_on && wsrep_provider && !wsrep_inited) { - char* tmp= strdup(wsrep_provider); // wsrep_init() rewrites provider - //when fails - + // wsrep_init() rewrites provide if it fails + char* tmp= strdup(wsrep_provider); mysql_mutex_unlock(&LOCK_global_system_variables); if (wsrep_init()) { my_error(ER_CANT_OPEN_LIBRARY, MYF(0), tmp, my_error, "wsrep_init failed"); //rcode= true; + saved_wsrep_on= false; } free(tmp); @@ -130,6 +133,16 @@ bool wsrep_on_update (sys_var *self, THD* thd, enum_var_type var_type) wsrep_set_wsrep_on(); + if (var_type == OPT_GLOBAL) + { + if (thd->variables.wsrep_on && + thd->wsrep_cs().state() == wsrep::client_state::s_none) + { + wsrep_open(thd); + wsrep_before_command(thd); + } + } + return false; } @@ -140,12 +153,57 @@ bool wsrep_on_check(sys_var *self, THD* thd, set_var* var) if (check_has_super(self, thd, var)) return true; - if (new_wsrep_on && innodb_hton_ptr && innodb_lock_schedule_algorithm != 0) { - my_message(ER_WRONG_ARGUMENTS, " WSREP (galera) can't be enabled " - "if innodb_lock_schedule_algorithm=VATS. Please configure" - " innodb_lock_schedule_algorithm=FCFS and restart.", MYF(0)); + if (new_wsrep_on) + { + if (innodb_hton_ptr && innodb_lock_schedule_algorithm != 0) + { + my_message(ER_WRONG_ARGUMENTS, " WSREP (galera) can't be enabled " + "if innodb_lock_schedule_algorithm=VATS. Please configure" + " innodb_lock_schedule_algorithm=FCFS and restart.", MYF(0)); + return true; + } + + if (!WSREP_PROVIDER_EXISTS) + { + my_message(ER_WRONG_ARGUMENTS, "WSREP (galera) can't be enabled " + "if the wsrep_provider is unset or set to 'none'", MYF(0)); + return true; + } + + if (var->type == OPT_SESSION && + !global_system_variables.wsrep_on) + { + my_message(ER_WRONG_ARGUMENTS, + "Can't enable @@session.wsrep_on, " + "while @@global.wsrep_on is disabled", MYF(0)); + return true; + } + } + + if (thd->in_active_multi_stmt_transaction()) + { + my_error(ER_CANT_DO_THIS_DURING_AN_TRANSACTION, MYF(0)); return true; } + + if (var->type == OPT_GLOBAL) + { + /* + The global value is about to change. Cleanup + the transaction state and close the client + state. wsrep_on_update() will take care of + reopening it should wsrep_on be re-enabled. + */ + if (global_system_variables.wsrep_on && !new_wsrep_on) + { + wsrep_commit_empty(thd, true); + wsrep_after_statement(thd); + wsrep_after_command_ignore_result(thd); + wsrep_close(thd); + wsrep_cleanup(thd); + } + } + return false; } @@ -212,8 +270,11 @@ bool wsrep_start_position_verify (const char* start_str) return true; char* endptr; - wsrep_seqno_t const seqno __attribute__((unused)) // to avoid GCC warnings - (strtoll(&start_str[uuid_len + 1], &endptr, 10)); + wsrep_seqno_t const seqno(strtoll(&start_str[uuid_len + 1], &endptr, 10)); + + // Do not allow seqno < -1 + if (*endptr == '\0' && seqno < -1) + return true; // Remaining string was seqno. if (*endptr == '\0') return false; @@ -230,12 +291,24 @@ bool wsrep_set_local_position(THD* thd, const char* const value, size_t const uuid_len= wsrep_uuid_scan(value, length, &uuid); wsrep_seqno_t const seqno= strtoll(value + uuid_len + 1, NULL, 10); - if (sst) { - wsrep_sst_received (thd, uuid, seqno, NULL, 0); - } else { - local_uuid= uuid; - local_seqno= seqno; - } + char start_pos_buf[FN_REFLEN]; + memcpy(start_pos_buf, value, length); + start_pos_buf[length]='\0'; + + // If both are same as WSREP_START_POSITION_ZERO just set local + if (!strcmp(start_pos_buf, WSREP_START_POSITION_ZERO) && + !strcmp(wsrep_start_position, WSREP_START_POSITION_ZERO)) + goto set; + else + WSREP_INFO("SST setting local position to %s current %s", start_pos_buf, wsrep_start_position); + + if (sst) + return (wsrep_sst_received (thd, uuid, seqno, NULL, 0)); + +set: + local_uuid= uuid; + local_seqno= seqno; + return false; } @@ -252,19 +325,34 @@ bool wsrep_start_position_check (sys_var *self, THD* thd, set_var* var) var->save_result.string_value.length); start_pos_buf[var->save_result.string_value.length]= 0; + + WSREP_DEBUG("SST wsrep_start_position check for new position %s old %s", + start_pos_buf, wsrep_start_position); + // Verify the format. if (wsrep_start_position_verify(start_pos_buf)) return true; + + // Give error if position is updated when wsrep is not enabled or + // provider is not loaded. + if ((!WSREP_ON || !Wsrep_server_state::instance().is_provider_loaded()) + && strcmp(start_pos_buf, WSREP_START_POSITION_ZERO)) + { + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_VALUE_FOR_VAR, + "Cannot set 'wsrep_start_position' because " + "wsrep is switched off or provider is not loaded"); + goto err; + } + /* As part of further verification, we try to update the value and catch - errors (if any). + errors (if any) only when value actually has been changed. */ if (wsrep_set_local_position(thd, var->save_result.string_value.str, var->save_result.string_value.length, true)) - { goto err; - } return false; @@ -286,7 +374,7 @@ bool wsrep_start_position_init (const char* val) { if (NULL == val || wsrep_start_position_verify (val)) { - WSREP_ERROR("Bad initial value for wsrep_start_position: %s", + WSREP_ERROR("Bad initial value for wsrep_start_position: %s", (val ? val : "")); return true; } @@ -400,8 +488,8 @@ bool wsrep_provider_update (sys_var *self, THD* thd, enum_var_type type) void wsrep_provider_init (const char* value) { - WSREP_DEBUG("wsrep_provider_init: %s -> %s", - (wsrep_provider) ? wsrep_provider : "null", + WSREP_DEBUG("wsrep_provider_init: %s -> %s", + (wsrep_provider) ? wsrep_provider : "null", (value) ? value : "null"); if (NULL == value || wsrep_provider_verify (value)) { @@ -427,20 +515,26 @@ bool wsrep_provider_options_check(sys_var *self, THD* thd, set_var* var) bool wsrep_provider_options_update(sys_var *self, THD* thd, enum_var_type type) { - enum wsrep::provider::status ret= - Wsrep_server_state::instance().provider().options(wsrep_provider_options); - if (ret) + if (wsrep_provider_options) { - WSREP_ERROR("Set options returned %d", ret); - refresh_provider_options(); - return true; + enum wsrep::provider::status ret= + Wsrep_server_state::instance().provider().options(wsrep_provider_options); + if (ret) + { + WSREP_ERROR("Set options returned %d", ret); + goto err; + } + + return refresh_provider_options(); } - return refresh_provider_options(); +err: + refresh_provider_options(); + return true; } void wsrep_provider_options_init(const char* value) { - if (wsrep_provider_options && wsrep_provider_options != value) + if (wsrep_provider_options && wsrep_provider_options != value) my_free((void *)wsrep_provider_options); wsrep_provider_options= (value) ? my_strdup(value, MYF(0)) : NULL; } @@ -469,8 +563,21 @@ bool wsrep_reject_queries_update(sys_var *self, THD* thd, enum_var_type type) bool wsrep_debug_update(sys_var *self, THD* thd, enum_var_type type) { + // Give warnings if wsrep_debug is set and wsrep is disabled or + // provider is not loaded, it will not have any effect + if ((!WSREP_ON || !Wsrep_server_state::instance().is_provider_loaded()) + && wsrep_debug) + { + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_VALUE_FOR_VAR, + "Setting 'wsrep_debug' has no effect because " + "wsrep is switched off"); + wsrep_debug= 0; + } + else Wsrep_server_state::instance().debug_log_level(wsrep_debug); - return false; + + return false; } static int wsrep_cluster_address_verify (const char* cluster_address_str) @@ -508,41 +615,42 @@ bool wsrep_cluster_address_update (sys_var *self, THD* thd, enum_var_type type) return false; } - /* stop replication is heavy operation, and includes closing all client + /* stop replication is heavy operation, and includes closing all client connections. Closing clients may need to get LOCK_global_system_variables at least in MariaDB. - - Note: releasing LOCK_global_system_variables may cause race condition, if - there can be several concurrent clients changing wsrep_provider */ + char *tmp= my_strdup(wsrep_cluster_address, MYF(MY_WME)); WSREP_DEBUG("wsrep_cluster_address_update: %s", wsrep_cluster_address); mysql_mutex_unlock(&LOCK_global_system_variables); + + mysql_mutex_lock(&LOCK_wsrep_cluster_config); wsrep_stop_replication(thd); - if (wsrep_start_replication()) + if (*tmp && wsrep_start_replication(tmp)) { wsrep_create_rollbacker(); WSREP_DEBUG("Cluster address update creating %ld applier threads running %lu", wsrep_slave_threads, wsrep_running_applier_threads); wsrep_create_appliers(wsrep_slave_threads); } - /* locking order to be enforced is: - 1. LOCK_global_system_variables - 2. LOCK_wsrep_cluster_config - => have to juggle mutexes to comply with this - */ - mysql_mutex_unlock(&LOCK_wsrep_cluster_config); + mysql_mutex_lock(&LOCK_global_system_variables); - mysql_mutex_lock(&LOCK_wsrep_cluster_config); + if (strcmp(tmp, wsrep_cluster_address)) + { + my_free((void*)wsrep_cluster_address); + wsrep_cluster_address= tmp; + } + else + my_free(tmp); return false; } void wsrep_cluster_address_init (const char* value) { - WSREP_DEBUG("wsrep_cluster_address_init: %s -> %s", - (wsrep_cluster_address) ? wsrep_cluster_address : "null", + WSREP_DEBUG("wsrep_cluster_address_init: %s -> %s", + (wsrep_cluster_address) ? wsrep_cluster_address : "null", (value) ? value : "null"); my_free((void*) wsrep_cluster_address); @@ -631,7 +739,12 @@ static void wsrep_slave_count_change_update () bool wsrep_slave_threads_update (sys_var *self, THD* thd, enum_var_type type) { + if (!wsrep_cluster_address_exists()) + return false; + + mysql_mutex_unlock(&LOCK_global_system_variables); mysql_mutex_lock(&LOCK_wsrep_slave_threads); + mysql_mutex_lock(&LOCK_global_system_variables); bool res= false; wsrep_slave_count_change_update(); @@ -742,6 +855,18 @@ bool wsrep_trx_fragment_size_update(sys_var* self, THD *thd, enum_var_type) { WSREP_DEBUG("wsrep_trx_fragment_size_update: %llu", thd->variables.wsrep_trx_fragment_size); + + // Give error if wsrep_trx_fragment_size is set and wsrep is disabled or + // provider is not loaded + if (!WSREP_ON || !Wsrep_server_state::instance().is_provider_loaded()) + { + push_warning (thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_VALUE_FOR_VAR, + "Cannot set 'wsrep_trx_fragment_size' because " + "wsrep is switched off"); + return true; + } + if (thd->variables.wsrep_trx_fragment_size) { return thd->wsrep_cs().enable_streaming( @@ -759,6 +884,18 @@ bool wsrep_trx_fragment_unit_update(sys_var* self, THD *thd, enum_var_type) { WSREP_DEBUG("wsrep_trx_fragment_unit_update: %lu", thd->variables.wsrep_trx_fragment_unit); + + // Give error if wsrep_trx_fragment_unit is set and wsrep is disabled or + // provider is not loaded + if (!WSREP_ON || !Wsrep_server_state::instance().is_provider_loaded()) + { + push_warning (thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_VALUE_FOR_VAR, + "Cannot set 'wsrep_trx_fragment_unit' because " + "wsrep is switched off"); + return true; + } + if (thd->variables.wsrep_trx_fragment_size) { return thd->wsrep_cs().enable_streaming( diff --git a/sql/wsrep_var.h b/sql/wsrep_var.h index 481df02f2d5..b1b2932cdfe 100644 --- a/sql/wsrep_var.h +++ b/sql/wsrep_var.h @@ -35,6 +35,7 @@ class set_var; class THD; int wsrep_init_vars(); +void wsrep_set_wsrep_on(); #define CHECK_ARGS (sys_var *self, THD* thd, set_var *var) #define UPDATE_ARGS (sys_var *self, THD* thd, enum_var_type type) |