diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/field.cc | 2 | ||||
-rw-r--r-- | sql/handler.h | 12 | ||||
-rw-r--r-- | sql/item.cc | 239 | ||||
-rw-r--r-- | sql/item.h | 199 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 10 | ||||
-rw-r--r-- | sql/item_func.cc | 67 | ||||
-rw-r--r-- | sql/item_func.h | 6 | ||||
-rw-r--r-- | sql/item_strfunc.cc | 29 | ||||
-rw-r--r-- | sql/log_event.cc | 7 | ||||
-rw-r--r-- | sql/rpl_rli.cc | 13 | ||||
-rw-r--r-- | sql/share/errmsg.txt | 2 | ||||
-rw-r--r-- | sql/slave.cc | 20 | ||||
-rw-r--r-- | sql/sql_acl.cc | 11 | ||||
-rw-r--r-- | sql/sql_base.cc | 7 | ||||
-rw-r--r-- | sql/sql_class.cc | 2 | ||||
-rw-r--r-- | sql/sql_delete.cc | 20 | ||||
-rw-r--r-- | sql/sql_parse.cc | 8 | ||||
-rw-r--r-- | sql/sql_plugin.cc | 346 | ||||
-rw-r--r-- | sql/sql_plugin.h | 1 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 3 | ||||
-rw-r--r-- | sql/sql_rename.cc | 2 | ||||
-rw-r--r-- | sql/sql_select.cc | 24 | ||||
-rw-r--r-- | sql/sql_string.cc | 3 | ||||
-rw-r--r-- | sql/sql_table.cc | 16 | ||||
-rw-r--r-- | sql/sql_union.cc | 16 | ||||
-rw-r--r-- | sql/sql_update.cc | 51 | ||||
-rw-r--r-- | sql/table.cc | 2 | ||||
-rw-r--r-- | sql/unireg.cc | 3 |
28 files changed, 881 insertions, 240 deletions
diff --git a/sql/field.cc b/sql/field.cc index d11b509075b..98b3b91fcbd 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -5307,7 +5307,7 @@ bool Field_time::get_time(MYSQL_TIME *ltime) ltime->neg= 1; tmp=-tmp; } - ltime->day= 0; + ltime->year= ltime->month= ltime->day= 0; ltime->hour= (int) (tmp/10000); tmp-=ltime->hour*10000; ltime->minute= (int) tmp/100; diff --git a/sql/handler.h b/sql/handler.h index d43fc4725dd..5c7cfa4d58b 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -876,9 +876,9 @@ typedef struct { ulonglong delete_length; ha_rows records; ulong mean_rec_length; - time_t create_time; - time_t check_time; - time_t update_time; + ulong create_time; + ulong check_time; + ulong update_time; ulonglong check_sum; } PARTITION_INFO; @@ -1037,9 +1037,9 @@ public: ha_rows records; ha_rows deleted; /* Deleted records */ ulong mean_rec_length; /* physical reclength */ - time_t create_time; /* When table was created */ - time_t check_time; - time_t update_time; + ulong create_time; /* When table was created */ + ulong check_time; + ulong update_time; uint block_size; /* index block size */ ha_statistics(): diff --git a/sql/item.cc b/sql/item.cc index d1418b9a137..768af47d660 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2472,8 +2472,9 @@ longlong_from_string_with_check (CHARSET_INFO *cs, const char *cptr, char *end) TODO: Give error if we wanted a signed integer and we got an unsigned one */ - if (err > 0 || - (end != org_end && !check_if_only_end_space(cs, end, org_end))) + if (!current_thd->no_errors && + (err > 0 || + (end != org_end && !check_if_only_end_space(cs, end, org_end)))) { push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE, @@ -3268,9 +3269,57 @@ Item_param::set_param_type_and_swap_value(Item_param *src) } /**************************************************************************** + Item_copy +****************************************************************************/ +Item_copy *Item_copy::create (Item *item) +{ + switch (item->result_type()) + { + case STRING_RESULT: + return new Item_copy_string (item); + case REAL_RESULT: + return new Item_copy_float (item); + case INT_RESULT: + return item->unsigned_flag ? + new Item_copy_uint (item) : new Item_copy_int (item); + case DECIMAL_RESULT: + return new Item_copy_decimal (item); + + case ROW_RESULT: + DBUG_ASSERT (0); + } + /* should not happen */ + return NULL; +} + +/**************************************************************************** Item_copy_string ****************************************************************************/ +double Item_copy_string::val_real() +{ + int err_not_used; + char *end_not_used; + return (null_value ? 0.0 : + my_strntod(str_value.charset(), (char*) str_value.ptr(), + str_value.length(), &end_not_used, &err_not_used)); +} + +longlong Item_copy_string::val_int() +{ + int err; + return null_value ? LL(0) : my_strntoll(str_value.charset(),str_value.ptr(), + str_value.length(),10, (char**) 0, + &err); +} + + +int Item_copy_string::save_in_field(Field *field, bool no_conversions) +{ + return save_str_value_in_field(field, &str_value); +} + + void Item_copy_string::copy() { String *res=item->val_str(&str_value); @@ -3293,12 +3342,163 @@ my_decimal *Item_copy_string::val_decimal(my_decimal *decimal_value) { // Item_copy_string is used without fix_fields call if (null_value) - return 0; + return (my_decimal *) 0; string2my_decimal(E_DEC_FATAL_ERROR, &str_value, decimal_value); return (decimal_value); } +/**************************************************************************** + Item_copy_int +****************************************************************************/ + +void Item_copy_int::copy() +{ + cached_value= item->val_int(); + null_value=item->null_value; +} + +static int save_int_value_in_field (Field *field, longlong nr, + bool null_value, bool unsigned_flag); + +int Item_copy_int::save_in_field(Field *field, bool no_conversions) +{ + return save_int_value_in_field(field, cached_value, + null_value, unsigned_flag); +} + + +String *Item_copy_int::val_str(String *str) +{ + if (null_value) + return (String *) 0; + + str->set(cached_value, &my_charset_bin); + return str; +} + + +my_decimal *Item_copy_int::val_decimal(my_decimal *decimal_value) +{ + if (null_value) + return (my_decimal *) 0; + + int2my_decimal(E_DEC_FATAL_ERROR, cached_value, unsigned_flag, decimal_value); + return decimal_value; +} + + +/**************************************************************************** + Item_copy_uint +****************************************************************************/ + +String *Item_copy_uint::val_str(String *str) +{ + if (null_value) + return (String *) 0; + + str->set((ulonglong) cached_value, &my_charset_bin); + return str; +} + + +/**************************************************************************** + Item_copy_float +****************************************************************************/ + +String *Item_copy_float::val_str(String *str) +{ + if (null_value) + return (String *) 0; + else + { + double nr= val_real(); + str->set_real(nr,decimals, &my_charset_bin); + return str; + } +} + + +my_decimal *Item_copy_float::val_decimal(my_decimal *decimal_value) +{ + if (null_value) + return (my_decimal *) 0; + else + { + double nr= val_real(); + double2my_decimal(E_DEC_FATAL_ERROR, nr, decimal_value); + return decimal_value; + } +} + + +int Item_copy_float::save_in_field(Field *field, bool no_conversions) +{ + if (null_value) + return set_field_to_null(field); + field->set_notnull(); + return field->store(cached_value); +} + + +/**************************************************************************** + Item_copy_decimal +****************************************************************************/ + +int Item_copy_decimal::save_in_field(Field *field, bool no_conversions) +{ + if (null_value) + return set_field_to_null(field); + field->set_notnull(); + return field->store_decimal(&cached_value); +} + + +String *Item_copy_decimal::val_str(String *result) +{ + if (null_value) + return (String *) 0; + result->set_charset(&my_charset_bin); + my_decimal2string(E_DEC_FATAL_ERROR, &cached_value, 0, 0, 0, result); + return result; +} + + +double Item_copy_decimal::val_real() +{ + if (null_value) + return 0.0; + else + { + double result; + my_decimal2double(E_DEC_FATAL_ERROR, &cached_value, &result); + return result; + } +} + + +longlong Item_copy_decimal::val_int() +{ + if (null_value) + return LL(0); + else + { + longlong result; + my_decimal2int(E_DEC_FATAL_ERROR, &cached_value, unsigned_flag, &result); + return result; + } +} + + +void Item_copy_decimal::copy() +{ + my_decimal *nr= item->val_decimal(&cached_value); + if (nr && nr != &cached_value) + memcpy (&cached_value, nr, sizeof (my_decimal)); + null_value= item->null_value; +} + + /* Functions to convert item to field (for send_fields) */ @@ -3390,14 +3590,12 @@ static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, current->mark_as_dependent(last); if (thd->lex->describe & DESCRIBE_EXTENDED) { - char warn_buff[MYSQL_ERRMSG_SIZE]; - sprintf(warn_buff, ER(ER_WARN_FIELD_RESOLVED), - db_name, (db_name[0] ? "." : ""), - table_name, (table_name [0] ? "." : ""), - resolved_item->field_name, - current->select_number, last->select_number); - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_WARN_FIELD_RESOLVED, warn_buff); + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_WARN_FIELD_RESOLVED, ER(ER_WARN_FIELD_RESOLVED), + db_name, (db_name[0] ? "." : ""), + table_name, (table_name [0] ? "." : ""), + resolved_item->field_name, + current->select_number, last->select_number); } } @@ -4875,7 +5073,10 @@ int Item_null::save_safe_in_field(Field *field) /* This implementation can lose str_value content, so if the Item uses str_value to store something, it should - reimplement it's ::save_in_field() as Item_string, for example, does + reimplement it's ::save_in_field() as Item_string, for example, does. + + Note: all Item_XXX::val_str(str) methods must NOT rely on the fact that + str != str_value. For example, see fix for bug #44743. */ int Item::save_in_field(Field *field, bool no_conversions) @@ -4945,10 +5146,9 @@ int Item_uint::save_in_field(Field *field, bool no_conversions) return Item_int::save_in_field(field, no_conversions); } - -int Item_int::save_in_field(Field *field, bool no_conversions) +static int save_int_value_in_field (Field *field, longlong nr, + bool null_value, bool unsigned_flag) { - longlong nr=val_int(); if (null_value) return set_field_to_null(field); field->set_notnull(); @@ -4956,6 +5156,12 @@ int Item_int::save_in_field(Field *field, bool no_conversions) } +int Item_int::save_in_field(Field *field, bool no_conversions) +{ + return save_int_value_in_field (field, val_int(), null_value, unsigned_flag); +} + + int Item_decimal::save_in_field(Field *field, bool no_conversions) { field->set_notnull(); @@ -5812,7 +6018,8 @@ void Item_ref::print(String *str, enum_query_type query_type) !table_name && name && alias_name_used) { THD *thd= current_thd; - append_identifier(thd, str, name, (uint) strlen(name)); + append_identifier(thd, str, (*ref)->real_item()->name, + (*ref)->real_item()->name_length); } else (*ref)->print(str, query_type); diff --git a/sql/item.h b/sql/item.h index 96a4e9f7a31..3dfcd7c2612 100644 --- a/sql/item.h +++ b/sql/item.h @@ -2443,48 +2443,203 @@ public: #include "item_xmlfunc.h" #endif -class Item_copy_string :public Item +/** + Base class to implement typed value caching Item classes + + Item_copy_ classes are very similar to the corresponding Item_ + classes (e.g. Item_copy_int is similar to Item_int) but they add + the following additional functionality to Item_ : + 1. Nullability + 2. Possibility to store the value not only on instantiation time, + but also later. + Item_copy_ classes are a functionality subset of Item_cache_ + classes, as e.g. they don't support comparisons with the original Item + as Item_cache_ classes do. + Item_copy_ classes are used in GROUP BY calculation. + TODO: Item_copy should be made an abstract interface and Item_copy_ + classes should inherit both the respective Item_ class and the interface. + Ideally we should drop Item_copy_ classes altogether and merge + their functionality to Item_cache_ (and these should be made to inherit + from Item_). +*/ + +class Item_copy :public Item { +protected: + + /** + Stores the type of the resulting field that would be used to store the data + in the cache. This is to avoid calls to the original item. + */ enum enum_field_types cached_field_type; -public: + + /** The original item that is copied */ Item *item; - Item_copy_string(Item *i) :item(i) + + /** + Stores the result type of the original item, so it can be returned + without calling the original item's method + */ + Item_result cached_result_type; + + /** + Constructor of the Item_copy class + + stores metadata information about the original class as well as a + pointer to it. + */ + Item_copy(Item *i) { + item= i; null_value=maybe_null=item->maybe_null; decimals=item->decimals; max_length=item->max_length; name=item->name; cached_field_type= item->field_type(); + cached_result_type= item->result_type(); + unsigned_flag= item->unsigned_flag; } + +public: + /** + Factory method to create the appropriate subclass dependent on the type of + the original item. + + @param item the original item. + */ + static Item_copy *create (Item *item); + + /** + Update the cache with the value of the original item + + This is the method that updates the cached value. + It must be explicitly called by the user of this class to store the value + of the orginal item in the cache. + */ + virtual void copy() = 0; + + Item *get_item() { return item; } + /** All of the subclasses should have the same type tag */ enum Type type() const { return COPY_STR_ITEM; } - enum Item_result result_type () const { return STRING_RESULT; } enum_field_types field_type() const { return cached_field_type; } - double val_real() + enum Item_result result_type () const { return cached_result_type; } + + void make_field(Send_field *field) { item->make_field(field); } + table_map used_tables() const { return (table_map) 1L; } + bool const_item() const { return 0; } + bool is_null() { return null_value; } + + /* + Override the methods below as pure virtual to make sure all the + sub-classes implement them. + */ + + virtual String *val_str(String*) = 0; + virtual my_decimal *val_decimal(my_decimal *) = 0; + virtual double val_real() = 0; + virtual longlong val_int() = 0; + virtual int save_in_field(Field *field, bool no_conversions) = 0; +}; + +/** + Implementation of a string cache. + + Uses Item::str_value for storage +*/ +class Item_copy_string : public Item_copy +{ +public: + Item_copy_string (Item *item) : Item_copy(item) {} + + String *val_str(String*); + my_decimal *val_decimal(my_decimal *); + double val_real(); + longlong val_int(); + void copy(); + int save_in_field(Field *field, bool no_conversions); +}; + + +class Item_copy_int : public Item_copy +{ +protected: + longlong cached_value; +public: + Item_copy_int (Item *i) : Item_copy(i) {} + int save_in_field(Field *field, bool no_conversions); + + virtual String *val_str(String*); + virtual my_decimal *val_decimal(my_decimal *); + virtual double val_real() { - int err_not_used; - char *end_not_used; - return (null_value ? 0.0 : - my_strntod(str_value.charset(), (char*) str_value.ptr(), - str_value.length(), &end_not_used, &err_not_used)); + return null_value ? 0.0 : (double) cached_value; } - longlong val_int() + virtual longlong val_int() + { + return null_value ? LL(0) : cached_value; + } + virtual void copy(); +}; + + +class Item_copy_uint : public Item_copy_int +{ +public: + Item_copy_uint (Item *item) : Item_copy_int(item) + { + unsigned_flag= 1; + } + + String *val_str(String*); + double val_real() { - int err; - return null_value ? LL(0) : my_strntoll(str_value.charset(),str_value.ptr(), - str_value.length(),10, (char**) 0, - &err); + return null_value ? 0.0 : (double) (ulonglong) cached_value; } +}; + + +class Item_copy_float : public Item_copy +{ +protected: + double cached_value; +public: + Item_copy_float (Item *i) : Item_copy(i) {} + int save_in_field(Field *field, bool no_conversions); + String *val_str(String*); my_decimal *val_decimal(my_decimal *); - void make_field(Send_field *field) { item->make_field(field); } - void copy(); - int save_in_field(Field *field, bool no_conversions) + double val_real() { - return save_str_value_in_field(field, &str_value); + return null_value ? 0.0 : cached_value; } - table_map used_tables() const { return (table_map) 1L; } - bool const_item() const { return 0; } - bool is_null() { return null_value; } + longlong val_int() + { + return (longlong) rint(val_real()); + } + void copy() + { + cached_value= item->val_real(); + null_value= item->null_value; + } +}; + + +class Item_copy_decimal : public Item_copy +{ +protected: + my_decimal cached_value; +public: + Item_copy_decimal (Item *i) : Item_copy(i) {} + int save_in_field(Field *field, bool no_conversions); + + String *val_str(String*); + my_decimal *val_decimal(my_decimal *) + { + return null_value ? NULL: &cached_value; + } + double val_real(); + longlong val_int(); + void copy(); }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index a9bfea1b806..ee823517df8 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2724,16 +2724,6 @@ void Item_func_case::fix_length_and_dec() nagg++; if (!(found_types= collect_cmp_types(agg, nagg))) return; - if (with_sum_func || current_thd->lex->current_select->group_list.elements) - { - /* - See TODO commentary in the setup_copy_fields function: - item in a group may be wrapped with an Item_copy_string item. - That item has a STRING_RESULT result type, so we need - to take this type into account. - */ - found_types |= (1 << item_cmp_type(left_result_type, STRING_RESULT)); - } for (i= 0; i <= (uint)DECIMAL_RESULT; i++) { diff --git a/sql/item_func.cc b/sql/item_func.cc index 876aee719a3..0c8f236a27c 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2143,9 +2143,6 @@ bool Item_func_rand::fix_fields(THD *thd,Item **ref) if (!rand && !(rand= (struct rand_struct*) thd->stmt_arena->alloc(sizeof(*rand)))) return TRUE; - - if (args[0]->const_item()) - seed_random (args[0]); } else { @@ -2175,8 +2172,21 @@ void Item_func_rand::update_used_tables() double Item_func_rand::val_real() { DBUG_ASSERT(fixed == 1); - if (arg_count && !args[0]->const_item()) - seed_random (args[0]); + if (arg_count) + { + if (!args[0]->const_item()) + seed_random(args[0]); + else if (first_eval) + { + /* + Constantness of args[0] may be set during JOIN::optimize(), if arg[0] + is a field item of "constant" table. Thus, we have to evaluate + seed_random() for constant arg there but not at the fix_fields method. + */ + first_eval= FALSE; + seed_random(args[0]); + } + } return my_rnd(rand); } @@ -4178,6 +4188,41 @@ Item_func_set_user_var::check(bool use_result_field) /** + @brief Evaluate and store item's result. + This function is invoked on "SELECT ... INTO @var ...". + + @param item An item to get value from. +*/ + +void Item_func_set_user_var::save_item_result(Item *item) +{ + DBUG_ENTER("Item_func_set_user_var::save_item_result"); + + switch (cached_result_type) { + case REAL_RESULT: + save_result.vreal= item->val_result(); + break; + case INT_RESULT: + save_result.vint= item->val_int_result(); + unsigned_flag= item->unsigned_flag; + break; + case STRING_RESULT: + save_result.vstr= item->str_result(&value); + break; + case DECIMAL_RESULT: + save_result.vdec= item->val_decimal_result(&decimal_buff); + break; + case ROW_RESULT: + default: + // Should never happen + DBUG_ASSERT(0); + break; + } + DBUG_VOID_RETURN; +} + + +/** This functions is invoked on SET \@variable or \@variable:= expression. @@ -4835,10 +4880,20 @@ bool Item_func_get_system_var::is_written_to_binlog() } +void Item_func_get_system_var::update_null_value() +{ + THD *thd= current_thd; + int save_no_errors= thd->no_errors; + thd->no_errors= TRUE; + Item::update_null_value(); + thd->no_errors= save_no_errors; +} + + void Item_func_get_system_var::fix_length_and_dec() { char *cptr; - maybe_null=0; + maybe_null= TRUE; max_length= 0; if (var->check_type(var_type)) diff --git a/sql/item_func.h b/sql/item_func.h index d23d821baf6..67049af81a2 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -696,14 +696,16 @@ public: class Item_func_rand :public Item_real_func { struct rand_struct *rand; + bool first_eval; // TRUE if val_real() is called 1st time public: - Item_func_rand(Item *a) :Item_real_func(a), rand(0) {} + Item_func_rand(Item *a) :Item_real_func(a), rand(0), first_eval(TRUE) {} Item_func_rand() :Item_real_func() {} double val_real(); const char *func_name() const { return "rand"; } bool const_item() const { return 0; } void update_used_tables(); bool fix_fields(THD *thd, Item **ref); + void cleanup() { first_eval= TRUE; Item_real_func::cleanup(); } private: void seed_random (Item * val); }; @@ -1341,6 +1343,7 @@ public: bool send(Protocol *protocol, String *str_arg); void make_field(Send_field *tmp_field); bool check(bool use_result_field); + void save_item_result(Item *item); bool update(); enum Item_result result_type () const { return cached_result_type; } bool fix_fields(THD *thd, Item **ref); @@ -1452,6 +1455,7 @@ public: LEX_STRING *component_arg, const char *name_arg, size_t name_len_arg); enum Functype functype() const { return GSYSVAR_FUNC; } + void update_null_value(); void fix_length_and_dec(); void print(String *str, enum_query_type query_type); bool const_item() const { return true; } diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 267036e4a3d..f9210c842bb 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2706,8 +2706,15 @@ String *Item_func_conv_charset::val_str(String *str) DBUG_ASSERT(fixed == 1); if (use_cached_value) return null_value ? 0 : &str_value; - String *arg= args[0]->val_str(str); + /* + Here we don't pass 'str' as a parameter to args[0]->val_str() + as 'str' may points to 'str_value' (e.g. see Item::save_in_field()), + which we use below to convert string. + Use argument's 'str_value' instead. + */ + String *arg= args[0]->val_str(&args[0]->str_value);; uint dummy_errors; + arg= args[0]->val_str(&args[0]->str_value); if (!arg) { null_value=1; @@ -2943,7 +2950,7 @@ String *Item_load_file::val_str(String *str) ) goto err; - (void) fn_format(path, file_name->c_ptr(), mysql_real_data_home, "", + (void) fn_format(path, file_name->c_ptr_safe(), mysql_real_data_home, "", MY_RELATIVE_PATH | MY_UNPACK_FILENAME); /* Read only allowed from within dir specified by secure_file_priv */ @@ -2969,7 +2976,7 @@ String *Item_load_file::val_str(String *str) } if (tmp_value.alloc(stat_info.st_size)) goto err; - if ((file = my_open(file_name->c_ptr(), O_RDONLY, MYF(0))) < 0) + if ((file = my_open(file_name->ptr(), O_RDONLY, MYF(0))) < 0) goto err; if (my_read(file, (uchar*) tmp_value.ptr(), stat_info.st_size, MYF(MY_NABP))) { @@ -3219,7 +3226,21 @@ longlong Item_func_uncompressed_length::val_int() if (res->is_empty()) return 0; /* - res->ptr() using is safe because we have tested that string is not empty, + If length is <= 4 bytes, data is corrupt. This is the best we can do + to detect garbage input without decompressing it. + */ + if (res->length() <= 4) + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + ER_ZLIB_Z_DATA_ERROR, + ER(ER_ZLIB_Z_DATA_ERROR)); + null_value= 1; + return 0; + } + + /* + res->ptr() using is safe because we have tested that string is at least + 5 bytes long. res->c_ptr() is not used because: - we do not need \0 terminated string to get first 4 bytes - c_ptr() tests simbol after string end (uninitialiozed memory) which diff --git a/sql/log_event.cc b/sql/log_event.cc index 1a8cb8ee4fa..651cd1418e3 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -7862,10 +7862,11 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid, /* Now set the size of the data to the size of the field metadata array - plus one or two bytes for number of elements in the field metadata array. + plus one or three bytes (see pack.c:net_store_length) for number of + elements in the field metadata array. */ if (m_field_metadata_size > 255) - m_data_size+= m_field_metadata_size + 2; + m_data_size+= m_field_metadata_size + 3; else m_data_size+= m_field_metadata_size + 1; @@ -9312,7 +9313,7 @@ Incident_log_event::print(FILE *file, Write_on_release_cache cache(&print_event_info->head_cache, file); print_header(&cache, print_event_info, FALSE); - my_b_printf(&cache, "\n# Incident: %s", description()); + my_b_printf(&cache, "\n# Incident: %s\nRELOAD DATABASE; # Shall generate syntax error\n", description()); } #endif diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index e93417374fe..45c0efb10c2 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -104,9 +104,16 @@ int init_relay_log_info(Relay_log_info* rli, rli->tables_to_lock= 0; rli->tables_to_lock_count= 0; - fn_format(rli->slave_patternload_file, PREFIX_SQL_LOAD, slave_load_tmpdir, "", - MY_PACK_FILENAME | MY_UNPACK_FILENAME | - MY_RETURN_REAL_PATH); + char pattern[FN_REFLEN]; + if (fn_format(pattern, PREFIX_SQL_LOAD, slave_load_tmpdir, "", + MY_SAFE_PATH | MY_RETURN_REAL_PATH) == NullS) + { + pthread_mutex_unlock(&rli->data_lock); + sql_print_error("Unable to use slave's temporary directory %s", + slave_load_tmpdir); + DBUG_RETURN(1); + } + unpack_filename(rli->slave_patternload_file, pattern); rli->slave_patternload_file_size= strlen(rli->slave_patternload_file); /* diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index f9b66990e93..2dfc5310643 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -6076,7 +6076,7 @@ ER_SLAVE_INCIDENT ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT eng "Table has no partition for some existing values" ER_BINLOG_UNSAFE_STATEMENT - eng "Statement is not safe to log in statement format." + eng "Statement may not be safe to log in statement format." swe "Detta är inte säkert att logga i statement-format." ER_SLAVE_FATAL_ERROR eng "Fatal error: %s" diff --git a/sql/slave.cc b/sql/slave.cc index 81c18c5e04b..c379a67201a 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -389,6 +389,13 @@ void init_slave_skip_errors(const char* arg) DBUG_VOID_RETURN; } +static void set_thd_in_use_temporary_tables(Relay_log_info *rli) +{ + TABLE *table; + + for (table= rli->save_temporary_tables ; table ; table= table->next) + table->in_use= rli->sql_thd; +} int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock) { @@ -2668,14 +2675,21 @@ err: LOAD DATA INFILE. */ static -int check_temp_dir(char* tmp_dir, char *tmp_file) +int check_temp_dir(char* tmp_file) { int fd; MY_DIR *dirp; + char tmp_dir[FN_REFLEN]; + size_t tmp_dir_size; DBUG_ENTER("check_temp_dir"); /* + Get the directory from the temporary file. + */ + dirname_part(tmp_dir, tmp_file, &tmp_dir_size); + + /* Check if the directory exists. */ if (!(dirp=my_dir(tmp_dir,MYF(MY_WME)))) @@ -2750,6 +2764,7 @@ pthread_handler_t handle_slave_sql(void *arg) } thd->init_for_queries(); thd->temporary_tables = rli->save_temporary_tables; // restore temp tables + set_thd_in_use_temporary_tables(rli); // (re)set sql_thd in use for saved temp tables pthread_mutex_lock(&LOCK_thread_count); threads.append(thd); pthread_mutex_unlock(&LOCK_thread_count); @@ -2830,7 +2845,7 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME, llstr(rli->group_master_log_pos,llbuff),rli->group_relay_log_name, llstr(rli->group_relay_log_pos,llbuff1)); - if (check_temp_dir(slave_load_tmpdir, rli->slave_patternload_file)) + if (check_temp_dir(rli->slave_patternload_file)) { rli->report(ERROR_LEVEL, thd->main_da.sql_errno(), "Unable to use slave's temporary directory %s - %s", @@ -2996,6 +3011,7 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ DBUG_ASSERT(rli->sql_thd == thd); THD_CHECK_SENTRY(thd); rli->sql_thd= 0; + set_thd_in_use_temporary_tables(rli); // (re)set sql_thd in use for saved temp tables pthread_mutex_lock(&LOCK_thread_count); THD_CHECK_SENTRY(thd); delete thd; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index b1dbb7031ce..4d4e4d24684 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -936,6 +936,7 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, #ifdef HAVE_OPENSSL Vio *vio=thd->net.vio; SSL *ssl= (SSL*) vio->ssl_arg; + X509 *cert; #endif /* @@ -964,8 +965,11 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, */ if (vio_type(vio) == VIO_TYPE_SSL && SSL_get_verify_result(ssl) == X509_V_OK && - SSL_get_peer_certificate(ssl)) + (cert= SSL_get_peer_certificate(ssl))) + { user_access= acl_user->access; + X509_free(cert); + } break; case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */ /* @@ -974,7 +978,6 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, If cipher name is specified, we compare it to actual cipher in use. */ - X509 *cert; if (vio_type(vio) != VIO_TYPE_SSL || SSL_get_verify_result(ssl) != X509_V_OK) break; @@ -1014,6 +1017,7 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, sql_print_information("X509 issuer mismatch: should be '%s' " "but is '%s'", acl_user->x509_issuer, ptr); free(ptr); + X509_free(cert); user_access=NO_ACCESS; break; } @@ -1033,12 +1037,15 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, sql_print_information("X509 subject mismatch: should be '%s' but is '%s'", acl_user->x509_subject, ptr); free(ptr); + X509_free(cert); user_access=NO_ACCESS; break; } user_access= acl_user->access; free(ptr); } + /* Deallocate the X509 certificate. */ + X509_free(cert); break; #else /* HAVE_OPENSSL */ default: diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 0dc29f7e3c2..d4d813d0fbd 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -5585,6 +5585,13 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table) other_bitmap= table->read_set; } + /* + The test-and-set mechanism in the bitmap is not reliable during + multi-UPDATE statements under MARK_COLUMNS_READ mode + (thd->mark_used_columns == MARK_COLUMNS_READ), as this bitmap contains + only those columns that are used in the SET clause. I.e they are being + set here. See multi_update::prepare() + */ if (bitmap_fast_test_and_set(current_bitmap, field->field_index)) { if (thd->mark_used_columns == MARK_COLUMNS_WRITE) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index b73822f5a48..cf5fdcf27a7 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2599,7 +2599,7 @@ bool select_dumpvar::send_data(List<Item> &items) { Item_func_set_user_var *suv= new Item_func_set_user_var(mv->s, item); suv->fix_fields(thd, 0); - suv->check(0); + suv->save_item_result(item); suv->update(); } } diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 1b42e522491..7b967206305 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -582,6 +582,11 @@ int mysql_multi_delete_prepare(THD *thd) } } } + /* + Reset the exclude flag to false so it doesn't interfare + with further calls to unique_table + */ + lex->select_lex.exclude_from_table_unique_test= FALSE; DBUG_RETURN(FALSE); } @@ -617,11 +622,24 @@ multi_delete::initialize_tables(JOIN *join) DBUG_RETURN(1); table_map tables_to_delete_from=0; + delete_while_scanning= 1; for (walk= delete_tables; walk; walk= walk->next_local) + { tables_to_delete_from|= walk->table->map; + if (delete_while_scanning && + unique_table(thd, walk, join->tables_list, false)) + { + /* + If the table we are going to delete from appears + in join, we need to defer delete. So the delete + doesn't interfers with the scaning of results. + */ + delete_while_scanning= 0; + } + } + walk= delete_tables; - delete_while_scanning= 1; for (JOIN_TAB *tab=join->join_tab, *end=join->join_tab+join->tables; tab < end; tab++) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 6dbe4a4fd8d..8f1d3842245 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -195,11 +195,8 @@ bool begin_trans(THD *thd) error= -1; else { - LEX *lex= thd->lex; thd->options|= OPTION_BEGIN; thd->server_status|= SERVER_STATUS_IN_TRANS; - if (lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT) - error= ha_start_consistent_snapshot(thd); } return error; } @@ -4027,6 +4024,11 @@ end_with_restore_list: } if (begin_trans(thd)) goto error; + if (lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT) + { + if (ha_start_consistent_snapshot(thd)) + goto error; + } my_ok(thd); break; case SQLCOM_COMMIT: diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 1465b6d2d30..da168d36429 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -29,6 +29,18 @@ extern struct st_mysql_plugin *mysqld_builtins[]; +/** + @note The order of the enumeration is critical. + @see construct_options +*/ +static const char *global_plugin_typelib_names[]= + { "OFF", "ON", "FORCE", NULL }; +enum enum_plugin_load_policy {PLUGIN_OFF, PLUGIN_ON, PLUGIN_FORCE}; +static TYPELIB global_plugin_typelib= + { array_elements(global_plugin_typelib_names)-1, + "", global_plugin_typelib_names, NULL }; + + char *opt_plugin_load= NULL; char *opt_plugin_dir_ptr; char opt_plugin_dir[FN_REFLEN]; @@ -192,7 +204,7 @@ static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv); static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv, const char *list); static int test_plugin_options(MEM_ROOT *, struct st_plugin_int *, - int *, char **, my_bool); + int *, char **); static bool register_builtin(struct st_mysql_plugin *, struct st_plugin_int *, struct st_plugin_int **); static void unlock_variables(THD *thd, struct system_variables *vars); @@ -751,7 +763,7 @@ static bool plugin_add(MEM_ROOT *tmp_root, tmp.name.length= name_len; tmp.ref_count= 0; tmp.state= PLUGIN_IS_UNINITIALIZED; - if (test_plugin_options(tmp_root, &tmp, argc, argv, true)) + if (test_plugin_options(tmp_root, &tmp, argc, argv)) tmp.state= PLUGIN_IS_DISABLED; if ((tmp_plugin_ptr= plugin_insert_or_reuse(&tmp))) @@ -997,7 +1009,6 @@ static int plugin_initialize(struct st_plugin_int *plugin) DBUG_ENTER("plugin_initialize"); safe_mutex_assert_owner(&LOCK_plugin); - if (plugin_type_initialize[plugin->plugin->type]) { if ((*plugin_type_initialize[plugin->plugin->type])(plugin)) @@ -1083,6 +1094,20 @@ uchar *get_bookmark_hash_key(const uchar *buff, size_t *length, return (uchar*) var->key; } +static inline void convert_dash_to_underscore(char *str, int len) +{ + for (char *p= str; p <= str+len; p++) + if (*p == '-') + *p= '_'; +} + +static inline void convert_underscore_to_dash(char *str, int len) +{ + for (char *p= str; p <= str+len; p++) + if (*p == '_') + *p= '-'; +} + /* The logic is that we first load and initialize all compiled in plugins. @@ -1094,11 +1119,12 @@ uchar *get_bookmark_hash_key(const uchar *buff, size_t *length, int plugin_init(int *argc, char **argv, int flags) { uint i; - bool def_enabled, is_myisam; + bool is_myisam; struct st_mysql_plugin **builtins; struct st_mysql_plugin *plugin; struct st_plugin_int tmp, *plugin_ptr, **reap; MEM_ROOT tmp_root; + bool reaped_mandatory_plugin= FALSE; DBUG_ENTER("plugin_init"); if (initialized) @@ -1142,17 +1168,13 @@ int plugin_init(int *argc, char **argv, int flags) !my_strnncoll(&my_charset_latin1, (const uchar*) plugin->name, 6, (const uchar*) "InnoDB", 6)) continue; - /* by default, ndbcluster and federated are disabled */ - def_enabled= - my_strcasecmp(&my_charset_latin1, plugin->name, "NDBCLUSTER") != 0 && - my_strcasecmp(&my_charset_latin1, plugin->name, "FEDERATED") != 0; bzero(&tmp, sizeof(tmp)); tmp.plugin= plugin; tmp.name.str= (char *)plugin->name; tmp.name.length= strlen(plugin->name); tmp.state= 0; free_root(&tmp_root, MYF(MY_MARK_BLOCKS_FREE)); - if (test_plugin_options(&tmp_root, &tmp, argc, argv, def_enabled)) + if (test_plugin_options(&tmp_root, &tmp, argc, argv)) tmp.state= PLUGIN_IS_DISABLED; else tmp.state= PLUGIN_IS_UNINITIALIZED; @@ -1227,6 +1249,8 @@ int plugin_init(int *argc, char **argv, int flags) while ((plugin_ptr= *(--reap))) { pthread_mutex_unlock(&LOCK_plugin); + if (plugin_ptr->is_mandatory) + reaped_mandatory_plugin= TRUE; plugin_deinitialize(plugin_ptr, true); pthread_mutex_lock(&LOCK_plugin); plugin_del(plugin_ptr); @@ -1234,6 +1258,8 @@ int plugin_init(int *argc, char **argv, int flags) pthread_mutex_unlock(&LOCK_plugin); my_afree(reap); + if (reaped_mandatory_plugin) + goto err; end: free_root(&tmp_root, MYF(0)); @@ -1299,7 +1325,7 @@ bool plugin_register_builtin(THD *thd, struct st_mysql_plugin *plugin) pthread_mutex_lock(&LOCK_plugin); rw_wrlock(&LOCK_system_variables_hash); - if (test_plugin_options(thd->mem_root, &tmp, &dummy_argc, NULL, true)) + if (test_plugin_options(thd->mem_root, &tmp, &dummy_argc, NULL)) goto end; tmp.state= PLUGIN_IS_UNINITIALIZED; if ((result= register_builtin(plugin, &tmp, &ptr))) @@ -2889,59 +2915,78 @@ my_bool get_one_plugin_option(int optid __attribute__((unused)), } +/** + Creates a set of my_option objects associated with a specified plugin- + handle. + + @param mem_root Memory allocator to be used. + @param tmp A pointer to a plugin handle + @param[out] options A pointer to a pre-allocated static array + + The set is stored in the pre-allocated static array supplied to the function. + The size of the array is calculated as (number_of_plugin_varaibles*2+3). The + reason is that each option can have a prefix '--plugin-' in addtion to the + shorter form '--<plugin-name>'. There is also space allocated for + terminating NULL pointers. + + @return + @retval -1 An error occurred + @retval 0 Success +*/ + static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp, - my_option *options, my_bool **enabled, - bool can_disable) + my_option *options) { const char *plugin_name= tmp->plugin->name; - uint namelen= strlen(plugin_name), optnamelen; - uint buffer_length= namelen * 4 + (can_disable ? 75 : 10); - char *name= (char*) alloc_root(mem_root, buffer_length) + 1; - char *optname, *p; + const LEX_STRING plugin_dash = { C_STRING_WITH_LEN("plugin-") }; + uint plugin_name_len= strlen(plugin_name); + uint optnamelen; + const int max_comment_len= 180; + char *comment= (char *) alloc_root(mem_root, max_comment_len + 1); + char *optname; + int index= 0, offset= 0; st_mysql_sys_var *opt, **plugin_option; st_bookmark *v; + + /** Used to circumvent the const attribute on my_option::name */ + char *plugin_name_ptr, *plugin_name_with_prefix_ptr; + DBUG_ENTER("construct_options"); - DBUG_PRINT("plugin", ("plugin: '%s' enabled: %d can_disable: %d", - plugin_name, **enabled, can_disable)); + options[0].name= plugin_name_ptr= (char*) alloc_root(mem_root, + plugin_name_len + 1); + strcpy(plugin_name_ptr, plugin_name); + my_casedn_str(&my_charset_latin1, plugin_name_ptr); + convert_underscore_to_dash(plugin_name_ptr, plugin_name_len); /* support --skip-plugin-foo syntax */ - memcpy(name, plugin_name, namelen + 1); - my_casedn_str(&my_charset_latin1, name); - strxmov(name + namelen + 1, "plugin-", name, NullS); - /* Now we have namelen + 1 + 7 + namelen + 1 == namelen * 2 + 9. */ - - for (p= name + namelen*2 + 8; p > name; p--) - if (*p == '_') - *p= '-'; + options[1].name= plugin_name_with_prefix_ptr= (char*) alloc_root(mem_root, + plugin_name_len + + plugin_dash.length + 1); + strxmov(plugin_name_with_prefix_ptr, plugin_dash.str, options[0].name, NullS); - if (can_disable) - { - strxmov(name + namelen*2 + 10, "Enable ", plugin_name, " plugin. " - "Disable with --skip-", name," (will save memory).", NullS); - /* - Now we have namelen * 2 + 10 (one char unused) + 7 + namelen + 9 + - 20 + namelen + 20 + 1 == namelen * 4 + 67. - */ + options[0].id= options[1].id= 256; /* must be >255. dup id ok */ + options[0].var_type= options[1].var_type= GET_ENUM; + options[0].arg_type= options[1].arg_type= OPT_ARG; + options[0].def_value= options[1].def_value= 1; /* ON */ + options[0].typelib= options[1].typelib= &global_plugin_typelib; - options[0].comment= name + namelen*2 + 10; - } + strxnmov(comment, max_comment_len, "Enable or disable ", plugin_name, + " plugin. Possible values are ON, OFF, FORCE (don't start " + "if the plugin fails to load).", NullS); + options[0].comment= comment; /* - NOTE: 'name' is one char above the allocated buffer! - NOTE: This code assumes that 'my_bool' and 'char' are of same size. + Allocate temporary space for the value of the tristate. + This option will have a limited lifetime and is not used beyond + server initialization. + GET_ENUM value is an integer. */ - *((my_bool *)(name -1))= **enabled; - *enabled= (my_bool *)(name - 1); - + options[0].value= options[1].value= (uchar **)alloc_root(mem_root, + sizeof(int)); + *((uint*) options[0].value)= *((uint*) options[1].value)= + (uint) options[0].def_value; - options[1].name= (options[0].name= name) + namelen + 1; - options[0].id= options[1].id= 256; /* must be >255. dup id ok */ - options[0].var_type= options[1].var_type= GET_BOOL; - options[0].arg_type= options[1].arg_type= NO_ARG; - options[0].def_value= options[1].def_value= **enabled; - options[0].value= options[0].u_max_value= - options[1].value= options[1].u_max_value= (uchar**) (name - 1); options+= 2; /* @@ -2955,7 +3000,7 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp, opt= *plugin_option; if (!(opt->flags & PLUGIN_VAR_THDLOCAL)) continue; - if (!(register_var(name, opt->name, opt->flags))) + if (!(register_var(plugin_name_ptr, opt->name, opt->flags))) continue; switch (opt->flags & PLUGIN_VAR_TYPEMASK) { case PLUGIN_VAR_BOOL: @@ -3020,7 +3065,7 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp, if (!opt->update) { opt->update= update_func_str; - if (!(opt->flags & PLUGIN_VAR_MEMALLOC | PLUGIN_VAR_READONLY)) + if (!(opt->flags & (PLUGIN_VAR_MEMALLOC | PLUGIN_VAR_READONLY))) { opt->flags|= PLUGIN_VAR_READONLY; sql_print_warning("Server variable %s of plugin %s was forced " @@ -3062,14 +3107,14 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp, if (!(opt->flags & PLUGIN_VAR_THDLOCAL)) { optnamelen= strlen(opt->name); - optname= (char*) alloc_root(mem_root, namelen + optnamelen + 2); - strxmov(optname, name, "-", opt->name, NullS); - optnamelen= namelen + optnamelen + 1; + optname= (char*) alloc_root(mem_root, plugin_name_len + optnamelen + 2); + strxmov(optname, plugin_name_ptr, "-", opt->name, NullS); + optnamelen= plugin_name_len + optnamelen + 1; } else { /* this should not fail because register_var should create entry */ - if (!(v= find_bookmark(name, opt->name, opt->flags))) + if (!(v= find_bookmark(plugin_name_ptr, opt->name, opt->flags))) { sql_print_error("Thread local variable '%s' not allocated " "in plugin '%s'.", opt->name, plugin_name); @@ -3085,10 +3130,7 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp, (optnamelen= v->name_len) + 1); } - /* convert '_' to '-' */ - for (p= optname; *p; p++) - if (*p == '_') - *p= '-'; + convert_underscore_to_dash(optname, optnamelen); options->name= optname; options->comment= opt->comment; @@ -3103,10 +3145,13 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp, else options->value= options->u_max_value= *(uchar***) (opt + 1); + char *option_name_ptr; options[1]= options[0]; - options[1].name= p= (char*) alloc_root(mem_root, optnamelen + 8); - options[1].comment= 0; // hidden - strxmov(p, "plugin-", optname, NullS); + options[1].name= option_name_ptr= (char*) alloc_root(mem_root, + plugin_dash.length + + optnamelen + 1); + options[1].comment= 0; /* Hidden from the help text */ + strxmov(option_name_ptr, plugin_dash.str, optname, NullS); options+= 2; } @@ -3120,55 +3165,57 @@ static my_option *construct_help_options(MEM_ROOT *mem_root, { st_mysql_sys_var **opt; my_option *opts; - my_bool dummy, can_disable; - my_bool *dummy2= &dummy; uint count= EXTRA_OPTIONS; DBUG_ENTER("construct_help_options"); - for (opt= p->plugin->system_vars; opt && *opt; opt++, count+= 2); + for (opt= p->plugin->system_vars; opt && *opt; opt++, count+= 2) + ; if (!(opts= (my_option*) alloc_root(mem_root, sizeof(my_option) * count))) DBUG_RETURN(NULL); bzero(opts, sizeof(my_option) * count); - dummy= TRUE; /* plugin is enabled. */ - - can_disable= - my_strcasecmp(&my_charset_latin1, p->name.str, "MyISAM") && - my_strcasecmp(&my_charset_latin1, p->name.str, "MEMORY"); - - if (construct_options(mem_root, p, opts, &dummy2, can_disable)) + if (construct_options(mem_root, p, opts)) DBUG_RETURN(NULL); DBUG_RETURN(opts); } -/* - SYNOPSIS - test_plugin_options() - tmp_root temporary scratch space - plugin internal plugin structure - argc user supplied arguments - argv user supplied arguments - default_enabled default plugin enable status - RETURNS: - 0 SUCCESS - plugin should be enabled/loaded - NOTE: - Requires that a write-lock is held on LOCK_system_variables_hash +/** + Create and register system variables supplied from the plugin and + assigns initial values from corresponding command line arguments. + + @param tmp_root Temporary scratch space + @param[out] plugin Internal plugin structure + @param argc Number of command line arguments + @param argv Command line argument vector + + The plugin will be updated with a policy on how to handle errors during + initialization. + + @note Requires that a write-lock is held on LOCK_system_variables_hash + + @return How initialization of the plugin should be handled. + @retval 0 Initialization should proceed. + @retval 1 Plugin is disabled. + @retval -1 An error has occurred. */ + static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp, - int *argc, char **argv, my_bool default_enabled) + int *argc, char **argv) { struct sys_var_chain chain= { NULL, NULL }; - my_bool enabled_saved= default_enabled, can_disable; - my_bool *enabled= &default_enabled; + my_bool can_disable; + bool disable_plugin; + enum_plugin_load_policy plugin_load_policy= PLUGIN_ON; + MEM_ROOT *mem_root= alloc_root_inited(&tmp->mem_root) ? &tmp->mem_root : &plugin_mem_root; st_mysql_sys_var **opt; my_option *opts= NULL; - char *p, *varname; + char *varname; int error; st_mysql_sys_var *o; sys_var *v; @@ -3177,13 +3224,17 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp, DBUG_ENTER("test_plugin_options"); DBUG_ASSERT(tmp->plugin && tmp->name.str); + /* + The 'federated' and 'ndbcluster' storage engines are always disabled by + default. + */ + if (!(my_strcasecmp(&my_charset_latin1, tmp->name.str, "federated") && + my_strcasecmp(&my_charset_latin1, tmp->name.str, "ndbcluster"))) + plugin_load_policy= PLUGIN_OFF; + for (opt= tmp->plugin->system_vars; opt && *opt; opt++) count+= 2; /* --{plugin}-{optname} and --plugin-{plugin}-{optname} */ - can_disable= - my_strcasecmp(&my_charset_latin1, tmp->name.str, "MyISAM") && - my_strcasecmp(&my_charset_latin1, tmp->name.str, "MEMORY"); - if (count > EXTRA_OPTIONS || (*argc > 1)) { if (!(opts= (my_option*) alloc_root(tmp_root, sizeof(my_option) * count))) @@ -3193,12 +3244,18 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp, } bzero(opts, sizeof(my_option) * count); - if (construct_options(tmp_root, tmp, opts, &enabled, can_disable)) + if (construct_options(tmp_root, tmp, opts)) { sql_print_error("Bad options for plugin '%s'.", tmp->name.str); DBUG_RETURN(-1); } + /* + We adjust the default value to account for the hardcoded exceptions + we have set for the federated and ndbcluster storage engines. + */ + opts[0].def_value= opts[1].def_value= (int)plugin_load_policy; + error= handle_options(argc, &argv, opts, get_one_plugin_option); (*argc)++; /* add back one for the program name */ @@ -3208,64 +3265,79 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp, tmp->name.str); goto err; } + /* + Set plugin loading policy from option value. First element in the option + list is always the <plugin name> option value. + */ + plugin_load_policy= (enum_plugin_load_policy)*(uint*)opts[0].value; } - if (!*enabled && !can_disable) + disable_plugin= (plugin_load_policy == PLUGIN_OFF); + /* + The 'MyISAM' and 'Memory' storage engines currently can't be disabled. + */ + can_disable= + my_strcasecmp(&my_charset_latin1, tmp->name.str, "MyISAM") && + my_strcasecmp(&my_charset_latin1, tmp->name.str, "MEMORY"); + + tmp->is_mandatory= (plugin_load_policy == PLUGIN_FORCE) || !can_disable; + + if (disable_plugin && !can_disable) { sql_print_warning("Plugin '%s' cannot be disabled", tmp->name.str); - *enabled= TRUE; + disable_plugin= FALSE; } - error= 1; + /* + If the plugin is disabled it should not be initialized. + */ + if (disable_plugin) + { + if (global_system_variables.log_warnings) + sql_print_information("Plugin '%s' is disabled.", + tmp->name.str); + if (opts) + my_cleanup_options(opts); + DBUG_RETURN(1); + } - if (*enabled) + error= 1; + for (opt= tmp->plugin->system_vars; opt && *opt; opt++) { - for (opt= tmp->plugin->system_vars; opt && *opt; opt++) + if (((o= *opt)->flags & PLUGIN_VAR_NOSYSVAR)) + continue; + if ((var= find_bookmark(tmp->name.str, o->name, o->flags))) + v= new (mem_root) sys_var_pluginvar(var->key + 1, o); + else { - if (((o= *opt)->flags & PLUGIN_VAR_NOSYSVAR)) - continue; - - if ((var= find_bookmark(tmp->name.str, o->name, o->flags))) - v= new (mem_root) sys_var_pluginvar(var->key + 1, o); - else - { - len= tmp->name.length + strlen(o->name) + 2; - varname= (char*) alloc_root(mem_root, len); - strxmov(varname, tmp->name.str, "-", o->name, NullS); - my_casedn_str(&my_charset_latin1, varname); - - for (p= varname; *p; p++) - if (*p == '-') - *p= '_'; - - v= new (mem_root) sys_var_pluginvar(varname, o); - } - DBUG_ASSERT(v); /* check that an object was actually constructed */ - - /* - Add to the chain of variables. - Done like this for easier debugging so that the - pointer to v is not lost on optimized builds. - */ - v->chain_sys_var(&chain); + len= tmp->name.length + strlen(o->name) + 2; + varname= (char*) alloc_root(mem_root, len); + strxmov(varname, tmp->name.str, "-", o->name, NullS); + my_casedn_str(&my_charset_latin1, varname); + convert_dash_to_underscore(varname, len-1); + v= new (mem_root) sys_var_pluginvar(varname, o); } - if (chain.first) + DBUG_ASSERT(v); /* check that an object was actually constructed */ + /* + Add to the chain of variables. + Done like this for easier debugging so that the + pointer to v is not lost on optimized builds. + */ + v->chain_sys_var(&chain); + } /* end for */ + if (chain.first) + { + chain.last->next = NULL; + if (mysql_add_sys_var_chain(chain.first, NULL)) { - chain.last->next = NULL; - if (mysql_add_sys_var_chain(chain.first, NULL)) - { - sql_print_error("Plugin '%s' has conflicting system variables", - tmp->name.str); - goto err; - } - tmp->system_vars= chain.first; + sql_print_error("Plugin '%s' has conflicting system variables", + tmp->name.str); + goto err; } - DBUG_RETURN(0); + tmp->system_vars= chain.first; } - - if (enabled_saved && global_system_variables.log_warnings) - sql_print_information("Plugin '%s' disabled by command line option", - tmp->name.str); + DBUG_RETURN(0); + err: if (opts) my_cleanup_options(opts); diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h index 8ae38d58845..004d0d5abb7 100644 --- a/sql/sql_plugin.h +++ b/sql/sql_plugin.h @@ -79,6 +79,7 @@ struct st_plugin_int void *data; /* plugin type specific, e.g. handlerton */ MEM_ROOT mem_root; /* memory for dynamic plugin structures */ sys_var *system_vars; /* server variables for this plugin */ + bool is_mandatory; /* If true then plugin must not fail to load */ }; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index cd11b6d4c24..cc7ea75dbbf 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2461,6 +2461,9 @@ void mysql_stmt_execute(THD *thd, char *packet_arg, uint packet_length) stmt->execute_loop(&expanded_query, open_cursor, packet, packet_end); + /* Close connection socket; for use with client testing (Bug#43560). */ + DBUG_EXECUTE_IF("close_conn_after_stmt_execute", vio_close(thd->net.vio);); + DBUG_VOID_RETURN; } diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index d4331b12cd4..7cc9130cc4a 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -261,6 +261,8 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name, old_alias= ren_table->table_name; new_alias= new_table_name; } + DBUG_ASSERT(new_alias); + build_table_filename(name, sizeof(name), new_db, new_alias, reg_ext, 0); if (!access(name,F_OK)) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 21d22e250ec..fad533986d6 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2248,6 +2248,14 @@ JOIN::destroy() cond_equal= 0; cleanup(1); + /* Cleanup items referencing temporary table columns */ + if (!tmp_all_fields3.is_empty()) + { + List_iterator_fast<Item> it(tmp_all_fields3); + Item *item; + while ((item= it++)) + item->cleanup(); + } if (exec_tmp_table1) free_tmp_table(thd, exec_tmp_table1); if (exec_tmp_table2) @@ -7084,15 +7092,17 @@ return_zero_rows(JOIN *join, select_result *result,TABLE_LIST *tables, if (!(result->send_fields(fields, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))) { + bool send_error= FALSE; if (send_row) { List_iterator_fast<Item> it(fields); Item *item; while ((item= it++)) item->no_rows_in_result(); - result->send_data(fields); + send_error= result->send_data(fields); } - result->send_eof(); // Should be safe + if (!send_error) + result->send_eof(); // Should be safe } /* Update results for FOUND_ROWS */ join->thd->limit_found_rows= join->thd->examined_row_count= 0; @@ -13845,7 +13855,7 @@ SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length, pos->field= ((Item_sum*) item)->get_tmp_table_field(); else if (item->type() == Item::COPY_STR_ITEM) { // Blob patch - pos->item= ((Item_copy_string*) item)->item; + pos->item= ((Item_copy*) item)->get_item(); } else pos->item= *order->item; @@ -14916,7 +14926,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, pos= item; if (item->field->flags & BLOB_FLAG) { - if (!(pos= new Item_copy_string(pos))) + if (!(pos= Item_copy::create(pos))) goto err; /* Item_copy_string::copy for function can call @@ -14970,7 +14980,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, on how the value is to be used: In some cases this may be an argument in a group function, like: IF(ISNULL(col),0,COUNT(*)) */ - if (!(pos=new Item_copy_string(pos))) + if (!(pos= Item_copy::create(pos))) goto err; if (i < border) // HAVING, ORDER and GROUP BY { @@ -15023,8 +15033,8 @@ copy_fields(TMP_TABLE_PARAM *param) (*ptr->do_copy)(ptr); List_iterator_fast<Item> it(param->copy_funcs); - Item_copy_string *item; - while ((item = (Item_copy_string*) it++)) + Item_copy *item; + while ((item = (Item_copy*) it++)) item->copy(); } diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 1dd7b55d136..61731f3b984 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -328,6 +328,9 @@ bool String::copy(const char *str, uint32 arg_length, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs, uint *errors) { uint32 offset; + + DBUG_ASSERT(str != Ptr); + if (!needs_conversion(arg_length, from_cs, to_cs, &offset)) { *errors= 0; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 29d43155778..5397128855a 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3126,7 +3126,7 @@ static bool prepare_blob_field(THD *thd, Create_field *sql_field) } sql_field->sql_type= MYSQL_TYPE_BLOB; sql_field->flags|= BLOB_FLAG; - sprintf(warn_buff, ER(ER_AUTO_CONVERT), sql_field->field_name, + my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_AUTO_CONVERT), sql_field->field_name, (sql_field->charset == &my_charset_bin) ? "VARBINARY" : "VARCHAR", (sql_field->charset == &my_charset_bin) ? "BLOB" : "TEXT"); push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_AUTO_CONVERT, @@ -6140,6 +6140,20 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, if (frm_type == FRMTYPE_VIEW && !(alter_info->flags & ~ALTER_RENAME)) { /* + The following branch handles "ALTER VIEW v1 /no arguments/;" + This feature is not documented one. + However, before "OPTIMIZE TABLE t1;" was implemented, + ALTER TABLE with no alter_specifications was used to force-rebuild + the table. That's why this grammar is allowed. That's why we ignore + it for views. So just do nothing in such a case. + */ + if (!new_name) + { + my_ok(thd); + DBUG_RETURN(FALSE); + } + + /* Avoid problems with a rename on a table that we have locked or if the user is trying to to do this in a transcation context */ diff --git a/sql/sql_union.cc b/sql/sql_union.cc index fd3036e3d80..cbf94ad7181 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -653,10 +653,22 @@ bool st_select_lex_unit::cleanup() join->tables= 0; } error|= fake_select_lex->cleanup(); - if (fake_select_lex->order_list.elements) + /* + There are two cases when we should clean order items: + 1. UNION with SELECTs which all enclosed into braces + in this case global_parameters == fake_select_lex + 2. UNION where last SELECT is not enclosed into braces + in this case global_parameters == 'last select' + So we should use global_parameters->order_list for + proper order list clean up. + Note: global_parameters and fake_select_lex are always + initialized for UNION + */ + DBUG_ASSERT(global_parameters); + if (global_parameters->order_list.elements) { ORDER *ord; - for (ord= (ORDER*)fake_select_lex->order_list.first; ord; ord= ord->next) + for (ord= (ORDER*)global_parameters->order_list.first; ord; ord= ord->next) (*ord->item)->cleanup(); } } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index b3bd5d0bc57..dcccf7f147f 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -818,7 +818,7 @@ int mysql_update(THD *thd, if (error < 0) { char buff[STRING_BUFFER_USUAL_SIZE]; - sprintf(buff, ER(ER_UPDATE_INFO), (ulong) found, (ulong) updated, + my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO), (ulong) found, (ulong) updated, (ulong) thd->cuted_fields); thd->row_count_func= (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated; @@ -1031,7 +1031,6 @@ reopen_tables: DBUG_RETURN(TRUE); } - table->mark_columns_needed_for_update(); DBUG_PRINT("info",("setting table `%s` for update", tl->alias)); /* If table will be updated we should not downgrade lock for it and @@ -1275,12 +1274,40 @@ int multi_update::prepare(List<Item> ¬_used_values, } /* + We gather the set of columns read during evaluation of SET expression in + TABLE::tmp_set by pointing TABLE::read_set to it and then restore it after + setup_fields(). + */ + for (table_ref= leaves; table_ref; table_ref= table_ref->next_leaf) + { + TABLE *table= table_ref->table; + if (tables_to_update & table->map) + { + DBUG_ASSERT(table->read_set == &table->def_read_set); + table->read_set= &table->tmp_set; + bitmap_clear_all(table->read_set); + } + } + + /* We have to check values after setup_tables to get covering_keys right in reference tables */ - if (setup_fields(thd, 0, *values, MARK_COLUMNS_READ, 0, 0)) - DBUG_RETURN(1); + int error= setup_fields(thd, 0, *values, MARK_COLUMNS_READ, 0, 0); + + for (table_ref= leaves; table_ref; table_ref= table_ref->next_leaf) + { + TABLE *table= table_ref->table; + if (tables_to_update & table->map) + { + table->read_set= &table->def_read_set; + bitmap_union(table->read_set, &table->tmp_set); + } + } + + if (error) + DBUG_RETURN(1); /* Save tables beeing updated in update_tables @@ -1375,6 +1402,8 @@ int multi_update::prepare(List<Item> ¬_used_values, a row in this table will never be read twice. This is true under the following conditions: + - No column is both written to and read in SET expressions. + - We are doing a table scan and the data is in a separate file (MyISAM) or if we don't update a clustered key. @@ -1389,6 +1418,9 @@ int multi_update::prepare(List<Item> ¬_used_values, WARNING This code is a bit dependent of how make_join_readinfo() works. + The field table->tmp_set is used for keeping track of which fields are + read during evaluation of the SET expression. See multi_update::prepare. + RETURN 0 Not safe to update 1 Safe to update @@ -1409,6 +1441,8 @@ static bool safe_update_on_fly(THD *thd, JOIN_TAB *join_tab, case JT_REF_OR_NULL: return !is_key_used(table, join_tab->ref.key, table->write_set); case JT_ALL: + if (bitmap_is_overlapping(&table->tmp_set, table->write_set)) + return FALSE; /* If range search on index */ if (join_tab->quick) return !join_tab->quick->is_keys_used(table->write_set); @@ -1464,17 +1498,18 @@ multi_update::initialize_tables(JOIN *join) ORDER group; TMP_TABLE_PARAM *tmp_param; - table->mark_columns_needed_for_update(); if (ignore) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); if (table == main_table) // First table in join { if (safe_update_on_fly(thd, join->join_tab, table_ref, all_tables)) { - table_to_update= main_table; // Update table on the fly + table->mark_columns_needed_for_update(); + table_to_update= table; // Update table on the fly continue; } } + table->mark_columns_needed_for_update(); table->prepare_for_position(); /* @@ -2066,8 +2101,8 @@ bool multi_update::send_eof() id= thd->arg_of_last_insert_id_function ? thd->first_successful_insert_id_in_prev_stmt : 0; - sprintf(buff, ER(ER_UPDATE_INFO), (ulong) found, (ulong) updated, - (ulong) thd->cuted_fields); + my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO), + (ulong) found, (ulong) updated, (ulong) thd->cuted_fields); thd->row_count_func= (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated; ::my_ok(thd, (ulong) thd->row_count_func, id, buff); diff --git a/sql/table.cc b/sql/table.cc index d24ee4c6a27..066bbc953fa 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -779,7 +779,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, strpos=disk_buff+6; if (!(rec_per_key= (ulong*) alloc_root(&share->mem_root, - sizeof(ulong*)*key_parts))) + sizeof(ulong)*key_parts))) goto err; for (i=0 ; i < keys ; i++, keyinfo++) diff --git a/sql/unireg.cc b/sql/unireg.cc index 51293184ad8..68a352e4a44 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -37,8 +37,7 @@ static bool pack_header(uchar *forminfo,enum legacy_db_type table_type, List<Create_field> &create_fields, uint info_length, uint screens, uint table_options, ulong data_offset, handler *file); -static uint get_interval_id(uint *int_count,List<Create_field> &create_fields, - Create_field *last_field); +static uint get_interval_id(uint *,List<Create_field> &, Create_field *); static bool pack_fields(File file, List<Create_field> &create_fields, ulong data_offset); static bool make_empty_rec(THD *thd, int file, enum legacy_db_type table_type, |