diff options
Diffstat (limited to 'sql')
93 files changed, 2029 insertions, 986 deletions
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 4049443de2a..63552e60c4b 100644 --- a/sql/create_options.cc +++ b/sql/create_options.cc @@ -97,14 +97,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/field.cc b/sql/field.cc index bdaaecc2026..652228beceb 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -8255,7 +8255,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) DBUG_ASSERT(length <= max_data_length()); new_length= length; - copy_length= (uint)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, @@ -8446,7 +8446,10 @@ int Field_blob::cmp_binary(const uchar *a_ptr, const uchar *b_ptr, b_length=get_length(b_ptr); if (b_length > max_length) b_length=max_length; - diff=memcmp(a,b,MY_MIN(a_length,b_length)); + if (uint32 len= MY_MIN(a_length,b_length)) + diff= memcmp(a,b,len); + else + diff= 0; return diff ? diff : (int) (a_length - b_length); } @@ -8503,7 +8506,8 @@ uint Field_blob::get_key_image(uchar *buff,uint length, imagetype type_arg) length=(uint) blob_length; } int2store(buff,length); - memcpy(buff+HA_KEY_BLOB_LENGTH, blob, length); + if (length) + memcpy(buff+HA_KEY_BLOB_LENGTH, blob, length); return HA_KEY_BLOB_LENGTH+length; } @@ -11162,7 +11166,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))) { @@ -11174,7 +11178,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); } - 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 fea40251587..18e44f1d9d4 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1339,8 +1339,6 @@ public: virtual uint max_packed_col_length(uint max_length) { return max_length;} - virtual bool is_packable() const { return false; } - uint offset(uchar *record) const { return (uint) (ptr - record); @@ -1829,7 +1827,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) */ diff --git a/sql/filesort.cc b/sql/filesort.cc index 96f9aa874da..d76c39c3bd4 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -1971,14 +1971,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; @@ -1989,10 +1982,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->sortlength(thd, sortorder->item, sortorder); if (use_strnxfrm(sortorder->item->collation.collation)) { @@ -2001,8 +1990,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/ha_partition.cc b/sql/ha_partition.cc index daac3f6a86d..523e76e511e 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -4124,7 +4124,7 @@ int ha_partition::write_row(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; @@ -4173,9 +4173,9 @@ int ha_partition::write_row(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; diff --git a/sql/handler.cc b/sql/handler.cc index d2408767530..87592beb5d3 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -838,6 +838,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); @@ -3118,6 +3119,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): @@ -4839,6 +4847,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); diff --git a/sql/handler.h b/sql/handler.h index 0aa56afe1a5..e0e0604176d 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -584,8 +584,10 @@ struct xid_t { void set(long f, const char *g, long gl, const char *b, long bl) { formatID= f; - memcpy(data, g, gtrid_length= gl); - memcpy(data+gl, b, bqual_length= bl); + if ((gtrid_length= gl)) + memcpy(data, g, gl); + if ((bqual_length= bl)) + memcpy(data+gl, b, bl); } void set(ulonglong xid) { @@ -1235,7 +1237,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); @@ -1417,6 +1419,12 @@ handlerton *ha_default_tmp_handlerton(THD *thd); // MySQL compatibility. Unused. #define HTON_SUPPORTS_FOREIGN_KEYS (1 << 0) //Foreign key constraint supported. +/* + 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 diff --git a/sql/item.cc b/sql/item.cc index 994d45a9dc3..45f40874ddb 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1486,7 +1486,7 @@ int Item::save_in_field_no_warnings(Field *field, bool no_conversions) TABLE *table= field->table; THD *thd= table->in_use; enum_check_fields tmp= thd->count_cuted_fields; - 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); sql_mode_t sql_mode= thd->variables.sql_mode; thd->variables.sql_mode&= ~(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE); thd->variables.sql_mode|= MODE_INVALID_DATES; @@ -1495,7 +1495,7 @@ int Item::save_in_field_no_warnings(Field *field, bool no_conversions) res= save_in_field(field, no_conversions); thd->count_cuted_fields= tmp; - dbug_tmp_restore_column_map(table->write_set, old_map); + dbug_tmp_restore_column_map(&table->write_set, old_map); thd->variables.sql_mode= sql_mode; return res; } @@ -8160,6 +8160,22 @@ bool Item_direct_ref::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) } +longlong Item_direct_ref::val_time_packed() +{ + longlong tmp = (*ref)->val_time_packed(); + null_value= (*ref)->null_value; + return tmp; +} + + +longlong Item_direct_ref::val_datetime_packed() +{ + longlong tmp = (*ref)->val_datetime_packed(); + null_value= (*ref)->null_value; + return tmp; +} + + Item_cache_wrapper::~Item_cache_wrapper() { DBUG_ASSERT(expr_cache == 0); @@ -9674,7 +9690,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; } @@ -9844,7 +9860,7 @@ bool Item_cache_temporal::cache_value() return true; value= pack_time(<ime); } - null_value= example->null_value; + null_value_inside= null_value= example->null_value; return true; } @@ -9939,7 +9955,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; } @@ -10001,7 +10017,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; } @@ -10067,11 +10084,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) { @@ -10170,6 +10190,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++) @@ -10202,12 +10224,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 a49f9e8e5e4..cb1e8519b27 100644 --- a/sql/item.h +++ b/sql/item.h @@ -818,6 +818,12 @@ public: void set_name_for_rollback(THD *thd, const char *str, uint length, CHARSET_INFO *cs); void rename(char *new_name); + void share_name_with(Item *item) + { + name= item->name; + name_length= item->name_length; + is_autogenerated_name= item->is_autogenerated_name; + } void init_make_field(Send_field *tmp_field,enum enum_field_types type); virtual void cleanup(); virtual void make_field(THD *thd, Send_field *field); @@ -4665,13 +4671,16 @@ 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); my_decimal *val_decimal(my_decimal *); bool val_bool(); + String *val_str(String* tmp); bool is_null(); bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + longlong val_datetime_packed(); + longlong val_time_packed(); virtual Ref_Type ref_type() { return DIRECT_REF; } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_direct_ref>(thd, mem_root, this); } @@ -4986,6 +4995,20 @@ public: } return Item_direct_ref::get_date(ltime, fuzzydate); } + longlong val_time_packed() + { + if (check_null_ref()) + return 0; + else + return Item_direct_ref::val_time_packed(); + } + longlong val_datetime_packed() + { + if (check_null_ref()) + return 0; + else + return Item_direct_ref::val_datetime_packed(); + } bool send(Protocol *protocol, String *buffer); void save_org_in_field(Field *field, fast_field_copier data __attribute__ ((__unused__))) @@ -5635,6 +5658,14 @@ protected: */ bool value_cached; 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_basic_constant(thd), Type_handler_hybrid_field_type(MYSQL_TYPE_STRING), @@ -5644,6 +5675,7 @@ public: fixed= 1; maybe_null= 1; null_value= 1; + null_value_inside= true; } protected: Item_cache(THD *thd, enum_field_types field_type_arg): @@ -5655,6 +5687,7 @@ protected: fixed= 1; maybe_null= 1; null_value= 1; + null_value_inside= true; } public: diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 2a0972216f8..7b7604053e3 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -428,13 +428,13 @@ static bool convert_const_to_int(THD *thd, Item_field *field_item, TABLE *table= field->table; sql_mode_t orig_sql_mode= thd->variables.sql_mode; enum_check_fields orig_count_cuted_fields= thd->count_cuted_fields; - 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= (orig_sql_mode & ~MODE_NO_ZERO_DATE) | MODE_INVALID_DATES; @@ -478,7 +478,7 @@ static bool convert_const_to_int(THD *thd, Item_field *field_item, thd->variables.sql_mode= orig_sql_mode; thd->count_cuted_fields= orig_count_cuted_fields; 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; } @@ -1234,7 +1234,9 @@ longlong Item_func_truth::val_int() bool Item_in_optimizer::is_top_level_item() { - 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; } @@ -1596,7 +1598,7 @@ longlong Item_in_optimizer::val_int() DBUG_RETURN(res); } - if (cache->null_value) + if (cache->null_value_inside) { DBUG_PRINT("info", ("Left NULL...")); /* @@ -5261,6 +5263,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) { @@ -5347,15 +5350,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) { @@ -5413,7 +5430,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref) { DBUG_ASSERT(fixed == 0); if (Item_bool_func2::fix_fields(thd, ref) || - escape_item->fix_fields(thd, &escape_item) || + (!escape_item->fixed && escape_item->fix_fields(thd, &escape_item)) || fix_escape_item(thd, escape_item, &cmp_value1, escape_used_in_parsing, cmp_collation.collation, &escape)) return TRUE; diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 22736339bf6..26469d88929 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -2013,6 +2013,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, MEM_ROOT *mem_root) diff --git a/sql/item_create.cc b/sql/item_create.cc index 1cf5a06a3a4..4bfb2615fbe 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -202,7 +202,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() {} @@ -229,7 +228,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() {} @@ -975,6 +973,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: + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + static Create_func_distance_sphere s_singleton; + + protected: + Create_func_distance_sphere() {} + virtual ~Create_func_distance_sphere() {} +}; + #endif @@ -4761,6 +4772,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_STRING 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 @@ -7051,6 +7082,7 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("ST_WITHIN") }, GEOM_BUILDER(Create_func_within)}, { { C_STRING_WITH_LEN("ST_X") }, GEOM_BUILDER(Create_func_x)}, { { C_STRING_WITH_LEN("ST_Y") }, GEOM_BUILDER(Create_func_y)}, + { { C_STRING_WITH_LEN("ST_DISTANCE_SPHERE") }, GEOM_BUILDER(Create_func_distance_sphere)}, { { C_STRING_WITH_LEN("SUBSTRING_INDEX") }, BUILDER(Create_func_substr_index)}, { { C_STRING_WITH_LEN("SUBTIME") }, BUILDER(Create_func_subtime)}, { { C_STRING_WITH_LEN("TAN") }, BUILDER(Create_func_tan)}, diff --git a/sql/item_func.cc b/sql/item_func.cc index 4d03e209eab..7b39b7710fb 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -4882,7 +4882,8 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, uint length, length--; // Fix length change above entry->value[length]= 0; // Store end \0 } - memmove(entry->value, ptr, length); + if (length) + memmove(entry->value, ptr, length); if (type == DECIMAL_RESULT) ((my_decimal*)entry->value)->fix_buffer_pointer(); entry->length= length; diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index 1c3fafc0582..0daf9da4a81 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -145,7 +145,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; } @@ -182,7 +182,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); @@ -2537,11 +2537,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 a858a9a2d57..efff940ec4b 100644 --- a/sql/item_geofunc.h +++ b/sql/item_geofunc.h @@ -714,6 +714,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, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_sphere_distance>(thd, mem_root, this); } +}; + + class Item_func_pointonsurface: public Item_geometry_func { String tmp_value; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 802bfca64b7..4b8f118ca43 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -368,7 +368,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; } @@ -437,6 +437,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 @@ -831,7 +851,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; } @@ -1299,7 +1319,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(); } @@ -1325,7 +1355,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; @@ -1334,6 +1368,7 @@ longlong Item_singlerow_subselect::val_int() else { reset(); + DBUG_ASSERT(null_value); return 0; } } @@ -1342,7 +1377,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; @@ -1351,6 +1390,7 @@ String *Item_singlerow_subselect::val_str(String *str) else { reset(); + DBUG_ASSERT(null_value); return 0; } } @@ -1360,7 +1400,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; @@ -1369,6 +1413,7 @@ my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value) else { reset(); + DBUG_ASSERT(null_value); return 0; } } @@ -1378,7 +1423,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; @@ -1387,6 +1436,7 @@ bool Item_singlerow_subselect::val_bool() else { reset(); + DBUG_ASSERT(null_value); return 0; } } @@ -1396,7 +1446,11 @@ bool Item_singlerow_subselect::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) { DBUG_ASSERT(fixed == 1); if (forced_const) - return value->get_date(ltime, fuzzydate); + { + bool val= value->get_date(ltime, fuzzydate); + null_value= value->null_value; + return val; + } if (!exec() && !value->null_value) { null_value= FALSE; @@ -1405,6 +1459,7 @@ bool Item_singlerow_subselect::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) else { reset(); + DBUG_ASSERT(null_value); return 1; } } diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 2292c22480f..130f90839e3 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -48,7 +48,11 @@ class Cached_item; class Item_subselect :public Item_result_field, protected Used_tables_and_const_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 */ @@ -249,6 +253,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. diff --git a/sql/item_sum.cc b/sql/item_sum.cc index e50822e71f2..dd65f04a652 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2015, Oracle and/or its affiliates. - Copyright (c) 2008, 2015, MariaDB + Copyright (c) 2008, 2020, 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 @@ -464,7 +464,8 @@ Item_sum::Item_sum(THD *thd, Item_sum *item): if (!(orig_args= (Item**) thd->alloc(sizeof(Item*)*arg_count))) return; } - memcpy(orig_args, item->orig_args, sizeof(Item*)*arg_count); + if (arg_count) + memcpy(orig_args, item->orig_args, sizeof(Item*)*arg_count); init_aggregator(); with_distinct= item->with_distinct; if (item->aggr) @@ -1136,7 +1137,8 @@ Item_sum_num::fix_fields(THD *thd, Item **ref) check_sum_func(thd, ref)) return TRUE; - memcpy (orig_args, args, sizeof (Item *) * arg_count); + if (arg_count) + memcpy (orig_args, args, sizeof (Item *) * arg_count); fixed= 1; return FALSE; } @@ -3169,7 +3171,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; @@ -3312,7 +3314,8 @@ Item_func_group_concat(THD *thd, Name_resolution_context *context_arg, /* orig_args is only used for print() */ orig_args= (Item**) (order + arg_count_order); - memcpy(orig_args, args, sizeof(Item*) * arg_count); + if (arg_count) + memcpy(orig_args, args, sizeof(Item*) * arg_count); } @@ -3496,7 +3499,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 { @@ -3517,7 +3520,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() { @@ -3563,7 +3566,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; @@ -3621,9 +3624,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/key.cc b/sql/key.cc index 18806efc18f..689b1e9c886 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); @@ -441,7 +440,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/log.cc b/sql/log.cc index 54a8bd9552f..dc8df9c6fdb 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -4397,7 +4397,9 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included) 0, 0, &log_space_reclaimed); mysql_mutex_lock(&rli->log_space_lock); - rli->log_space_total-= log_space_reclaimed; + my_atomic_add64_explicit((volatile int64*)(&rli->log_space_total), + (-(int64)log_space_reclaimed), + MY_MEMORY_ORDER_RELAXED); mysql_cond_broadcast(&rli->log_space_cond); mysql_mutex_unlock(&rli->log_space_lock); diff --git a/sql/log.h b/sql/log.h index 0e6b2c895af..0770861fe01 100644 --- a/sql/log.h +++ b/sql/log.h @@ -716,7 +716,9 @@ public: char buf1[22],buf2[22]; #endif DBUG_ENTER("harvest_bytes_written"); - (*counter)+=bytes_written; + + my_atomic_add64_explicit((volatile int64*)(counter), bytes_written, + MY_MEMORY_ORDER_RELAXED); DBUG_PRINT("info",("counter: %s bytes_written: %s", llstr(*counter,buf1), llstr(bytes_written,buf2))); bytes_written=0; diff --git a/sql/log_event.cc b/sql/log_event.cc index 097cbe8da9d..c32f31db1f6 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -7150,10 +7150,10 @@ error: if (thd->transaction_rollback_request) { trans_rollback_implicit(thd); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); } else if (! thd->in_multi_stmt_transaction_mode()) - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); else thd->mdl_context.release_statement_locks(); @@ -8554,7 +8554,7 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi) "COMMIT /* implicit, from Xid_log_event */"); thd->variables.option_bits&= ~OPTION_GTID_BEGIN; res= trans_commit(thd); /* Automatically rolls back on error. */ - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); #ifdef WITH_WSREP if (WSREP(thd)) mysql_mutex_lock(&thd->LOCK_thd_data); @@ -10718,7 +10718,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; @@ -10733,6 +10733,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 @@ -10823,27 +10825,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 if (!WSREP_ON) + { + tables->slave_fk_event_map= new_trg_event_map; + lex->query_tables_last= &tables->next_global; + } } if (open_and_lock_tables(thd, rgi->tables_to_lock, FALSE, 0)) { @@ -11193,8 +11197,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) && thd->wsrep_exec_mode == REPL_RECV) @@ -11212,8 +11215,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); } diff --git a/sql/mdl.cc b/sql/mdl.cc index 9eeb82eeffd..8cb21771991 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -1082,7 +1082,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); } @@ -1155,7 +1155,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); @@ -1581,7 +1581,7 @@ MDL_lock::can_grant_lock(enum_mdl_type type_arg, ticket->is_incompatible_when_granted(type_arg)) { #ifdef WITH_WSREP - if (wsrep_thd_is_BF(requestor_ctx->get_thd(),false) && + if (WSREP_ON && wsrep_thd_is_BF(requestor_ctx->get_thd(),false) && key.mdl_namespace() == MDL_key::GLOBAL) { WSREP_DEBUG("global lock granted for BF: %lu %s", @@ -1615,7 +1615,7 @@ MDL_lock::can_grant_lock(enum_mdl_type type_arg, } else { - if (wsrep_thd_is_BF(requestor_ctx->get_thd(), false) && + if (WSREP_ON && wsrep_thd_is_BF(requestor_ctx->get_thd(), false) && key.mdl_namespace() == MDL_key::GLOBAL) { WSREP_DEBUG("global lock granted for BF (waiting queue): %lu %s", @@ -2842,6 +2842,9 @@ void MDL_context::rollback_to_savepoint(const MDL_savepoint &mdl_savepoint) void MDL_context::release_transactional_locks() { DBUG_ENTER("MDL_context::release_transactional_locks"); + /* Fail if there are active transactions */ + DBUG_ASSERT(!(current_thd->server_status & + (SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY))); release_locks_stored_before(MDL_STATEMENT, NULL); release_locks_stored_before(MDL_TRANSACTION, NULL); DBUG_VOID_RETURN; diff --git a/sql/mf_iocache_encr.cc b/sql/mf_iocache_encr.cc index 29d7074aeb1..434ae08fde0 100644 --- a/sql/mf_iocache_encr.cc +++ b/sql/mf_iocache_encr.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2015, MariaDB + Copyright (c) 2015, 2020, 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 @@ -85,7 +85,6 @@ static int my_b_encr_read(IO_CACHE *info, uchar *Buffer, size_t Count) do { - size_t copied; uint elength, wlength, length; uchar iv[MY_AES_BLOCK_SIZE]= {0}; @@ -116,11 +115,13 @@ static int my_b_encr_read(IO_CACHE *info, uchar *Buffer, size_t Count) DBUG_ASSERT(length <= info->buffer_length); - copied= MY_MIN(Count, (size_t)(length - pos_offset)); - - memcpy(Buffer, info->buffer + pos_offset, copied); - Count-= copied; - Buffer+= copied; + size_t copied= MY_MIN(Count, (size_t)(length - pos_offset)); + if (copied) + { + memcpy(Buffer, info->buffer + pos_offset, copied); + Count-= copied; + Buffer+= copied; + } info->read_pos= info->buffer + pos_offset + copied; info->read_end= info->buffer + length; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index d3e124e3405..48121ce2ea2 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -385,7 +385,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; /* Global variables */ @@ -758,7 +757,7 @@ mysql_mutex_t LOCK_crypt, LOCK_global_system_variables, LOCK_user_conn, LOCK_slave_list, - 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; @@ -947,8 +946,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; static PSI_mutex_info all_server_mutexes[]= @@ -1017,7 +1015,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_LOG_INFO_lock, "LOG_INFO::lock", 0}, { &key_LOCK_thread_count, "LOCK_thread_count", PSI_FLAG_GLOBAL}, { &key_LOCK_thread_cache, "LOCK_thread_cache", PSI_FLAG_GLOBAL}, @@ -1074,7 +1071,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; static PSI_cond_info all_server_conds[]= @@ -1124,7 +1121,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} @@ -2015,8 +2011,11 @@ static void __cdecl kill_server(int sig_ptr) close_connections(); +#ifdef WITH_WSREP if (wsrep_inited == 1) wsrep_deinit(true); + wsrep_sst_auth_free(); +#endif /* WITH_WSREP */ if (sig != MYSQL_KILL_SIGNAL && sig != 0) @@ -2136,6 +2135,7 @@ extern "C" void unireg_abort(int exit_code) /* In bootstrap mode we deinitialize wsrep here. */ if (opt_bootstrap && wsrep_inited) wsrep_deinit(true); + wsrep_sst_auth_free(); } #endif // WITH_WSREP @@ -2379,8 +2379,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); DBUG_VOID_RETURN; } @@ -4838,9 +4836,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, @@ -5422,6 +5417,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))) @@ -5431,20 +5430,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); @@ -8435,8 +8441,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 2947734901c..64e5aef5946 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -568,8 +568,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_slave_list, 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_thread_count, LOCK_global_system_variables; extern mysql_mutex_t LOCK_start_thread; @@ -583,7 +582,6 @@ extern mysql_rwlock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave; extern mysql_prlock_t LOCK_system_variables_hash; extern mysql_cond_t COND_thread_count, COND_start_thread; extern mysql_cond_t COND_manager; -extern mysql_cond_t COND_slave_background; extern int32 thread_running; extern int32 thread_count, service_thread_count; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 798b1f284bc..f3f184367c9 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -3248,8 +3248,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()) { @@ -3261,10 +3260,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 @@ -3479,7 +3478,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, thd->variables.range_alloc_block_size, 0, @@ -3495,7 +3494,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 */ @@ -3592,7 +3591,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 @@ -7158,6 +7157,30 @@ SEL_TREE *Item_func_in::get_func_mm_tree(RANGE_OPT_PARAM *param, if (array->count > NOT_IN_IGNORE_THRESHOLD || !value_item) 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 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 + 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++) != 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(0); + } + } + } + /* Get a SEL_TREE for "(-inf|NULL) < X < c_0" interval. */ uint i=0; do @@ -10362,6 +10385,7 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only, uint keynr= param->real_keynr[idx]; DBUG_ENTER("check_quick_select"); + param->is_ror_scan= FALSE; /* Handle cases when we don't have a valid non-empty list of range */ if (!tree) DBUG_RETURN(HA_POS_ERROR); @@ -14786,6 +14810,113 @@ static void print_ror_scans_arr(TABLE *table, const char *msg, DBUG_VOID_RETURN; } +static String dbug_print_sel_arg_buf; + +static void +print_sel_arg_key(Field *field, const uchar *key, String *out) +{ + TABLE *table= field->table; + MY_BITMAP *old_sets[2]; + dbug_tmp_use_all_columns(table, old_sets, &table->read_set, &table->write_set); + + if (field->real_maybe_null()) + { + if (*key) + { + out->append("NULL"); + goto end; + } + key++; // Skip null byte + } + + field->set_key_image(key, field->pack_length()); + + if (field->type() == MYSQL_TYPE_BIT) + (void) field->val_int_as_str(out, 1); + else + field->val_str(out); + +end: + dbug_tmp_restore_column_maps(&table->read_set, &table->write_set, old_sets); +} + + +/* + @brief + Produce a string representation of an individual SEL_ARG and return pointer + to it + + @detail + Intended usage: + + (gdb) p dbug_print_sel_arg(ptr) +*/ + +const char *dbug_print_sel_arg(SEL_ARG *sel_arg) +{ + StringBuffer<64> buf; + String &out= dbug_print_sel_arg_buf; + out.length(0); + + if (!sel_arg) + { + out.append("NULL"); + goto end; + } + + out.append("SEL_ARG("); + + const char *stype; + switch(sel_arg->type) { + case SEL_ARG::IMPOSSIBLE: + stype="IMPOSSIBLE"; + break; + case SEL_ARG::MAYBE: + stype="MAYBE"; + break; + case SEL_ARG::MAYBE_KEY: + stype="MAYBE_KEY"; + break; + case SEL_ARG::KEY_RANGE: + default: + stype= NULL; + } + + if (stype) + { + out.append("type="); + out.append(stype); + goto end; + } + + if (sel_arg->min_flag & NO_MIN_RANGE) + out.append("-inf"); + else + { + print_sel_arg_key(sel_arg->field, sel_arg->min_value, &buf); + out.append(buf); + } + + out.append((sel_arg->min_flag & NEAR_MIN)? "<" : "<="); + + out.append(sel_arg->field->field_name); + + out.append((sel_arg->max_flag & NEAR_MAX)? "<" : "<="); + + if (sel_arg->max_flag & NO_MAX_RANGE) + out.append("+inf"); + else + { + print_sel_arg_key(sel_arg->field, sel_arg->max_value, &buf); + out.append(buf); + } + + out.append(")"); + +end: + return dbug_print_sel_arg_buf.c_ptr_safe(); +} + /***************************************************************************** ** Print a quick range for debugging @@ -14801,9 +14932,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++) { @@ -14830,7 +14961,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); } @@ -14838,16 +14969,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)); diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 02b95dae44a..868d8b26f4b 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -830,7 +830,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_info.cc b/sql/partition_info.cc index ef817fffe1e..503b523a66c 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -1642,13 +1642,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 de6d1b96f76..1bffcfb3bdb 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -1250,15 +1250,15 @@ bool Protocol_text::store(Field *field) CHARSET_INFO *tocs= this->thd->variables.character_set_results; #ifndef DBUG_OFF 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); #ifndef DBUG_OFF 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_gtid.cc b/sql/rpl_gtid.cc index 43fdeec9e6a..a8f7641fce4 100644 --- a/sql/rpl_gtid.cc +++ b/sql/rpl_gtid.cc @@ -432,7 +432,7 @@ rpl_slave_state::truncate_state_table(THD *thd) close_thread_tables(thd); ha_commit_trans(thd, TRUE); } - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); } reenable_binlog(thd); @@ -726,7 +726,7 @@ end: } else { - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); #ifdef HAVE_REPLICATION rpl_group_info::pending_gtid_deletes_free(elist); #endif diff --git a/sql/rpl_injector.cc b/sql/rpl_injector.cc index 53999d57bf6..3bcc0204036 100644 --- a/sql/rpl_injector.cc +++ b/sql/rpl_injector.cc @@ -68,34 +68,34 @@ injector::transaction::~transaction() */ int injector::transaction::commit() { - DBUG_ENTER("injector::transaction::commit()"); - int error= m_thd->binlog_flush_pending_rows_event(true); - /* - Cluster replication does not preserve statement or - transaction boundaries of the master. Instead, a new - transaction on replication slave is started when a new GCI - (global checkpoint identifier) is issued, and is committed - when the last event of the check point has been received and - processed. This ensures consistency of each cluster in - cluster replication, and there is no requirement for stronger - consistency: MySQL replication is asynchronous with other - engines as well. - - A practical consequence of that is that row level replication - stream passed through the injector thread never contains - COMMIT events. - Here we should preserve the server invariant that there is no - outstanding statement transaction when the normal transaction - is committed by committing the statement transaction - explicitly. - */ - trans_commit_stmt(m_thd); - if (!trans_commit(m_thd)) - { - close_thread_tables(m_thd); - m_thd->mdl_context.release_transactional_locks(); - } - DBUG_RETURN(error); + DBUG_ENTER("injector::transaction::commit()"); + int error= m_thd->binlog_flush_pending_rows_event(true); + /* + Cluster replication does not preserve statement or + transaction boundaries of the master. Instead, a new + transaction on replication slave is started when a new GCI + (global checkpoint identifier) is issued, and is committed + when the last event of the check point has been received and + processed. This ensures consistency of each cluster in + cluster replication, and there is no requirement for stronger + consistency: MySQL replication is asynchronous with other + engines as well. + + A practical consequence of that is that row level replication + stream passed through the injector thread never contains + COMMIT events. + Here we should preserve the server invariant that there is no + outstanding statement transaction when the normal transaction + is committed by committing the statement transaction + explicitly. + */ + trans_commit_stmt(m_thd); + if (!trans_commit(m_thd)) + { + close_thread_tables(m_thd); + m_thd->release_transactional_locks(); + } + DBUG_RETURN(error); } diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc index 4cf87ba73b7..869640fd46f 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" /* Code for optional parallel execution of replicated events on the slave. @@ -82,7 +83,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; @@ -91,7 +92,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 99f8da2c928..5273b33c728 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -454,7 +454,8 @@ static inline int add_relay_log(Relay_log_info* rli,LOG_INFO* linfo) linfo->log_file_name); DBUG_RETURN(1); } - rli->log_space_total += s.st_size; + my_atomic_add64_explicit((volatile int64*)(&rli->log_space_total), + s.st_size, MY_MEMORY_ORDER_RELAXED); DBUG_PRINT("info",("log_space_total: %llu", rli->log_space_total)); DBUG_RETURN(0); } @@ -464,7 +465,8 @@ static int count_relay_log_space(Relay_log_info* rli) { LOG_INFO linfo; DBUG_ENTER("count_relay_log_space"); - rli->log_space_total= 0; + my_atomic_store64_explicit((volatile int64*)(&rli->log_space_total), 0, + MY_MEMORY_ORDER_RELAXED); if (rli->relay_log.find_log_pos(&linfo, NullS, 1)) { sql_print_error("Could not find first log while counting relay log space"); @@ -987,7 +989,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; @@ -999,7 +1001,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) @@ -1223,8 +1225,8 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset, strmake_buf(rli->group_relay_log_name, rli->relay_log.get_log_fname()); strmake_buf(rli->event_relay_log_name, rli->relay_log.get_log_fname()); rli->group_relay_log_pos= rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE; - rli->log_space_total= 0; - + my_atomic_store64_explicit((volatile int64*)(&rli->log_space_total), 0, + MY_MEMORY_ORDER_RELAXED); if (count_relay_log_space(rli)) { *errmsg= "Error counting relay log space"; @@ -1734,7 +1736,7 @@ end: if (table_opened) { close_thread_tables(thd); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); } if (array_inited) delete_dynamic(&array); @@ -1902,7 +1904,7 @@ void rpl_group_info::cleanup_context(THD *thd, bool error) if (error) { - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); if (thd == rli->sql_driver_thd) { @@ -2016,10 +2018,10 @@ void rpl_group_info::slave_close_thread_tables(THD *thd) if (thd->transaction_rollback_request) { trans_rollback_implicit(thd); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); } else if (! thd->in_multi_stmt_transaction_mode()) - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); else thd->mdl_context.release_statement_locks(); diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc index c913a0477d7..61c53e6b6e3 100644 --- a/sql/signal_handler.cc +++ b/sql/signal_handler.cc @@ -30,6 +30,11 @@ #define SIGNAL_FMT "signal %d" #endif + +#ifdef __APPLE__ +#include <sys/sysctl.h> +#endif + #ifndef PATH_MAX #define PATH_MAX 4096 #endif @@ -51,7 +56,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__) char buff[PATH_MAX]; ssize_t len; int fd; @@ -77,6 +82,13 @@ static inline void output_core_info() my_close(fd, MYF(0)); } #endif +#elif defined(__APPLE__) + 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); diff --git a/sql/slave.cc b/sql/slave.cc index 87eacfcfd0a..3124b2d10ab 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -60,6 +60,7 @@ #include "rpl_tblmap.h" #include "debug_sync.h" #include "rpl_parallel.h" +#include "sql_manager.h" #define FLAGSTR(V,F) ((V)&(F)?#F" ":"") @@ -279,8 +280,6 @@ static void init_slave_psi_keys(void) #endif /* HAVE_PSI_INTERFACE */ -static bool slave_background_thread_running; -static bool slave_background_thread_stop; static bool slave_background_thread_gtid_loaded; struct slave_background_kill_t { @@ -289,24 +288,15 @@ struct slave_background_kill_t { } *slave_background_kill_list; -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 *thd= new THD(next_thread_id()); thd->thread_stack= (char*) &thd; /* Set approximate stack start */ thd->system_thread = SYSTEM_THREAD_SLAVE_BACKGROUND; - thread_safe_increment32(&service_thread_count); thd->store_globals(); thd->security_ctx->skip_grants(); thd->set_command(COM_DAEMON); -#ifdef WITH_WSREP thd->variables.wsrep_on= 0; -#endif thd_proc_info(thd, "Loading slave GTID position from table"); if (rpl_load_gtid_slave_state(thd)) @@ -316,136 +306,34 @@ 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; - - thd->ENTER_COND(&COND_slave_background, &LOCK_slave_background, - &stage_slave_background_wait_request, - &old_stage); - for (;;) - { - stop= abort_loop || thd->killed || slave_background_thread_stop; - kill_list= slave_background_kill_list; - if (stop || kill_list) - break; - mysql_cond_wait(&COND_slave_background, &LOCK_slave_background); - } - - slave_background_kill_list= NULL; - 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; - - mysql_mutex_lock(&to_kill->LOCK_thd_data); - to_kill->awake(KILL_CONNECTION); - mysql_mutex_unlock(&to_kill->LOCK_thd_data); - 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); - } - 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; - thread_safe_decrement32(&service_thread_count); - signal_thd_deleted(); - - my_thread_end(); - return 0; } - - -void -slave_background_kill_request(THD *to_kill) +static void bg_slave_kill(void *victim) { - 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); - } + THD *to_kill= (THD *)victim; + mysql_mutex_lock(&to_kill->LOCK_thd_data); + to_kill->awake(KILL_CONNECTION); + mysql_mutex_unlock(&to_kill->LOCK_thd_data); + 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); } - -/* - 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() +void slave_background_kill_request(THD *to_kill) { - 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); + if (to_kill->rgi_slave->killed_for_retry) + return; // Already deadlock killed. + to_kill->rgi_slave->killed_for_retry= rpl_group_info::RETRY_KILL_PENDING; + mysql_manager_submit(bg_slave_kill, to_kill); } - /* Initialize slave structures */ int init_slave() @@ -457,12 +345,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. @@ -1080,7 +975,6 @@ void slave_prepare_for_shutdown() mysql_mutex_lock(&LOCK_active_mi); master_info_index->free_connections(); mysql_mutex_unlock(&LOCK_active_mi); - stop_slave_background_thread(); } /* @@ -1111,8 +1005,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; @@ -2373,7 +2265,10 @@ static bool wait_for_relay_log_space(Relay_log_info* rli) &rli->log_space_lock, &stage_waiting_for_relay_log_space, &old_stage); - while (rli->log_space_limit < rli->log_space_total && + while (rli->log_space_limit < + (ulonglong)my_atomic_load64_explicit((volatile int64*) + (&rli->log_space_total), + MY_MEMORY_ORDER_RELAXED) && !(slave_killed=io_slave_killed(mi)) && !rli->ignore_log_space_limit) mysql_cond_wait(&rli->log_space_cond, &rli->log_space_lock); @@ -2923,7 +2818,10 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full, protocol->store(mi->rli.last_error().message, &my_charset_bin); protocol->store((uint32) mi->rli.slave_skip_counter); protocol->store((ulonglong) mi->rli.group_master_log_pos); - protocol->store((ulonglong) mi->rli.log_space_total); + protocol->store((ulonglong) + my_atomic_load64_explicit((volatile int64*) + (&mi->rli.log_space_total), + MY_MEMORY_ORDER_RELAXED)); protocol->store( mi->rli.until_condition==Relay_log_info::UNTIL_NONE ? "None": @@ -4329,9 +4227,7 @@ pthread_handler_t handle_slave_io(void *arg) goto err; } -#ifdef WITH_WSREP thd->variables.wsrep_on= 0; -#endif if (RUN_HOOK(binlog_relay_io, thread_start, (thd, mi))) { mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL, @@ -4623,7 +4519,9 @@ Stopping slave I/O thread due to out-of-memory error from master"); #endif if (rli->log_space_limit && rli->log_space_limit < - rli->log_space_total && + (ulonglong) my_atomic_load64_explicit((volatile int64*) + (&rli->log_space_total), + MY_MEMORY_ORDER_RELAXED) && !rli->ignore_log_space_limit) if (wait_for_relay_log_space(rli)) { @@ -4638,8 +4536,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; @@ -4648,6 +4549,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); } RUN_HOOK(binlog_relay_io, thread_stop, (thd, mi)); thd->reset_query(); @@ -5236,6 +5138,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); } err_before_start: @@ -7222,7 +7125,10 @@ static Log_event* next_event(rpl_group_info *rgi, ulonglong *event_size) is are able to rotate and purge sometime soon. */ if (rli->log_space_limit && - rli->log_space_limit < rli->log_space_total) + rli->log_space_limit < + (ulonglong) my_atomic_load64_explicit((volatile int64*) + (&rli->log_space_total), + MY_MEMORY_ORDER_RELAXED)) { /* force rotation if not in an unfinished group */ rli->sql_force_rotate_relay= !rli->is_in_group(); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 6a650183fb8..b205d253733 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2163,10 +2163,10 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) if (thd->transaction_rollback_request) { trans_rollback_implicit(thd); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); } else if (! thd->in_multi_stmt_transaction_mode()) - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); else thd->mdl_context.release_statement_locks(); } @@ -3119,10 +3119,10 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, if (thd->transaction_rollback_request) { trans_rollback_implicit(thd); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); } else if (! thd->in_multi_stmt_transaction_mode()) - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); else thd->mdl_context.release_statement_locks(); } diff --git a/sql/spatial.cc b/sql/spatial.cc index 83905fc9f3d..711efa91394 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -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,119 @@ 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, dlong, dlat, res; + + // 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); + } + else + { + if (static_cast<const Gis_point *>(g)->get_xy_radian(&x2r, &y2r)) + DBUG_ASSERT(0); + } + if (this->get_xy_radian(&x1r, &y1r)) + DBUG_ASSERT(0); + // 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: lattitude[-90,90] + if (!((y2r >= -M_PI/2 && y2r <= M_PI/2) && (y1r >= -M_PI/2 && y1r <= M_PI/2))) + { + *error=-1; + return -1; + } + dlat= sin((y2r - y1r)/2)*sin((y2r - y1r)/2); + dlong= sin((x2r - x1r)/2)*sin((x2r - x1r)/2); + res= 2*sphere_radius*asin((sqrt(dlat + cos(y1r)*cos(y2r)*dlong))); + return res; +} + + +/** + 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 @@ -2161,6 +2275,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 e78da6e615d..1da788b07a0 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 641ab69c2a7..f1034986f22 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2770,6 +2770,12 @@ end: int acl_check_setrole(THD *thd, 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); } @@ -8945,7 +8951,8 @@ static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry, if (!handle_as_role) add_user_parameters(&global, (ACL_USER *)acl_entry, (want_access & GRANT_ACL)); - + else if (want_access & GRANT_ACL) + global.append(STRING_WITH_LEN(" WITH GRANT OPTION")); protocol->prepare_for_resend(); protocol->store(global.ptr(),global.length(),global.charset()); if (protocol->write()) diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index af4b9127f8f..17eede61337 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -42,7 +42,7 @@ static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list) trans_rollback_stmt(thd); trans_rollback(thd); close_thread_tables(thd); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); /* table_list->table has been closed and freed. Do not reference @@ -115,7 +115,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, acquire the exclusive lock to satisfy MDL asserts and avoid deadlocks. */ - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); /* Attempt to do full-blown table open in mysql_admin_table() has failed. Let us try to open at least a .FRM for this table. @@ -266,7 +266,7 @@ end: } /* In case of a temporary table there will be no metadata lock. */ if (error && has_mdl_lock) - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); DBUG_RETURN(error); } @@ -543,7 +543,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, trans_rollback(thd); close_thread_tables(thd); table->table= NULL; - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name, MDL_SHARED_NO_READ_WRITE, MDL_TRANSACTION); } @@ -597,7 +597,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, trans_rollback_stmt(thd); trans_rollback(thd); close_thread_tables(thd); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); DBUG_PRINT("admin", ("simple error, admin next table")); continue; case -1: // error, message could be written to net @@ -670,7 +670,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, trans_commit_stmt(thd); trans_commit(thd); close_thread_tables(thd); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); lex->reset_query_tables_list(FALSE); /* Restore Query_tables_list::sql_command value to make statement @@ -803,7 +803,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, thd->open_options|= extra_open_options; close_thread_tables(thd); table->table= NULL; - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name, MDL_SHARED_NO_READ_WRITE, MDL_TRANSACTION); table->mdl_request.set_type(MDL_SHARED_READ); @@ -1035,7 +1035,7 @@ send_result_message: trans_commit_stmt(thd); trans_commit(thd); close_thread_tables(thd); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); /* Clear references to TABLE and MDL_ticket after releasing them. */ table->mdl_request.ticket= NULL; @@ -1188,7 +1188,7 @@ send_result_message: goto err; } close_thread_tables(thd); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); /* If it is CHECK TABLE v1, v2, v3, and v1, v2, v3 are views, we will run @@ -1226,7 +1226,7 @@ err: table->table= 0; } close_thread_tables(thd); // Shouldn't be needed - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); thd->resume_subsequent_commits(suspended_wfc); DBUG_RETURN(TRUE); } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 635e17d1865..b8d18abb50c 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -4341,6 +4341,70 @@ bool table_already_fk_prelocked(TABLE_LIST *tl, LEX_STRING *db, 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) +{ + 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_list->table->file->get_parent_foreign_key_list(thd, &fk_list); + if (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 + static bool can_write[]= { true, false, true, true, false, true }; + thr_lock_type lock_type; + + if ((op & (1 << TRG_EVENT_DELETE) && can_write[fk->delete_method]) + || (op & (1 << TRG_EVENT_UPDATE) && can_write[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->str, fk->foreign_db->length, + fk->foreign_table->str, fk->foreign_table->length, + NULL, lock_type, false, table_list->belong_to_view, + op, &prelocking_ctx->query_tables_last); + } + if (arena) + thd->restore_active_arena(arena, &backup); + + return FALSE; +} + /** Defines how prelocking algorithm for DML statements should handle table list @@ -4381,55 +4445,21 @@ handle_table(THD *thd, Query_tables_list *prelocking_ctx, add_tables_and_routines_for_triggers(thd, prelocking_ctx, table_list)) return TRUE; } - if (table_list->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_list->table->file->get_parent_foreign_key_list(thd, &fk_list); - if (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 - static bool can_write[]= { true, false, true, true, false, true }; - uint8 op= table_list->trg_event_map; - thr_lock_type lock_type; - - if ((op & (1 << TRG_EVENT_DELETE) && can_write[fk->delete_method]) - || (op & (1 << TRG_EVENT_UPDATE) && can_write[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->str, fk->foreign_db->length, - fk->foreign_table->str, fk->foreign_table->length, - NULL, lock_type, false, table_list->belong_to_view, - op, &prelocking_ctx->query_tables_last); - } - if (arena) - thd->restore_active_arena(arena, &backup); + return (prepare_fk_prelocking_list(thd, prelocking_ctx, table_list, + need_prelocking, + table_list->trg_event_map)); } } + else if (table_list->slave_fk_event_map && + table_list->table->file->referenced_by_foreign_key()) + { + return (prepare_fk_prelocking_list(thd, prelocking_ctx, + table_list, need_prelocking, + table_list->slave_fk_event_map)); + } return FALSE; } @@ -5795,6 +5825,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= WRONG_GRANT; else @@ -7647,36 +7678,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)) @@ -8652,7 +8670,7 @@ close_mysql_tables(THD *thd) if (! thd->in_sub_stmt) trans_commit_stmt(thd); close_thread_tables(thd); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); } /* diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 8555ae6ee3e..0de153a5e7d 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1451,7 +1451,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: %lu, \ +sql mode: 0x%llx, sort len: %llu, concat len: %u, div_precision: %lu, \ def_week_frmt: %lu, in_trans: %d, autocommit: %d", (int)flags.client_long_flag, (int)flags.client_protocol_41, @@ -1951,7 +1951,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: %lu, \ +sql mode: 0x%llx, sort len: %llu, concat len: %u, div_precision: %lu, \ def_week_frmt: %lu, 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 6ad73bbb0a2..d428c7fdb80 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -554,11 +554,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; ulong default_week_format; ulong div_precision_increment; MY_LOCALE *lc_time_names; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 0a8c136e556..047d6517a4b 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 @@ -444,6 +444,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) @@ -451,7 +452,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); } @@ -2859,12 +2862,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))) { @@ -4485,7 +4488,7 @@ void destroy_thd(MYSQL_THD thd) void reset_thd(MYSQL_THD thd) { close_thread_tables(thd); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); thd->free_items(); free_root(thd->mem_root, MYF(MY_KEEP_PREALLOC)); } @@ -4548,7 +4551,8 @@ extern "C" size_t thd_query_safe(MYSQL_THD thd, char *buf, size_t buflen) if (!mysql_mutex_trylock(&thd->LOCK_thd_data)) { len= MY_MIN(buflen - 1, thd->query_length()); - memcpy(buf, thd->query(), len); + if (len) + memcpy(buf, thd->query(), len); mysql_mutex_unlock(&thd->LOCK_thd_data); } buf[len]= '\0'; @@ -4726,6 +4730,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 this function + is called while holding lock_sys mutex, therefore we can't + use THD::LOCK_thd_data mutex below to follow mutex ordering rules. + */ + if (WSREP_ON && + wsrep_thd_is_BF(const_cast<THD *>(thd), false) && + wsrep_thd_is_BF(const_cast<THD *>(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 2727b4c84d5..c47ea9c9020 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -555,7 +555,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; @@ -645,6 +644,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). */ @@ -864,11 +865,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, @@ -886,7 +900,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) { @@ -905,7 +919,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); } @@ -4177,6 +4191,13 @@ public: locked_tables_mode= mode_arg; } void leave_locked_tables_mode(); + /* Relesae transactional locks if there are no active transactions */ + void release_transactional_locks() + { + if (!(server_status & + (SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY))) + mdl_context.release_transactional_locks(); + } int decide_logging_format(TABLE_LIST *tables); /* In Some cases when decide_logging_format is called it does not have all @@ -5144,10 +5165,15 @@ class select_union_recursive :public select_union 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 @@ -5157,7 +5183,8 @@ class select_union_recursive :public select_union select_union_recursive(THD *thd_arg): select_union(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, @@ -5449,8 +5476,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; } }; @@ -5985,6 +6010,38 @@ 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 Check_level_instant_set +{ + THD *m_thd; + enum_check_fields m_check_level; +public: + Check_level_instant_set(THD *thd, enum_check_fields temporary_value) + :m_thd(thd), m_check_level(thd->count_cuted_fields) + { + thd->count_cuted_fields= temporary_value; + } + ~Check_level_instant_set() + { + m_thd->count_cuted_fields= m_check_level; + } +}; + class Switch_to_definer_security_ctx { public: diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index b2900a20b28..ec1bc45433a 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -85,7 +85,6 @@ int get_or_create_user_conn(THD *thd, const char *user, uc->host= uc->user + user_len + 1; uc->len= 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)) { @@ -95,6 +94,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: @@ -1112,7 +1112,7 @@ void end_connection(THD *thd) { NET *net= &thd->net; #ifdef WITH_WSREP - if (WSREP(thd)) + if (WSREP(thd) && wsrep) { wsrep_status_t rcode= wsrep->free_connection(wsrep, thd->thread_id); if (rcode) { diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc index fe8e0de71b4..3a2301a5730 100644 --- a/sql/sql_cte.cc +++ b/sql/sql_cte.cc @@ -227,6 +227,7 @@ With_element *With_clause::find_table_def(TABLE_LIST *table, !table->is_fqtn) { table->set_derived(); + table->db= empty_c_string; return with_elem; } } @@ -863,8 +864,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) @@ -880,6 +879,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); @@ -890,6 +890,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: @@ -1428,10 +1444,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_derived.cc b/sql/sql_derived.cc index 39499e6895f..be5905da683 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -677,7 +677,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); @@ -733,17 +733,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_union(thd))) + if ((!derived->is_with_table_recursive_reference() || + !derived->derived_result) && + !(derived->derived_result= new (thd->mem_root) select_union(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(thd, 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; @@ -752,7 +752,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; /* @@ -1199,7 +1200,8 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived) DBUG_RETURN(false); st_select_lex_unit *unit= derived->get_unit(); - st_select_lex *sl= unit->first_select(); + st_select_lex *first_sl= unit->first_select(); + st_select_lex *sl= first_sl; if (derived->prohibit_cond_pushdown) DBUG_RETURN(false); @@ -1311,7 +1313,24 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived) if (!extracted_cond_copy) continue; } - + + /* + Rename the columns of all non-first selects of a union to be compatible + by names with the columns of the first select. It will allow to use copies + of the same expression pushed into having clauses of different selects. + */ + if (sl != first_sl) + { + DBUG_ASSERT(sl->item_list.elements == first_sl->item_list.elements); + List_iterator_fast<Item> it(sl->item_list); + List_iterator_fast<Item> nm_it(unit->types); + Item * item; + while((item= it++)) + { + item->share_name_with(nm_it++); + } + } + /* Transform the references to the 'derived' columns from the condition pushed into the having clause of sl to make them usable in the new context diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index a811cee998f..7c2122b6a0a 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -600,7 +600,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->fixed && item->fix_fields(thd, it_ke.ref())) || @@ -613,9 +612,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 c05c9a7d229..5ae9d3beb8b 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -741,6 +741,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 0c50a251d26..ec79ff6d688 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2948,7 +2948,7 @@ pthread_handler_t handle_delayed_insert(void *arg) if (thd->mdl_context.clone_ticket(&di->grl_protection) || thd->mdl_context.clone_ticket(&di->table_list.mdl_request)) { - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); di->handler_thread_initialized= TRUE; goto err; } @@ -3144,7 +3144,7 @@ pthread_handler_t handle_delayed_insert(void *arg) mysql_mutex_unlock(&thd->LOCK_thd_data); close_thread_tables(thd); // Free the table - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); mysql_cond_broadcast(&di->cond_client); // Safety mysql_mutex_lock(&LOCK_delayed_create); // Because of delayed_get_table @@ -4580,7 +4580,8 @@ bool select_create::send_eof() WSREP_ERROR("Appending table key for CTAS failed: %s, %d", (wsrep_thd_query(thd)) ? wsrep_thd_query(thd) : "void", rcode); - return true; + abort_result_set(); + DBUG_RETURN(true); } /* If commit fails, we should be able to reset the OK status. */ thd->get_stmt_da()->set_overwrite_status(TRUE); diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index 77017812074..4f9facc6d0c 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -1199,7 +1199,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)) { @@ -1395,7 +1395,8 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full) blob_field->get_image(cp, copy->length, blob_field->charset()); DBUG_ASSERT(cp + copy->length + copy->blob_length <= buff + buff_size); - memcpy(cp+copy->length, copy->str, copy->blob_length); + if (copy->blob_length) + memcpy(cp+copy->length, copy->str, copy->blob_length); cp+= copy->length+copy->blob_length; } break; @@ -1648,7 +1649,7 @@ void JOIN_CACHE::get_record_by_pos(uchar *rec_ptr) } -/* +/* Get the match flag from the referenced record: the default implementation SYNOPSIS @@ -1660,6 +1661,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 @@ -1684,6 +1686,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 @@ -1953,6 +1988,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 @@ -1965,7 +2004,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; @@ -1982,13 +2023,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 @@ -1999,11 +2050,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; @@ -2103,7 +2162,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; } @@ -2220,7 +2286,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 1cbc6acfd79..456991037d1 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 e1e307330e3..5059e4f656e 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -4450,9 +4450,11 @@ 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 && tab->table->pos_in_table_list && - tab->table->pos_in_table_list->with && - tab->table->pos_in_table_list->with->is_recursive) + 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()) { uses_cte= true; break; @@ -4583,6 +4585,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; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 965c3f29834..f733f783d0e 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -280,7 +280,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= diff --git a/sql/sql_manager.cc b/sql/sql_manager.cc index 7fa9a78f06f..d57963cceb0 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 83100e743ad..573df24cb33 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2034,7 +2034,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, locks. */ trans_rollback_implicit(thd); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); } thd->cleanup_after_query(); @@ -2099,7 +2099,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, ulonglong options= (ulonglong) (uchar) packet[0]; if (trans_commit_implicit(thd)) break; - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); if (check_global_access(thd,RELOAD_ACL)) break; general_log_print(thd, command, NullS); @@ -2132,7 +2132,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (trans_commit_implicit(thd)) break; close_thread_tables(thd); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); my_ok(thd); break; } @@ -2179,6 +2179,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; @@ -2934,7 +2935,7 @@ err: /* Close tables and release metadata locks. */ close_thread_tables(thd); DBUG_ASSERT(!thd->locked_tables_mode); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); return TRUE; } @@ -2994,6 +2995,146 @@ static bool do_execute_sp(THD *thd, sp_head *sp) /** + 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 @@ -3195,6 +3336,11 @@ mysql_execute_command(THD *thd) #ifdef HAVE_REPLICATION } /* endif unlikely slave */ #endif + /* 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 && WSREP(thd)) { @@ -3246,132 +3392,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); - 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; @@ -3406,7 +3433,7 @@ mysql_execute_command(THD *thd) /* Commit the normal transaction if one is active. */ bool commit_failed= trans_commit_implicit(thd); /* Release metadata locks acquired in this transaction. */ - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); if (commit_failed) { WSREP_DEBUG("implicit commit failed, MDL released: %lld", @@ -4645,7 +4672,7 @@ mysql_execute_command(THD *thd) { res= trans_commit_implicit(thd); thd->locked_tables_list.unlock_locked_tables(thd); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); thd->variables.option_bits&= ~(OPTION_TABLE_LOCK); } if (thd->global_read_lock.is_acquired()) @@ -4659,7 +4686,7 @@ mysql_execute_command(THD *thd) res= trans_commit_implicit(thd); thd->locked_tables_list.unlock_locked_tables(thd); /* Release transactional metadata locks. */ - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); if (res) goto error; @@ -5313,7 +5340,7 @@ mysql_execute_command(THD *thd) DBUG_PRINT("info", ("Executing SQLCOM_BEGIN thd: %p", thd)); if (trans_begin(thd, lex->start_transaction_opt)) { - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); WSREP_DEBUG("BEGIN failed, MDL released: %lld", (longlong) thd->thread_id); goto error; @@ -5331,7 +5358,7 @@ mysql_execute_command(THD *thd) (thd->variables.completion_type == 2 && lex->tx_release != TVL_NO)); bool commit_failed= trans_commit(thd); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); if (commit_failed) { WSREP_DEBUG("COMMIT failed, MDL released: %lld", @@ -5382,7 +5409,7 @@ mysql_execute_command(THD *thd) (thd->variables.completion_type == 2 && lex->tx_release != TVL_NO)); bool rollback_failed= trans_rollback(thd); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); if (rollback_failed) { @@ -5859,7 +5886,6 @@ mysql_execute_command(THD *thd) case SQLCOM_XA_COMMIT: { bool commit_failed= trans_xa_commit(thd); - thd->mdl_context.release_transactional_locks(); if (commit_failed) { WSREP_DEBUG("XA commit failed, MDL released: %lld", @@ -5877,7 +5903,6 @@ mysql_execute_command(THD *thd) case SQLCOM_XA_ROLLBACK: { bool rollback_failed= trans_xa_rollback(thd); - thd->mdl_context.release_transactional_locks(); if (rollback_failed) { WSREP_DEBUG("XA rollback failed, MDL released: %lld", @@ -6095,7 +6120,7 @@ finish: all storage engines including binary log. */ trans_rollback_implicit(thd); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); } else if (stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END)) { @@ -6108,7 +6133,7 @@ finish: /* Commit the normal transaction if one is active. */ trans_commit_implicit(thd); thd->get_stmt_da()->set_overwrite_status(false); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); } } else if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode()) @@ -6123,7 +6148,7 @@ finish: - If in autocommit mode, or outside a transactional context, automatically release metadata locks of the current statement. */ - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); } else if (! thd->in_sub_stmt) { @@ -6145,7 +6170,7 @@ finish: { WSREP_DEBUG("Forcing release of transactional locks for thd: %lld", (longlong) thd->thread_id); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); } #endif /* WITH_WSREP */ @@ -6641,6 +6666,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; @@ -7332,8 +7360,13 @@ void THD::reset_for_next_command(bool do_clear_error) thd->save_prep_leaf_list= false; - DBUG_PRINT("debug", - ("is_current_stmt_binlog_format_row(): %d", +#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", thd->is_current_stmt_binlog_format_row())); DBUG_VOID_RETURN; @@ -8745,6 +8778,8 @@ 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(); + on_context->select_lex = thd->lex->current_select; + on_context->outer_context = thd->lex->current_context()->outer_context; return thd->lex->push_context(on_context, thd->mem_root); } diff --git a/sql/sql_parse.h b/sql/sql_parse.h index ce5953696a2..968fa223e1e 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -101,6 +101,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); pthread_handler_t handle_bootstrap(void *arg); +bool run_set_statement_if_requested(THD *thd, LEX *lex); int mysql_execute_command(THD *thd); bool do_command(THD *thd); void do_handle_bootstrap(THD *thd); diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h index 64194b5a1b5..13f7296e0cd 100644 --- a/sql/sql_plugin.h +++ b/sql/sql_plugin.h @@ -22,9 +22,10 @@ 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_LEX_STRING + SHOW_LONG_NOFLUSH, SHOW_LEX_STRING, \ + /* SHOW_*_STATUS must be at the end, SHOW_LONG_STATUS being first */ \ + SHOW_LONG_STATUS, SHOW_DOUBLE_STATUS, SHOW_LONGLONG_STATUS #include <my_global.h> #undef SHOW_always_last diff --git a/sql/sql_plugin_services.ic b/sql/sql_plugin_services.ic index 43fe540731e..358052132a0 100644 --- a/sql/sql_plugin_services.ic +++ b/sql/sql_plugin_services.ic @@ -177,7 +177,6 @@ static struct wsrep_service_st wsrep_handler = { wsrep_thd_ignore_table, wsrep_thd_trx_seqno, wsrep_thd_ws_handle, - wsrep_thd_auto_increment_variables, wsrep_set_load_multi_commit, wsrep_is_load_multi_commit, wsrep_trx_is_aborting, diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 4bf8142959d..fa335465f02 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 @@ -117,15 +117,15 @@ 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 #include "wsrep_mysqld.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; - /** A result class used to send cursor rows using the binary protocol. */ @@ -2522,6 +2522,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, TYPE_ENUM_PROCEDURE)) == 2) @@ -4239,6 +4249,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 @@ -4257,6 +4277,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(); @@ -4278,7 +4304,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) if (thd->transaction_rollback_request) { trans_rollback_implicit(thd); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); } /* Preserve CHANGE MASTER attributes */ diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 4af8ebc2dd8..59a3f686e45 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -4541,5 +4541,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 8ddfa9239f6..9129aaeed5e 100644 --- a/sql/sql_repl.h +++ b/sql/sql_repl.h @@ -56,6 +56,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 3b090093060..7bfbf719017 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -729,22 +729,6 @@ JOIN::prepare(TABLE_LIST *tables_init, tables_list, select_lex->leaf_tables, 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); - } /* TRUE if the SELECT list mixes elements with and without grouping, @@ -824,6 +808,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()) @@ -3800,6 +3801,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. @@ -11929,10 +11933,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 : "?"), @@ -12275,6 +12275,9 @@ void JOIN::join_free() for (tmp_unit= select_lex->first_inner_unit(); tmp_unit; tmp_unit= tmp_unit->next_unit()) + { + if (tmp_unit->with_element && tmp_unit->with_element->is_recursive) + continue; for (sl= tmp_unit->first_select(); sl; sl= sl->next_select()) { Item_subselect *subselect= sl->master_unit()->item; @@ -12292,7 +12295,7 @@ void JOIN::join_free() /* Can't unlock if at least one JOIN is still needed */ can_unlock= can_unlock && full_local; } - + } /* We are not using tables anymore Unlock all tables. We may be in an INSERT .... SELECT statement. @@ -12668,6 +12671,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 @@ -22464,7 +22468,7 @@ cp_buffer_from_ref(THD *thd, TABLE *table, TABLE_REF *ref) { enum enum_check_fields save_count_cuted_fields= thd->count_cuted_fields; thd->count_cuted_fields= 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++) @@ -22476,7 +22480,7 @@ cp_buffer_from_ref(THD *thd, TABLE *table, TABLE_REF *ref) } } thd->count_cuted_fields= save_count_cuted_fields; - dbug_tmp_restore_column_map(table->write_set, old_map); + dbug_tmp_restore_column_map(&table->write_set, old_map); return result; } @@ -26064,10 +26068,10 @@ JOIN::reoptimize(Item *added_where, table_map join_tables, if (save_to) { DBUG_ASSERT(!keyuse.elements); - memcpy(keyuse.buffer, - save_to->keyuse.buffer, - (size_t) save_to->keyuse.elements * keyuse.size_of_element); keyuse.elements= save_to->keyuse.elements; + if (size_t e= keyuse.elements) + memcpy(keyuse.buffer, + save_to->keyuse.buffer, e * keyuse.size_of_element); } /* Add the new access methods to the keyuse array. */ diff --git a/sql/sql_select.h b/sql/sql_select.h index 4584460ca3f..f41c0df5ad8 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -1842,8 +1842,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 @@ -1854,7 +1854,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; } @@ -1889,8 +1889,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; /* @@ -1911,7 +1911,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); @@ -1947,8 +1947,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) @@ -1960,7 +1960,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_show.cc b/sql/sql_show.cc index 5544c765775..7023e5fe9ea 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1925,7 +1925,6 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, bool check_options= !(sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS) && !create_info_arg; handlerton *hton; - my_bitmap_map *old_map; int error= 0; DBUG_ENTER("show_create_table"); DBUG_PRINT("enter",("table: %s", table->s->table_name.str)); @@ -1987,7 +1986,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); for (ptr=table->field ; (field= *ptr); ptr++) { @@ -2014,8 +2013,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); @@ -2352,7 +2356,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); } @@ -3576,6 +3580,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); @@ -3616,8 +3623,6 @@ uint calc_sum_of_all_status(STATUS_VAR *to) I_List_iterator<THD> it(threads); THD *tmp; - /* Get global values as base */ - *to= global_status_var; to->local_memory_used= 0; /* Add to this status from existing threads */ @@ -5046,6 +5051,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, FALSE, table, schema_table, @@ -7582,13 +7593,7 @@ int fill_status(THD *thd, TABLE_LIST *tables, COND *cond) if (partial_cond) partial_cond->val_int(); - if (scope == OPT_GLOBAL) - { - /* We only hold LOCK_status for summary status vars */ - mysql_mutex_lock(&LOCK_status); - calc_sum_of_all_status(&tmp); - mysql_mutex_unlock(&LOCK_status); - } + tmp.local_memory_used= 0; // meaning tmp was not populated yet mysql_mutex_lock(&LOCK_show_status); res= show_status_array(thd, wild, @@ -9410,7 +9415,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_statistics.cc b/sql/sql_statistics.cc index d22921ba1e2..59c6423e388 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -1042,16 +1042,15 @@ 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 { @@ -1059,10 +1058,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); uint32 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); @@ -1070,42 +1069,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); uint32 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); } @@ -1155,16 +1150,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()); @@ -2087,20 +2096,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= @@ -2111,12 +2124,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; @@ -2138,19 +2145,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++; } } @@ -2599,7 +2604,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); } @@ -2626,7 +2631,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(rows))) break; @@ -2654,7 +2659,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) @@ -2758,7 +2763,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); @@ -3715,6 +3720,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; @@ -3745,7 +3751,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()); @@ -3789,10 +3795,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 fa3e8d6b894..d8788d99574 100644 --- a/sql/sql_statistics.h +++ b/sql/sql_statistics.h @@ -212,6 +212,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_string.cc b/sql/sql_string.cc index b79ca82698b..4e0c7aea84b 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. - Copyright (c) 2016, MariaDB + Copyright (c) 2016, 2020, 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 @@ -175,8 +175,8 @@ bool String::copy(const String &str) { if (alloc(str.str_length)) return TRUE; - str_length=str.str_length; - bmove(Ptr,str.Ptr,str_length); // May be overlapping + if ((str_length=str.str_length)) + bmove(Ptr,str.Ptr,str_length); // May be overlapping Ptr[str_length]=0; str_charset=str.str_charset; return FALSE; @@ -539,8 +539,11 @@ bool String::append_ulonglong(ulonglong val) bool String::append(const char *s,uint32 arg_length, CHARSET_INFO *cs) { + if (!arg_length) + return false; + uint32 offset; - + if (needs_conversion(arg_length, cs, str_charset, &offset)) { uint32 add_length; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 57284272316..cb28c6adcec 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3983,8 +3983,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, { /* 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; @@ -4030,7 +4029,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (key->type == Key::MULTIPLE) { /* 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 */ @@ -4254,7 +4253,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, @@ -5132,6 +5132,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, db, table_name, create_info, alter_info, &is_trans, create_table_mode) > 0) { @@ -5164,6 +5167,8 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, } err: + thd->abort_on_warning= 0; + /* In RBR we don't need to log CREATE TEMPORARY TABLE */ if (!result && thd->is_current_stmt_binlog_format_row() && create_info->tmp_table()) DBUG_RETURN(result); diff --git a/sql/sql_test.cc b/sql/sql_test.cc index 4e68ec2ec2e..4ad0b365612 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -564,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)); @@ -615,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 ea4d7399ea3..7d1f630b88c 100644 --- a/sql/sql_truncate.cc +++ b/sql/sql_truncate.cc @@ -439,6 +439,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_type.h b/sql/sql_type.h index 046b42e4a83..42090037ead 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -92,7 +92,6 @@ public: virtual void sortlength(THD *thd, const Type_std_attributes *item, SORT_FIELD_ATTR *attr) const= 0; - virtual bool is_packable() const { return false; } }; @@ -170,7 +169,6 @@ public: void sortlength(THD *thd, const Type_std_attributes *item, SORT_FIELD_ATTR *attr) const; - bool is_packable()const { return true; } }; diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 8b1719c60cb..7baedfb259c 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -211,7 +211,10 @@ select_union_recursive::create_result_table(THD *thd_arg, create_table, keep_row_order)) 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, "", true, keep_row_order))) @@ -221,20 +224,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; } @@ -272,23 +261,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; } } @@ -715,9 +706,29 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, goto err; if (!derived->table) { - derived->table= with_element->rec_result->rec_tables.head(); - if (derived->derived_result) - derived->derived_result->table= derived->table; + bool res= false; + + if ((!derived->is_with_table_recursive_reference() || + !derived->derived_result) && + !(derived->derived_result= new (thd->mem_root) select_union(thd))) + goto err; // out of memory + thd->create_tmp_table_for_derived= TRUE; + res= derived->derived_result->create_result_table(thd, + &types, + FALSE, + create_options, + derived->alias, + FALSE, FALSE); + thd->create_tmp_table_for_derived= FALSE; + if (res) + goto err; + derived->derived_result->set_unit(this); + derived->table= derived->derived_result->table; + if (derived->is_with_table_recursive_reference()) + { + /* Here 'derived" is the primary recursive table reference */ + derived->with->rec_result->rec_table_refs.push_back(derived); + } } with_element->mark_as_with_prepared_anchor(); is_rec_result_table_created= true; @@ -1272,11 +1283,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"); @@ -1335,8 +1346,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, @@ -1370,13 +1382,7 @@ bool st_select_lex_unit::cleanup() { DBUG_RETURN(FALSE); } - /* - When processing a PS/SP or an EXPLAIN command cleanup of a unit can - be performed immediately when the unit is reached in the cleanup - traversal initiated by the cleanup of the main unit. - */ - if (!thd->stmt_arena->is_stmt_prepare() && !thd->lex->describe && - with_element && with_element->is_recursive && union_result) + if (with_element && with_element->is_recursive && union_result) { select_union_recursive *result= with_element->rec_result; if (++result->cleanup_count == with_element->rec_outer_references) @@ -1554,27 +1560,32 @@ bool st_select_lex::cleanup() if (join) { + List_iterator<TABLE_LIST> ti(leaf_tables); + TABLE_LIST *tbl; + while ((tbl= ti++)) + { + if (tbl->is_recursive_with_table() && + !tbl->is_with_table_recursive_reference()) + { + /* + If query is killed before open_and_process_table() for tbl + is called then 'with' is already set, but 'derived' is not. + */ + st_select_lex_unit *unit= tbl->with->spec; + error|= (bool) error | (uint) unit->cleanup(); + } + } DBUG_ASSERT((st_select_lex*)join->select_lex == this); error= join->destroy(); delete join; join= 0; } - for (TABLE_LIST *tbl= get_table_list(); tbl; tbl= tbl->next_local) - { - if (tbl->is_recursive_with_table() && - !tbl->is_with_table_recursive_reference()) - { - /* - If query is killed before open_and_process_table() for tbl - is called then 'with' is already set, but 'derived' is not. - */ - st_select_lex_unit *unit= tbl->with->spec; - error|= (bool) error | (uint) unit->cleanup(); - } - } + leaf_tables.empty(); for (SELECT_LEX_UNIT *lex_unit= first_inner_unit(); lex_unit ; lex_unit= lex_unit->next_unit()) { + if (lex_unit->with_element && lex_unit->with_element->is_recursive) + continue; error= (bool) ((uint) error | (uint) lex_unit->cleanup()); } inner_refs_list.empty(); @@ -1594,8 +1605,12 @@ void st_select_lex::cleanup_all_joins(bool full) join->cleanup(full); for (unit= first_inner_unit(); unit; unit= unit->next_unit()) + { + if (unit->with_element && unit->with_element->is_recursive) + continue; for (sl= unit->first_select(); sl; sl= sl->next_select()) sl->cleanup_all_joins(full); + } DBUG_VOID_RETURN; } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 5e40cd242a4..01743a6751e 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1398,6 +1398,9 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) if (select_lex->handle_derived(thd->lex, DT_MERGE)) DBUG_RETURN(1); + if (thd->lex->save_prep_leaf_tables()) + DBUG_RETURN(1); + List<Item> *fields= &lex->select_lex.item_list; if (setup_fields_with_no_wrap(thd, Ref_ptr_array(), *fields, MARK_COLUMNS_WRITE, 0, 0)) @@ -1534,7 +1537,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, @@ -1557,6 +1564,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. */ diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 8bfe6896ea2..0701c5233ac 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -291,6 +291,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 2eabc4f0a6d..6eb47f1e49f 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1030,10 +1030,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %parse-param { THD *thd } %lex-param { THD *thd } /* - Currently there are 105 shift/reduce conflicts. + Currently there are 98 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 105 +%expect 115 /* Comments for TOKENS. @@ -1774,7 +1774,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <field_type> int_type real_type -%type <Lex_field_type> type_with_opt_collate field_type +%type <Lex_field_type> field_type %type <Lex_dyncol_type> opt_dyncol_type dyncol_type numeric_dyncol_type temporal_dyncol_type string_dyncol_type @@ -1836,7 +1836,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <item> literal text_literal insert_ident order_ident temporal_literal simple_ident expr opt_expr opt_else 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 expr_or_default set_expr_or_default @@ -3010,11 +3010,12 @@ sp_param_name_and_type: thd->variables.collation_database); $<spvar>$= spvar; } - type_with_opt_collate + field_type { LEX *lex= Lex; sp_variable *spvar= $<spvar>2; + Lex->set_last_field_type($3); if (lex->sphead->fill_field_definition(thd, lex, lex->last_field)) { MYSQL_YYABORT; @@ -3096,14 +3097,15 @@ sp_decl: thd->lex->init_last_field(&spvar->field_def, spvar->name.str, thd->variables.collation_database); } - type_with_opt_collate + field_type sp_opt_default { LEX *lex= Lex; sp_pcontext *pctx= lex->spcont; uint num_vars= pctx->context_var_count(); Item *dflt_value_item= $5; - + Lex->set_last_field_type($4); + if (!dflt_value_item) { dflt_value_item= new (thd->mem_root) Item_null(thd); @@ -6665,20 +6667,6 @@ serial_attribute: ; -type_with_opt_collate: - field_type opt_collate - { - $$= $1; - - if ($2) - { - if (!(Lex->charset= merge_charset_and_collation(Lex->charset, $2))) - MYSQL_YYABORT; - } - Lex->set_last_field_type($1); - } - ; - charset: CHAR_SYM SET {} | CHARSET {} @@ -6751,12 +6739,25 @@ charset_or_alias: } ; +collate: COLLATE_SYM collation_name_or_default + { + Lex->charset= Lex->last_field->charset= $2; + } + ; + opt_binary: /* empty */ { bincmp_collation(NULL, false); } | BYTE_SYM { bincmp_collation(&my_charset_bin, false); } | 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: @@ -8972,23 +8973,19 @@ expr: if ($$ == 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 ($$ == NULL) MYSQL_YYABORT; } - | bool_pri comp_op predicate %prec '=' + | expr comp_op predicate %prec '=' { $$= (*$2)(0)->create(thd, $1, $3); if ($$ == 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 ($$ == NULL) @@ -13528,7 +13525,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); } @@ -13541,16 +13538,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 { @@ -13563,7 +13565,6 @@ kill_expr: } ; - shutdown: SHUTDOWN { Lex->sql_command= SQLCOM_SHUTDOWN; } ; @@ -16858,8 +16859,9 @@ sf_tail: lex->init_last_field(&lex->sphead->m_return_field_def, NULL, thd->variables.collation_database); } - type_with_opt_collate /* $11 */ + field_type /* $11 */ { /* $12 */ + Lex->set_last_field_type($11); if (Lex->sphead->fill_field_definition(thd, Lex, Lex->last_field)) MYSQL_YYABORT; } diff --git a/sql/structs.h b/sql/structs.h index 67fb0d5dd66..27b9725815b 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -90,7 +90,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 2a582a098fb..e4de3d8d0aa 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -2224,7 +2224,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", @@ -3730,7 +3730,7 @@ static bool fix_autocommit(sys_var *self, THD *thd, enum_var_type type) if (trans_commit_stmt(thd) || trans_commit(thd)) { thd->variables.option_bits&= ~OPTION_AUTOCOMMIT; - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); WSREP_DEBUG("autocommit, MDL TRX lock released: %lld", (longlong) thd->thread_id); return true; @@ -4157,11 +4157,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( @@ -4958,7 +4958,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)); @@ -5171,7 +5171,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 e4492f21a30..1004f583448 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -2273,6 +2273,12 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, key_part->null_bit= field->null_bit; key_part->store_length+=HA_KEY_NULL_LENGTH; keyinfo->flags|=HA_NULL_PART_KEY; + + /* + This branch is executed only for user defined key parts of the + secondary indexes. + */ + DBUG_ASSERT(i < keyinfo->user_defined_key_parts); keyinfo->key_length+= HA_KEY_NULL_LENGTH; } if (field->type() == MYSQL_TYPE_BLOB || @@ -2285,7 +2291,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; @@ -2378,7 +2385,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) diff --git a/sql/table.h b/sql/table.h index 14ab0027a79..83c72f76831 100644 --- a/sql/table.h +++ b/sql/table.h @@ -773,7 +773,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 */ @@ -2277,8 +2277,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; @@ -2766,25 +2770,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) { #ifndef DBUG_OFF return tmp_use_all_columns(table, bitmap); @@ -2793,8 +2797,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) { #ifndef DBUG_OFF tmp_restore_column_map(bitmap, old); @@ -2807,22 +2811,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) { #ifndef DBUG_OFF - 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) { #ifndef DBUG_OFF tmp_restore_column_map(read_set, old[0]); @@ -2924,7 +2928,8 @@ inline void mark_as_null_row(TABLE *table) { table->null_row=1; table->status|=STATUS_NULL_ROW; - bfill(table->null_flags,table->s->null_bytes,255); + if (table->s->null_bytes) + bfill(table->null_flags,table->s->null_bytes,255); } bool is_simple_order(ORDER *order); diff --git a/sql/transaction.cc b/sql/transaction.cc index 72b7f8e6fe4..543e0b7ad38 100644 --- a/sql/transaction.cc +++ b/sql/transaction.cc @@ -212,7 +212,7 @@ bool trans_begin(THD *thd, uint flags) Release transactional metadata locks only after the transaction has been committed. */ - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); // The RO/RW options are mutually exclusive. DBUG_ASSERT(!((flags & MYSQL_START_TRANS_OPT_READ_ONLY) && @@ -889,11 +889,13 @@ bool trans_xa_prepare(THD *thd) /** Commit and terminate the a XA transaction. + Transactional locks are released if transaction ended @param thd Current thread @retval FALSE Success @retval TRUE Failure + */ bool trans_xa_commit(THD *thd) @@ -984,6 +986,7 @@ bool trans_xa_commit(THD *thd) thd->transaction.xid_state.xa_state= XA_NOTR; trans_track_end_trx(thd); + thd->mdl_context.release_transactional_locks(); DBUG_RETURN(res); } @@ -991,6 +994,7 @@ bool trans_xa_commit(THD *thd) /** Roll back and terminate a XA transaction. + Transactional locks are released if transaction ended @param thd Current thread @@ -1041,6 +1045,7 @@ bool trans_xa_rollback(THD *thd) thd->transaction.xid_state.xa_state= XA_NOTR; trans_track_end_trx(thd); + thd->mdl_context.release_transactional_locks(); DBUG_RETURN(res); } diff --git a/sql/unireg.cc b/sql/unireg.cc index 083960523c1..7974255af35 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -921,8 +921,11 @@ static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields, it.rewind(); while ((field=it++)) { - memcpy(buff, field->comment.str, field->comment.length); - buff+= field->comment.length; + if (size_t l= field->comment.length) + { + memcpy(buff, field->comment.str, l); + buff+= l; + } } } *buff_arg= buff; @@ -941,7 +944,8 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options, TABLE table; TABLE_SHARE share; Create_field *field; - enum_check_fields old_count_cuted_fields= thd->count_cuted_fields; + 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 */ @@ -960,7 +964,6 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options, null_pos= buff; List_iterator<Create_field> it(create_fields); - thd->count_cuted_fields= CHECK_FIELD_WARN; // To find wrong default values while ((field=it++)) { /* regfield don't have to be deleted as it's allocated on THD::mem_root */ @@ -1036,6 +1039,5 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options, *(null_pos + null_count / 8)|= ~(((uchar) 1 << (null_count & 7)) - 1); err: - thd->count_cuted_fields= old_count_cuted_fields; DBUG_RETURN(error); } /* make_empty_rec */ diff --git a/sql/unireg.h b/sql/unireg.h index 6ce638928e8..efc3d6958f5 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -78,7 +78,8 @@ #define cmp_record(A,B) memcmp((A)->record[0],(A)->B,(size_t) (A)->s->reclength) #define empty_record(A) { \ restore_record((A),s->default_values); \ - bfill((A)->null_flags,(A)->s->null_bytes,255);\ + if ((A)->s->null_bytes) \ + bfill((A)->null_flags,(A)->s->null_bytes,255); \ } /* Defines for use with openfrm, openprt and openfrd */ diff --git a/sql/wsrep_applier.cc b/sql/wsrep_applier.cc index 66335c412e2..ea8ebb7522f 100644 --- a/sql/wsrep_applier.cc +++ b/sql/wsrep_applier.cc @@ -187,7 +187,7 @@ static wsrep_cb_status_t wsrep_apply_events(THD* thd, trans_rollback(thd); thd->locked_tables_list.unlock_locked_tables(thd); /* Release transactional metadata locks. */ - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); thd->wsrep_conflict_state= NO_CONFLICT; DBUG_RETURN(WSREP_CB_FAILURE); } @@ -369,7 +369,7 @@ wsrep_cb_status_t wsrep_commit_cb(void* const ctx, /* Cleanup */ wsrep_set_apply_format(thd, NULL); - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); thd->reset_query(); /* Mutex protected */ free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; diff --git a/sql/wsrep_dummy.cc b/sql/wsrep_dummy.cc index 53941c06892..f5e88aeac1f 100644 --- a/sql/wsrep_dummy.cc +++ b/sql/wsrep_dummy.cc @@ -125,14 +125,6 @@ longlong wsrep_thd_trx_seqno(THD *) struct wsrep_ws_handle* wsrep_thd_ws_handle(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; -} - void wsrep_set_load_multi_commit(THD *thd, bool split) { } diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index d392d1c2a61..f22d8bf0f5a 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -2762,9 +2762,7 @@ extern "C" void wsrep_thd_awake(THD *thd, my_bool signal) { if (signal) { - mysql_mutex_lock(&thd->LOCK_thd_data); thd->awake(KILL_QUERY); - mysql_mutex_unlock(&thd->LOCK_thd_data); } else { @@ -2790,16 +2788,18 @@ extern "C" bool wsrep_thd_ignore_table(THD *thd) extern int wsrep_trx_order_before(THD *thd1, THD *thd2) { - if (wsrep_thd_trx_seqno(thd1) < wsrep_thd_trx_seqno(thd2)) { - WSREP_DEBUG("BF conflict, order: %lld %lld\n", - (long long)wsrep_thd_trx_seqno(thd1), - (long long)wsrep_thd_trx_seqno(thd2)); - return 1; - } - WSREP_DEBUG("waiting for BF, trx order: %lld %lld\n", - (long long)wsrep_thd_trx_seqno(thd1), - (long long)wsrep_thd_trx_seqno(thd2)); - return 0; + const longlong trx1_seqno= wsrep_thd_trx_seqno(thd1); + const longlong trx2_seqno= wsrep_thd_trx_seqno(thd2); + WSREP_DEBUG("BF conflict, order: %lld %lld\n", + trx1_seqno, trx2_seqno); + + if (trx1_seqno == WSREP_SEQNO_UNDEFINED || + trx2_seqno == WSREP_SEQNO_UNDEFINED) + return 1; /* trx is not yet replicated */ + else if (trx1_seqno < trx2_seqno) + return 1; + + return 0; } diff --git a/sql/wsrep_notify.cc b/sql/wsrep_notify.cc index fb04d7be7c1..3683d70a980 100644 --- a/sql/wsrep_notify.cc +++ b/sql/wsrep_notify.cc @@ -40,11 +40,13 @@ void wsrep_notify_status (wsrep_member_status_t status, return; } - char cmd_buf[1 << 16]; // this can be long - long cmd_len = sizeof(cmd_buf) - 1; - char* cmd_ptr = cmd_buf; + 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); @@ -93,6 +95,7 @@ void wsrep_notify_status (wsrep_member_status_t status, { WSREP_ERROR("Notification buffer too short (%ld). Aborting notification.", cmd_len); + my_free(cmd_ptr); return; } @@ -106,5 +109,6 @@ void wsrep_notify_status (wsrep_member_status_t status, WSREP_ERROR("Notification command failed: %d (%s): \"%s\"", err, strerror(err), cmd_ptr); } + my_free(cmd_ptr); } diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc index 65a3f971c62..f4092b9b8a7 100644 --- a/sql/wsrep_sst.cc +++ b/sql/wsrep_sst.cc @@ -149,6 +149,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; @@ -701,8 +707,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; } @@ -725,10 +743,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; } @@ -809,8 +836,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; } @@ -884,10 +923,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; } diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc index 4dddb399bd1..ef8c0e132f7 100644 --- a/sql/wsrep_thd.cc +++ b/sql/wsrep_thd.cc @@ -22,6 +22,7 @@ //#include "global_threads.h" // LOCK_thread_count, etc. #include "sql_base.h" // close_thread_tables() #include "mysqld.h" // start_wsrep_THD(); +#include "debug_sync.h" #include "slave.h" // opt_log_slave_updates #include "rpl_filter.h" @@ -75,7 +76,7 @@ void wsrep_client_rollback(THD *thd) } /* Release transactional metadata locks. */ - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); /* release explicit MDL locks */ thd->mdl_context.release_explicit_locks(); @@ -146,11 +147,12 @@ static void wsrep_prepare_bf_thd(THD *thd, struct wsrep_thd_shadow* shadow) // Disable general logging on applier threads thd->variables.option_bits |= OPTION_LOG_OFF; - // Enable binlogging if opt_log_slave_updates is set - if (opt_log_slave_updates) - thd->variables.option_bits|= OPTION_BIN_LOG; - else - thd->variables.option_bits&= ~(OPTION_BIN_LOG); + + /* enable binlogging regardless of log_slave_updates setting + this is for ensuring that both local and applier transaction go through + same commit ordering algorithm in group commit control + */ + thd->variables.option_bits|= OPTION_BIN_LOG; if (!thd->wsrep_rgi) thd->wsrep_rgi= wsrep_relay_group_init(thd, "wsrep_relay"); @@ -211,7 +213,7 @@ void wsrep_replay_sp_transaction(THD* thd) thd->locked_tables_list.unlock_locked_tables(thd); thd->variables.option_bits&= ~(OPTION_TABLE_LOCK); } - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); mysql_mutex_unlock(&thd->LOCK_thd_data); THD *replay_thd= new THD(true); @@ -350,7 +352,7 @@ void wsrep_replay_transaction(THD *thd) thd->locked_tables_list.unlock_locked_tables(thd); thd->variables.option_bits&= ~(OPTION_TABLE_LOCK); } - thd->mdl_context.release_transactional_locks(); + thd->release_transactional_locks(); /* Replaying will call MYSQL_START_STATEMENT when handling BEGIN Query_log_event so end statement must be called before @@ -370,6 +372,19 @@ void wsrep_replay_transaction(THD *thd) thd->variables.option_bits|= OPTION_BEGIN; thd->server_status|= SERVER_STATUS_IN_TRANS; + /* Allow tests to block the replayer thread using the DBUG facilities */ +#ifdef ENABLED_DEBUG_SYNC + 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))); + };); +#endif /* ENABLED_DEBUG_SYNC */ + int rcode = wsrep->replay_trx(wsrep, &thd->wsrep_ws_handle, (void *)thd); @@ -836,27 +851,6 @@ bool wsrep_thd_has_explicit_locks(THD *thd) return thd->mdl_context.has_explicit_locks(); } -/* - 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 (thd->wsrep_exec_mode == REPL_RECV && - thd->wsrep_conflict_state != REPLAYING) - { - *offset= global_system_variables.auto_increment_offset; - *increment= global_system_variables.auto_increment_increment; - } - else - { - *offset= thd->variables.auto_increment_offset; - *increment= thd->variables.auto_increment_increment; - } -} - my_bool wsrep_thd_is_applier(MYSQL_THD thd) { my_bool is_applier= false; |