diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/field.cc | 122 | ||||
-rw-r--r-- | sql/field.h | 31 | ||||
-rw-r--r-- | sql/handler.cc | 3 | ||||
-rw-r--r-- | sql/item.cc | 104 | ||||
-rw-r--r-- | sql/item.h | 5 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 202 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 3 | ||||
-rw-r--r-- | sql/item_func.cc | 52 | ||||
-rw-r--r-- | sql/item_func.h | 1 | ||||
-rw-r--r-- | sql/item_geofunc.cc | 9 | ||||
-rw-r--r-- | sql/item_subselect.cc | 11 | ||||
-rw-r--r-- | sql/item_sum.cc | 3 | ||||
-rw-r--r-- | sql/log.cc | 9 | ||||
-rw-r--r-- | sql/log_event.cc | 2 | ||||
-rw-r--r-- | sql/log_event_old.cc | 2 | ||||
-rw-r--r-- | sql/my_decimal.h | 14 | ||||
-rw-r--r-- | sql/mysqld.cc | 28 | ||||
-rw-r--r-- | sql/opt_range.cc | 27 | ||||
-rw-r--r-- | sql/records.cc | 21 | ||||
-rw-r--r-- | sql/sp_head.cc | 31 | ||||
-rw-r--r-- | sql/sql_acl.cc | 77 | ||||
-rw-r--r-- | sql/sql_base.cc | 13 | ||||
-rw-r--r-- | sql/sql_class.cc | 21 | ||||
-rw-r--r-- | sql/sql_class.h | 6 | ||||
-rw-r--r-- | sql/sql_load.cc | 30 | ||||
-rw-r--r-- | sql/sql_parse.cc | 9 | ||||
-rw-r--r-- | sql/sql_plugin.cc | 6 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 3 | ||||
-rw-r--r-- | sql/sql_select.cc | 163 | ||||
-rw-r--r-- | sql/sql_select.h | 1 | ||||
-rw-r--r-- | sql/sql_update.cc | 5 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 3 | ||||
-rw-r--r-- | sql/time.cc | 16 |
33 files changed, 620 insertions, 413 deletions
diff --git a/sql/field.cc b/sql/field.cc index b1790cb6934..19b0ee8ec7e 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1,4 +1,4 @@ -/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc. +/* Copyright 2000-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. 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 @@ -2513,97 +2513,12 @@ Field_new_decimal::Field_new_decimal(uint32 len_arg, { precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg); set_if_smaller(precision, DECIMAL_MAX_PRECISION); - DBUG_ASSERT(precision >= dec); DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION) && (dec <= DECIMAL_MAX_SCALE)); bin_size= my_decimal_get_binary_size(precision, dec); } -/** - Create a field to hold a decimal value from an item. - - @remark The MySQL DECIMAL data type has a characteristic that needs to be - taken into account when deducing the type from a Item_decimal. - - But first, let's briefly recap what is the new MySQL DECIMAL type: - - The declaration syntax for a decimal is DECIMAL(M,D), where: - - * M is the maximum number of digits (the precision). - It has a range of 1 to 65. - * D is the number of digits to the right of the decimal separator (the scale). - It has a range of 0 to 30 and must be no larger than M. - - D and M are used to determine the storage requirements for the integer - and fractional parts of each value. The integer part is to the left of - the decimal separator and to the right is the fractional part. Hence: - - M is the number of digits for the integer and fractional part. - D is the number of digits for the fractional part. - - Consequently, M - D is the number of digits for the integer part. For - example, a DECIMAL(20,10) column has ten digits on either side of - the decimal separator. - - The characteristic that needs to be taken into account is that the - backing type for Item_decimal is a my_decimal that has a higher - precision (DECIMAL_MAX_POSSIBLE_PRECISION, see my_decimal.h) than - DECIMAL. - - Drawing a comparison between my_decimal and DECIMAL: - - * M has a range of 1 to 81. - * D has a range of 0 to 81. - - There can be a difference in range if the decimal contains a integer - part. This is because the fractional part must always be on a group - boundary, leaving at least one group for the integer part. Since each - group is 9 (DIG_PER_DEC1) digits and there are 9 (DECIMAL_BUFF_LENGTH) - groups, the fractional part is limited to 72 digits if there is at - least one digit in the integral part. - - Although the backing type for a DECIMAL is also my_decimal, every - time a my_decimal is stored in a DECIMAL field, the precision and - scale are explicitly capped at 65 (DECIMAL_MAX_PRECISION) and 30 - (DECIMAL_MAX_SCALE) digits, following my_decimal truncation procedure - (FIX_INTG_FRAC_ERROR). -*/ - -Field_new_decimal * -Field_new_decimal::new_decimal_field(const Item *item) -{ - uint32 len; - uint intg= item->decimal_int_part(), scale= item->decimals; - - DBUG_ASSERT(item->decimal_precision() >= item->decimals); - - /* - Employ a procedure along the lines of the my_decimal truncation process: - - If the integer part is equal to or bigger than the maximum precision: - Truncate integer part to fit and the fractional becomes zero. - - Otherwise: - Truncate fractional part to fit. - */ - if (intg >= DECIMAL_MAX_PRECISION) - { - intg= DECIMAL_MAX_PRECISION; - scale= 0; - } - else - { - uint room= min(DECIMAL_MAX_PRECISION - intg, DECIMAL_MAX_SCALE); - if (scale > room) - scale= room; - } - - len= my_decimal_precision_to_length(intg + scale, scale, item->unsigned_flag); - - return new Field_new_decimal(len, item->maybe_null, item->name, scale, - item->unsigned_flag); -} - - int Field_new_decimal::reset(void) { store_value(&decimal_zero); @@ -6577,20 +6492,9 @@ uint Field::is_equal(Create_field *new_field) } -/* If one of the fields is binary and the other one isn't return 1 else 0 */ - -bool Field_str::compare_str_field_flags(Create_field *new_field, uint32 flag_arg) -{ - return (((new_field->flags & (BINCMP_FLAG | BINARY_FLAG)) && - !(flag_arg & (BINCMP_FLAG | BINARY_FLAG))) || - (!(new_field->flags & (BINCMP_FLAG | BINARY_FLAG)) && - (flag_arg & (BINCMP_FLAG | BINARY_FLAG)))); -} - - uint Field_str::is_equal(Create_field *new_field) { - if (compare_str_field_flags(new_field, flags)) + if (field_flags_are_binary() != new_field->field_flags_are_binary()) return 0; return ((new_field->sql_type == real_type()) && @@ -8381,7 +8285,7 @@ uint Field_blob::max_packed_col_length(uint max_length) uint Field_blob::is_equal(Create_field *new_field) { - if (compare_str_field_flags(new_field, flags)) + if (field_flags_are_binary() != new_field->field_flags_are_binary()) return 0; return ((new_field->sql_type == get_blob_type_from_length(max_data_length())) @@ -8941,7 +8845,7 @@ uint Field_enum::is_equal(Create_field *new_field) The fields are compatible if they have the same flags, type, charset and have the same underlying length. */ - if (compare_str_field_flags(new_field, flags) || + if (new_field->field_flags_are_binary() != field_flags_are_binary() || new_field->sql_type != real_type() || new_field->charset != field_charset || new_field->pack_length != pack_length()) @@ -9710,7 +9614,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, } if (length == 0) - fld_length= 0; /* purecov: inspected */ + fld_length= NULL; /* purecov: inspected */ } sign_len= fld_type_modifier & UNSIGNED_FLAG ? 0 : 1; @@ -9862,8 +9766,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, case MYSQL_TYPE_TIMESTAMP: if (fld_length == NULL) { - /* Compressed date YYYYMMDDHHMMSS */ - length= MAX_DATETIME_COMPRESSED_WIDTH; + length= MAX_DATETIME_WIDTH; } else if (length != MAX_DATETIME_WIDTH) { @@ -9927,7 +9830,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, sql_type= MYSQL_TYPE_NEWDATE; /* fall trough */ case MYSQL_TYPE_NEWDATE: - length= 10; + length= MAX_DATE_WIDTH; break; case MYSQL_TYPE_TIME: length= 10; @@ -10008,6 +9911,17 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, DBUG_RETURN(TRUE); } + switch (fld_type) { + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + charset= &my_charset_bin; + flags|= BINCMP_FLAG; + default: break; + } + DBUG_RETURN(FALSE); /* success */ } diff --git a/sql/field.h b/sql/field.h index 9ad00e0dab3..7235115a888 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1,7 +1,7 @@ #ifndef FIELD_INCLUDED #define FIELD_INCLUDED -/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc. +/* Copyright 2000-2008 MySQL AB, 2008, 2009 Sun Microsystems, Inc. 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 @@ -613,15 +613,17 @@ protected: handle_int64(to, from, low_byte_first_from, table->s->db_low_byte_first); return from + sizeof(int64); } + + bool field_flags_are_binary() + { + return (flags & (BINCMP_FLAG | BINARY_FLAG)) != 0; + } + }; class Field_num :public Field { public: - /** - The scale of the Field's value, i.e. the number of digits to the right - of the decimal point. - */ const uint8 dec; bool zerofill,unsigned_flag; // Purify cannot handle bit fields Field_num(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, @@ -672,7 +674,6 @@ public: friend class Create_field; my_decimal *val_decimal(my_decimal *); virtual bool str_needs_quotes() { return TRUE; } - bool compare_str_field_flags(Create_field *new_field, uint32 flags); uint is_equal(Create_field *new_field); }; @@ -780,11 +781,6 @@ public: Field_new_decimal(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, uint8 dec_arg, bool unsigned_arg); - /* - Create a field to hold a decimal value from an item. - Truncates the precision and/or scale if necessary. - */ - static Field_new_decimal *new_decimal_field(const Item *item); enum_field_types type() const { return MYSQL_TYPE_NEWDECIMAL;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } Item_result result_type () const { return DECIMAL_RESULT; } @@ -1287,12 +1283,12 @@ public: Field_date(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, CHARSET_INFO *cs) - :Field_str(ptr_arg, 10, null_ptr_arg, null_bit_arg, + :Field_str(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, cs) {} Field_date(bool maybe_null_arg, const char *field_name_arg, CHARSET_INFO *cs) - :Field_str((uchar*) 0,10, maybe_null_arg ? (uchar*) "": 0,0, + :Field_str((uchar*) 0, MAX_DATE_WIDTH, maybe_null_arg ? (uchar*) "": 0,0, NONE, field_name_arg, cs) {} enum_field_types type() const { return MYSQL_TYPE_DATE;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; } @@ -1402,12 +1398,12 @@ public: Field_datetime(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, CHARSET_INFO *cs) - :Field_str(ptr_arg, 19, null_ptr_arg, null_bit_arg, + :Field_str(ptr_arg, MAX_DATETIME_WIDTH, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, cs) {} Field_datetime(bool maybe_null_arg, const char *field_name_arg, CHARSET_INFO *cs) - :Field_str((uchar*) 0,19, maybe_null_arg ? (uchar*) "": 0,0, + :Field_str((uchar*) 0, MAX_DATETIME_WIDTH, maybe_null_arg ? (uchar*) "": 0,0, NONE, field_name_arg, cs) {} enum_field_types type() const { return MYSQL_TYPE_DATETIME;} #ifdef HAVE_LONG_LONG @@ -2082,6 +2078,11 @@ public: Item *on_update_value, LEX_STRING *comment, char *change, List<String> *interval_list, CHARSET_INFO *cs, uint uint_geom_type); + + bool field_flags_are_binary() + { + return (flags & (BINCMP_FLAG | BINARY_FLAG)) != 0; + } }; diff --git a/sql/handler.cc b/sql/handler.cc index cccedc20f48..ad7e1ecfa80 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1341,7 +1341,8 @@ int ha_rollback_trans(THD *thd, bool all) } trans->ha_list= 0; trans->no_2pc=0; - if (is_real_trans && thd->transaction_rollback_request) + if (is_real_trans && thd->transaction_rollback_request && + thd->transaction.xid_state.xa_state != XA_NOTR) thd->transaction.xid_state.rm_error= thd->stmt_da->sql_errno(); if (all) thd->variables.tx_isolation=thd->session_tx_isolation; diff --git a/sql/item.cc b/sql/item.cc index 90b209e880f..b59a82f8045 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -436,26 +436,17 @@ Item::Item(THD *thd, Item *item): } -/** - Decimal precision of the item. - - @remark The precision must not be capped as it can be used in conjunction - with Item::decimals to determine the size of the integer part when - constructing a decimal data type. - - @see Item::decimal_int_part() - @see Item::decimals -*/ - uint Item::decimal_precision() const { - uint precision= max_length; Item_result restype= result_type(); if ((restype == DECIMAL_RESULT) || (restype == INT_RESULT)) - precision= my_decimal_length_to_precision(max_length, decimals, unsigned_flag); - - return precision; + { + uint prec= + my_decimal_length_to_precision(max_length, decimals, unsigned_flag); + return min(prec, DECIMAL_MAX_PRECISION); + } + return min(max_length, DECIMAL_MAX_PRECISION); } @@ -5072,7 +5063,9 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table, bool fixed_length) switch (field_type()) { case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: - field= Field_new_decimal::new_decimal_field(this); + field= new Field_new_decimal((uchar*) 0, max_length, null_ptr, 0, + Field::NONE, name, decimals, 0, + unsigned_flag); break; case MYSQL_TYPE_TINY: field= new Field_tiny((uchar*) 0, max_length, null_ptr, 0, Field::NONE, @@ -7030,72 +7023,61 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) } /** - Compare the value stored in field, with the original item. + Compare the value stored in field with the expression from the query. - @param field field which the item is converted and stored in - @param item original item + @param field Field which the Item is stored in after conversion + @param item Original expression from query - @return Return an integer greater than, equal to, or less than 0 if - the value stored in the field is greater than, equal to, - or less than the original item + @return Returns an integer greater than, equal to, or less than 0 if + the value stored in the field is greater than, equal to, + or less than the original Item. A 0 may also be returned if + out of memory. @note We only use this on the range optimizer/partition pruning, because in some cases we can't store the value in the field without some precision/character loss. */ -int stored_field_cmp_to_item(Field *field, Item *item) +int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) { - Item_result res_type=item_cmp_type(field->result_type(), item->result_type()); if (res_type == STRING_RESULT) { char item_buff[MAX_FIELD_WIDTH]; char field_buff[MAX_FIELD_WIDTH]; - String item_tmp(item_buff,sizeof(item_buff),&my_charset_bin),*item_result; + + String item_tmp(item_buff,sizeof(item_buff),&my_charset_bin); String field_tmp(field_buff,sizeof(field_buff),&my_charset_bin); - enum_field_types field_type; - item_result=item->val_str(&item_tmp); + String *item_result= item->val_str(&item_tmp); + /* + Some implementations of Item::val_str(String*) actually modify + the field Item::null_value, hence we can't check it earlier. + */ if (item->null_value) return 0; - field->val_str(&field_tmp); + String *field_result= field->val_str(&field_tmp); - /* - If comparing DATE with DATETIME, append the time-part to the DATE. - So that the strings are equally formatted. - A DATE converted to string is 10 (MAX_DATE_WIDTH) characters, - and a DATETIME converted to string is 19 (MAX_DATETIME_WIDTH) characters. - */ - field_type= field->type(); - uint32 item_length= item_result->length(); - if (field_type == MYSQL_TYPE_DATE && - item_length == MAX_DATETIME_WIDTH) - field_tmp.append(" 00:00:00"); - else if (field_type == MYSQL_TYPE_DATETIME) + enum_field_types field_type= field->type(); + + if (field_type == MYSQL_TYPE_DATE || field_type == MYSQL_TYPE_DATETIME) { - if (item_length == MAX_DATE_WIDTH) - item_result->append(" 00:00:00"); - else if (item_length > MAX_DATETIME_WIDTH) - { - /* - We don't store microsecond part of DATETIME in field - but item_result contains it. As we compare DATETIMEs as strings - we must trim trailing 0's in item_result's microsecond part - to ensure "YYYY-MM-DD HH:MM:SS" == "YYYY-MM-DD HH:MM:SS.0000" - */ - char *end= (char *) item_result->ptr() + item_length - 1; - /* Trim trailing 0's */ - while (*end == '0') - end--; - /* Trim '.' if no microseconds */ - if (*end == '.') - end--; - DBUG_ASSERT(end - item_result->ptr() + 1 >= MAX_DATETIME_WIDTH); - item_result->length(end - item_result->ptr() + 1); - } + enum_mysql_timestamp_type type= MYSQL_TIMESTAMP_ERROR; + + if (field_type == MYSQL_TYPE_DATE) + type= MYSQL_TIMESTAMP_DATE; + + if (field_type == MYSQL_TYPE_DATETIME) + type= MYSQL_TIMESTAMP_DATETIME; + + const char *field_name= field->field_name; + MYSQL_TIME field_time, item_time; + get_mysql_time_from_str(thd, field_result, type, field_name, &field_time); + get_mysql_time_from_str(thd, item_result, type, field_name, &item_time); + + return my_time_compare(&field_time, &item_time); } - return stringcmp(&field_tmp,item_result); + return stringcmp(field_result, item_result); } if (res_type == INT_RESULT) return 0; // Both are of type int diff --git a/sql/item.h b/sql/item.h index bb7ef73a5e2..72e4e37342a 100644 --- a/sql/item.h +++ b/sql/item.h @@ -770,10 +770,9 @@ public: virtual cond_result eq_cmp_result() const { return COND_OK; } inline uint float_length(uint decimals_par) const { return decimals != NOT_FIXED_DEC ? (DBL_DIG+2+decimals_par) : DBL_DIG+8;} - /** Returns the uncapped decimal precision of this item. */ virtual uint decimal_precision() const; inline int decimal_int_part() const - { return decimal_precision() - decimals; } + { return my_decimal_int_part(decimal_precision(), decimals); } /* Returns true if this is constant (during query execution, i.e. its value will not change until next fix_fields) and its value is known. @@ -3154,6 +3153,6 @@ void mark_select_range_as_dependent(THD *thd, extern Cached_item *new_Cached_item(THD *thd, Item *item); extern Item_result item_cmp_type(Item_result a,Item_result b); extern void resolve_const_item(THD *thd, Item **ref, Item *cmp_item); -extern int stored_field_cmp_to_item(Field *field, Item *item); +extern int stored_field_cmp_to_item(THD *thd, Field *field, Item *item); #endif /* ITEM_INCLUDED */ diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index c29031d25b5..c6b88cd8188 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -636,56 +636,51 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type) return 0; } - /** - @brief Convert date provided in a string to the int representation. - - @param[in] thd thread handle - @param[in] str a string to convert - @param[in] warn_type type of the timestamp for issuing the warning - @param[in] warn_name field name for issuing the warning - @param[out] error_arg could not extract a DATE or DATETIME - - @details Convert date provided in the string str to the int - representation. If the string contains wrong date or doesn't - contain it at all then a warning is issued. The warn_type and - the warn_name arguments are used as the name and the type of the - field when issuing the warning. If any input was discarded - (trailing or non-timestampy characters), was_cut will be non-zero. - was_type will return the type str_to_datetime() could correctly - extract. - - @return - converted value. 0 on error and on zero-dates -- check 'failure' + Parse date provided in a string to a MYSQL_TIME. + + @param[in] thd Thread handle + @param[in] str A string to convert + @param[in] warn_type Type of the timestamp for issuing the warning + @param[in] warn_name Field name for issuing the warning + @param[out] l_time The MYSQL_TIME objects is initialized. + + Parses a date provided in the string str into a MYSQL_TIME object. If the + string contains an incorrect date or doesn't correspond to a date at all + then a warning is issued. The warn_type and the warn_name arguments are used + as the name and the type of the field when issuing the warning. If any input + was discarded (trailing or non-timestamp-y characters), return value will be + TRUE. + + @return Status flag + @retval FALSE Success. + @retval True Indicates failure. */ -static ulonglong -get_date_from_str(THD *thd, String *str, timestamp_type warn_type, - char *warn_name, bool *error_arg) +bool get_mysql_time_from_str(THD *thd, String *str, timestamp_type warn_type, + const char *warn_name, MYSQL_TIME *l_time) { - ulonglong value= 0; + bool value; int error; - MYSQL_TIME l_time; - enum_mysql_timestamp_type ret; + enum_mysql_timestamp_type timestamp_type; - ret= str_to_datetime(str->ptr(), str->length(), &l_time, - (TIME_FUZZY_DATE | MODE_INVALID_DATES | - (thd->variables.sql_mode & - (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE))), - &error); + timestamp_type= + str_to_datetime(str->ptr(), str->length(), l_time, + (TIME_FUZZY_DATE | MODE_INVALID_DATES | + (thd->variables.sql_mode & + (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE))), + &error); - if (ret == MYSQL_TIMESTAMP_DATETIME || ret == MYSQL_TIMESTAMP_DATE) - { + if (timestamp_type == MYSQL_TIMESTAMP_DATETIME || + timestamp_type == MYSQL_TIMESTAMP_DATE) /* Do not return yet, we may still want to throw a "trailing garbage" warning. */ - *error_arg= FALSE; - value= TIME_to_ulonglong_datetime(&l_time); - } + value= FALSE; else { - *error_arg= TRUE; + value= TRUE; error= 1; /* force warning */ } @@ -698,6 +693,37 @@ get_date_from_str(THD *thd, String *str, timestamp_type warn_type, } +/** + @brief Convert date provided in a string to the int representation. + + @param[in] thd thread handle + @param[in] str a string to convert + @param[in] warn_type type of the timestamp for issuing the warning + @param[in] warn_name field name for issuing the warning + @param[out] error_arg could not extract a DATE or DATETIME + + @details Convert date provided in the string str to the int + representation. If the string contains wrong date or doesn't + contain it at all then a warning is issued. The warn_type and + the warn_name arguments are used as the name and the type of the + field when issuing the warning. + + @return + converted value. 0 on error and on zero-dates -- check 'failure' +*/ +static ulonglong get_date_from_str(THD *thd, String *str, + timestamp_type warn_type, + const char *warn_name, bool *error_arg) +{ + MYSQL_TIME l_time; + *error_arg= get_mysql_time_from_str(thd, str, warn_type, warn_name, &l_time); + + if (*error_arg) + return 0; + return TIME_to_ulonglong_datetime(&l_time); +} + + /* Check whether compare_datetime() can be used to compare items. @@ -1559,61 +1585,73 @@ longlong Item_in_optimizer::val_int() if (cache->null_value) { + /* + We're evaluating + "<outer_value_list> [NOT] IN (SELECT <inner_value_list>...)" + where one or more of the outer values is NULL. + */ if (((Item_in_subselect*)args[1])->is_top_level_item()) { /* - We're evaluating "NULL IN (SELECT ...)". The result can be NULL or - FALSE, and we can return one instead of another. Just return NULL. + We're evaluating a top level item, e.g. + "<outer_value_list> IN (SELECT <inner_value_list>...)", + and in this case a NULL value in the outer_value_list means + that the result shall be NULL/FALSE (makes no difference for + top level items). The cached value is NULL, so just return + NULL. */ null_value= 1; } else { - if (!((Item_in_subselect*)args[1])->is_correlated && - result_for_null_param != UNKNOWN) + /* + We're evaluating an item where a NULL value in either the + outer or inner value list does not automatically mean that we + can return NULL/FALSE. An example of such a query is + "<outer_value_list> NOT IN (SELECT <inner_value_list>...)" + The result when there is at least one NULL value is: NULL if the + SELECT evaluated over the non-NULL values produces at least + one row, FALSE otherwise + */ + Item_in_subselect *item_subs=(Item_in_subselect*)args[1]; + bool all_left_cols_null= true; + const uint ncols= cache->cols(); + + /* + Turn off the predicates that are based on column compares for + which the left part is currently NULL + */ + for (uint i= 0; i < ncols; i++) { - /* Use cached value from previous execution */ - null_value= result_for_null_param; + if (cache->element_index(i)->null_value) + item_subs->set_cond_guard_var(i, FALSE); + else + all_left_cols_null= false; } - else + + if (!((Item_in_subselect*)args[1])->is_correlated && + all_left_cols_null && result_for_null_param != UNKNOWN) { - /* - We're evaluating "NULL IN (SELECT ...)". The result is: - FALSE if SELECT produces an empty set, or - NULL otherwise. - We disable the predicates we've pushed down into subselect, run the - subselect and see if it has produced any rows. + /* + This is a non-correlated subquery, all values in the outer + value list are NULL, and we have already evaluated the + subquery for all NULL values: Return the same result we + did last time without evaluating the subquery. */ - Item_in_subselect *item_subs=(Item_in_subselect*)args[1]; - if (cache->cols() == 1) - { - item_subs->set_cond_guard_var(0, FALSE); - (void) args[1]->val_bool_result(); - result_for_null_param= null_value= !item_subs->engine->no_rows(); - item_subs->set_cond_guard_var(0, TRUE); - } - else - { - uint i; - uint ncols= cache->cols(); - /* - Turn off the predicates that are based on column compares for - which the left part is currently NULL - */ - for (i= 0; i < ncols; i++) - { - if (cache->element_index(i)->null_value) - item_subs->set_cond_guard_var(i, FALSE); - } - - (void) args[1]->val_bool_result(); - result_for_null_param= null_value= !item_subs->engine->no_rows(); - - /* Turn all predicates back on */ - for (i= 0; i < ncols; i++) - item_subs->set_cond_guard_var(i, TRUE); - } + null_value= result_for_null_param; + } + else + { + /* The subquery has to be evaluated */ + (void) args[1]->val_bool_result(); + null_value= !item_subs->engine->no_rows(); + if (all_left_cols_null) + result_for_null_param= null_value; } + + /* Turn all predicates back on */ + for (uint i= 0; i < ncols; i++) + item_subs->set_cond_guard_var(i, TRUE); } return 0; } @@ -2191,7 +2229,7 @@ uint Item_func_ifnull::decimal_precision() const int arg1_int_part= args[1]->decimal_int_part(); int max_int_part= max(arg0_int_part, arg1_int_part); int precision= max_int_part + decimals; - return precision; + return min(precision, DECIMAL_MAX_PRECISION); } @@ -2375,7 +2413,7 @@ uint Item_func_if::decimal_precision() const int arg1_prec= args[1]->decimal_int_part(); int arg2_prec= args[2]->decimal_int_part(); int precision=max(arg1_prec,arg2_prec) + decimals; - return precision; + return min(precision, DECIMAL_MAX_PRECISION); } @@ -2783,7 +2821,7 @@ uint Item_func_case::decimal_precision() const if (else_expr_num != -1) set_if_bigger(max_int_part, args[else_expr_num]->decimal_int_part()); - return max_int_part + decimals; + return min(max_int_part + decimals, DECIMAL_MAX_PRECISION); } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 3462bed94a2..8354f6a5d30 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1725,4 +1725,7 @@ inline Item *and_conds(Item *a, Item *b) Item *and_expressions(Item *a, Item *b, Item **org_item); +bool get_mysql_time_from_str(THD *thd, String *str, timestamp_type warn_type, + const char *warn_name, MYSQL_TIME *l_time); + #endif /* ITEM_CMPFUNC_INCLUDED */ diff --git a/sql/item_func.cc b/sql/item_func.cc index f4db3ef03e7..23af528c256 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -451,8 +451,45 @@ Field *Item_func::tmp_table_field(TABLE *table) return make_string_field(table); break; case DECIMAL_RESULT: - field= Field_new_decimal::new_decimal_field(this); + { + uint8 dec= decimals; + uint8 intg= decimal_precision() - dec; + uint32 len= max_length; + + /* + Trying to put too many digits overall in a DECIMAL(prec,dec) + will always throw a warning. We must limit dec to + DECIMAL_MAX_SCALE however to prevent an assert() later. + */ + + if (dec > 0) + { + int overflow; + + dec= min(dec, DECIMAL_MAX_SCALE); + + /* + If the value still overflows the field with the corrected dec, + we'll throw out decimals rather than integers. This is still + bad and of course throws a truncation warning. + */ + + const int required_length= + my_decimal_precision_to_length(intg + dec, dec, + unsigned_flag); + + overflow= required_length - len; + + if (overflow > 0) + dec= max(0, dec - overflow); // too long, discard fract + else + /* Corrected value fits. */ + len= required_length; + } + + field= new Field_new_decimal(len, maybe_null, name, dec, unsigned_flag); break; + } case ROW_RESULT: default: // This case should never be chosen @@ -4739,19 +4776,6 @@ void Item_func_get_user_var::fix_length_and_dec() } -uint Item_func_get_user_var::decimal_precision() const -{ - uint precision= max_length; - Item_result restype= result_type(); - - /* Default to maximum as the precision is unknown a priori. */ - if ((restype == DECIMAL_RESULT) || (restype == INT_RESULT)) - precision= DECIMAL_MAX_PRECISION; - - return precision; -} - - bool Item_func_get_user_var::const_item() const { return (!var_entry || current_thd->query_id != var_entry->update_query_id); diff --git a/sql/item_func.h b/sql/item_func.h index 628878bcaed..6f2b3f0bfc2 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1396,7 +1396,6 @@ public: table_map used_tables() const { return const_item() ? 0 : RAND_TABLE_BIT; } bool eq(const Item *item, bool binary_cmp) const; - uint decimal_precision() const; private: bool set_value(THD *thd, sp_rcontext *ctx, Item **it); diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index a34204b7181..3c5990eb359 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -84,7 +84,9 @@ String *Item_func_geometry_from_wkb::val_str(String *str) if (args[0]->field_type() == MYSQL_TYPE_GEOMETRY) { - return args[0]->val_str(str); + String *str_ret= args[0]->val_str(str); + null_value= args[0]->null_value; + return str_ret; } wkb= args[0]->val_str(&arg_val); @@ -94,7 +96,10 @@ String *Item_func_geometry_from_wkb::val_str(String *str) str->set_charset(&my_charset_bin); if (str->reserve(SRID_SIZE, 512)) - return 0; + { + null_value= TRUE; /* purecov: inspected */ + return 0; /* purecov: inspected */ + } str->length(0); str->q_append(srid); if ((null_value= diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index d16710f6660..335b9f79e78 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -311,9 +311,14 @@ void Item_subselect::update_used_tables() void Item_subselect::print(String *str, enum_query_type query_type) { - str->append('('); - engine->print(str, query_type); - str->append(')'); + if (engine) + { + str->append('('); + engine->print(str, query_type); + str->append(')'); + } + else + str->append("(...)"); } diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 273e996a6a0..68aa52f561c 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -522,7 +522,8 @@ Field *Item_sum::create_tmp_field(bool group, TABLE *table, name, table->s, collation.collation); break; case DECIMAL_RESULT: - field= Field_new_decimal::new_decimal_field(this); + field= new Field_new_decimal(max_length, maybe_null, name, + decimals, unsigned_flag); break; case ROW_RESULT: default: diff --git a/sql/log.cc b/sql/log.cc index c0bba0160c3..e7deebad196 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1562,7 +1562,6 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all) trx_data->at_least_one_stmt_committed = my_b_tell(&trx_data->trans_log) > 0; -end: if (!all) trx_data->before_stmt_pos = MY_OFF_T_UNDEF; // part of the stmt commit DBUG_RETURN(error); @@ -3617,7 +3616,7 @@ void MYSQL_BIN_LOG::new_file_impl(bool need_lock) } old_name=name; name=0; // Don't free name - close(LOG_CLOSE_TO_BE_OPENED); + close(LOG_CLOSE_TO_BE_OPENED | LOG_CLOSE_INDEX); /* Note that at this point, log_state != LOG_CLOSED (important for is_open()). @@ -3632,8 +3631,10 @@ void MYSQL_BIN_LOG::new_file_impl(bool need_lock) trigger temp tables deletion on slaves. */ - open(old_name, log_type, new_name_ptr, - io_cache_type, no_auto_events, max_size, 1); + /* reopen index binlog file, BUG#34582 */ + if (!open_index_file(index_file_name, 0)) + open(old_name, log_type, new_name_ptr, + io_cache_type, no_auto_events, max_size, 1); my_free(old_name,MYF(0)); end: diff --git a/sql/log_event.cc b/sql/log_event.cc index b7894bb2ba4..6a73bd69d02 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -7550,7 +7550,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) } if (get_flags(STMT_END_F)) - if (error= rows_event_stmt_cleanup(rli, thd)) + if ((error= rows_event_stmt_cleanup(rli, thd))) rli->report(ERROR_LEVEL, error, "Error in %s event: commit of row events failed, " "table `%s`.`%s`", diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index e6888af9cfc..fbcbb388236 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -1814,7 +1814,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli) are involved, commit the transaction and flush the pending event to the binlog. */ - if (error= ha_autocommit_or_rollback(thd, 0)) + if ((error= ha_autocommit_or_rollback(thd, 0))) rli->report(ERROR_LEVEL, error, "Error in %s event: commit of row events failed, " "table `%s`.`%s`", diff --git a/sql/my_decimal.h b/sql/my_decimal.h index b1df1395dcd..21669e82c44 100644 --- a/sql/my_decimal.h +++ b/sql/my_decimal.h @@ -48,12 +48,10 @@ C_MODE_END digits * number of decimal digits in one our big digit - number of decimal digits in one our big digit decreased by 1 (because we always put decimal point on the border of our big digits)) - - This value is 65 due to historical reasons partly due to it being used - as the maximum allowed precision and not the actual maximum precision. */ #define DECIMAL_MAX_PRECISION (DECIMAL_MAX_POSSIBLE_PRECISION - 8*2) #define DECIMAL_MAX_SCALE 30 +#define DECIMAL_NOT_SPECIFIED 31 /** maximum length of string representation (number of maximum decimal @@ -77,6 +75,12 @@ inline uint my_decimal_size(uint precision, uint scale) } +inline int my_decimal_int_part(uint precision, uint decimals) +{ + return precision - ((decimals == DECIMAL_NOT_SPECIFIED) ? 0 : decimals); +} + + /** my_decimal class limits 'decimal_t' type to what we need in MySQL. @@ -180,7 +184,7 @@ inline uint my_decimal_length_to_precision(uint length, uint scale, } inline uint32 my_decimal_precision_to_length_no_truncation(uint precision, - uint scale, + uint8 scale, bool unsigned_flag) { /* @@ -192,7 +196,7 @@ inline uint32 my_decimal_precision_to_length_no_truncation(uint precision, (unsigned_flag || !precision ? 0 : 1)); } -inline uint32 my_decimal_precision_to_length(uint precision, uint scale, +inline uint32 my_decimal_precision_to_length(uint precision, uint8 scale, bool unsigned_flag) { /* diff --git a/sql/mysqld.cc b/sql/mysqld.cc index cd2ff2055c5..c1b8b62c470 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1740,7 +1740,7 @@ static void network_init(void) saPipeSecurity.lpSecurityDescriptor = &sdPipeDescriptor; saPipeSecurity.bInheritHandle = FALSE; if ((hPipe= CreateNamedPipe(pipe_name, - PIPE_ACCESS_DUPLEX, + PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, @@ -5233,17 +5233,26 @@ pthread_handler_t handle_connections_sockets_thread(void *arg) pthread_handler_t handle_connections_namedpipes(void *arg) { HANDLE hConnectedPipe; - BOOL fConnected; + OVERLAPPED connectOverlapped = {0}; THD *thd; my_thread_init(); DBUG_ENTER("handle_connections_namedpipes"); - (void) my_pthread_getprio(pthread_self()); // For debugging + connectOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); DBUG_PRINT("general",("Waiting for named pipe connections.")); while (!abort_loop) { /* wait for named pipe connection */ - fConnected = ConnectNamedPipe(hPipe, NULL); + BOOL fConnected= ConnectNamedPipe(hPipe, &connectOverlapped); + if (!fConnected && (GetLastError() == ERROR_IO_PENDING)) + { + /* + ERROR_IO_PENDING says async IO has started but not yet finished. + GetOverlappedResult will wait for completion. + */ + DWORD bytes; + fConnected= GetOverlappedResult(hPipe, &connectOverlapped,&bytes, TRUE); + } if (abort_loop) break; if (!fConnected) @@ -5252,7 +5261,7 @@ pthread_handler_t handle_connections_namedpipes(void *arg) { CloseHandle(hPipe); if ((hPipe= CreateNamedPipe(pipe_name, - PIPE_ACCESS_DUPLEX, + PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, @@ -5272,7 +5281,7 @@ pthread_handler_t handle_connections_namedpipes(void *arg) hConnectedPipe = hPipe; /* create new pipe for new connection */ if ((hPipe = CreateNamedPipe(pipe_name, - PIPE_ACCESS_DUPLEX, + PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, @@ -5294,7 +5303,7 @@ pthread_handler_t handle_connections_namedpipes(void *arg) CloseHandle(hConnectedPipe); continue; } - if (!(thd->net.vio = vio_new_win32pipe(hConnectedPipe)) || + if (!(thd->net.vio= vio_new_win32pipe(hConnectedPipe)) || my_net_init(&thd->net, thd->net.vio)) { close_connection(thd, ER_OUT_OF_RESOURCES, 1); @@ -5305,7 +5314,7 @@ pthread_handler_t handle_connections_namedpipes(void *arg) thd->security_ctx->host= my_strdup(my_localhost, MYF(0)); create_new_thread(thd); } - + CloseHandle(connectOverlapped.hEvent); decrement_handler_count(); DBUG_RETURN(0); } @@ -5482,8 +5491,7 @@ pthread_handler_t handle_connections_shared_memory(void *arg) errmsg= "Could not set client to read mode"; goto errorconn; } - if (!(thd->net.vio= vio_new_win32shared_memory(&thd->net, - handle_client_file_map, + if (!(thd->net.vio= vio_new_win32shared_memory(handle_client_file_map, handle_client_map, event_client_wrote, event_client_read, diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 61122b9892d..efd8acf7709 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -5885,6 +5885,27 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field, !(conf_func->compare_collation()->state & MY_CS_BINSORT)) goto end; + if (key_part->image_type == Field::itMBR) + { + switch (type) { + case Item_func::SP_EQUALS_FUNC: + case Item_func::SP_DISJOINT_FUNC: + case Item_func::SP_INTERSECTS_FUNC: + case Item_func::SP_TOUCHES_FUNC: + case Item_func::SP_CROSSES_FUNC: + case Item_func::SP_WITHIN_FUNC: + case Item_func::SP_CONTAINS_FUNC: + case Item_func::SP_OVERLAPS_FUNC: + break; + default: + /* + We cannot involve spatial indexes for queries that + don't use MBREQUALS(), MBRDISJOINT(), etc. functions. + */ + goto end; + } + } + if (param->using_real_indexes) optimize_range= field->optimize_range(param->real_keynr[key_part->key], key_part->part); @@ -6123,7 +6144,7 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field, switch (type) { case Item_func::LT_FUNC: - if (stored_field_cmp_to_item(field,value) == 0) + if (stored_field_cmp_to_item(param->thd, field, value) == 0) tree->max_flag=NEAR_MAX; /* fall through */ case Item_func::LE_FUNC: @@ -6138,14 +6159,14 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field, case Item_func::GT_FUNC: /* Don't use open ranges for partial key_segments */ if ((!(key_part->flag & HA_PART_KEY_SEG)) && - (stored_field_cmp_to_item(field, value) <= 0)) + (stored_field_cmp_to_item(param->thd, field, value) <= 0)) tree->min_flag=NEAR_MIN; tree->max_flag= NO_MAX_RANGE; break; case Item_func::GE_FUNC: /* Don't use open ranges for partial key_segments */ if ((!(key_part->flag & HA_PART_KEY_SEG)) && - (stored_field_cmp_to_item(field,value) < 0)) + (stored_field_cmp_to_item(param->thd, field, value) < 0)) tree->min_flag= NEAR_MIN; tree->max_flag=NO_MAX_RANGE; break; diff --git a/sql/records.cc b/sql/records.cc index 9b5ea40478e..93b19aefbaf 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -62,6 +62,7 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, { empty_record(table); bzero((char*) info,sizeof(*info)); + info->thd= thd; info->table= table; info->file= table->file; info->record= table->record[0]; @@ -297,6 +298,12 @@ void end_read_record(READ_RECORD *info) static int rr_handle_error(READ_RECORD *info, int error) { + if (info->thd->killed) + { + info->thd->send_kill_message(); + return 1; + } + if (error == HA_ERR_END_OF_FILE) error= -1; else @@ -317,12 +324,7 @@ static int rr_quick(READ_RECORD *info) int tmp; while ((tmp= info->select->quick->get_next())) { - if (info->thd->killed) - { - my_error(ER_SERVER_SHUTDOWN, MYF(0)); - return 1; - } - if (tmp != HA_ERR_RECORD_DELETED) + if (info->thd->killed || (tmp != HA_ERR_RECORD_DELETED)) { tmp= rr_handle_error(info, tmp); break; @@ -385,16 +387,11 @@ int rr_sequential(READ_RECORD *info) int tmp; while ((tmp=info->file->rnd_next(info->record))) { - if (info->thd->killed) - { - info->thd->send_kill_message(); - return 1; - } /* rnd_next can return RECORD_DELETED for MyISAM when one thread is reading and another deleting without locks. */ - if (tmp != HA_ERR_RECORD_DELETED) + if (info->thd->killed || (tmp != HA_ERR_RECORD_DELETED)) { tmp= rr_handle_error(info, tmp); break; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 3c610c1fdf0..0287ed8f2c2 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -336,16 +336,18 @@ bool sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr) { Item *expr_item; + enum_check_fields save_count_cuted_fields= thd->count_cuted_fields; + bool save_abort_on_warning= thd->abort_on_warning; + bool save_stmt_modified_non_trans_table= + thd->transaction.stmt.modified_non_trans_table; DBUG_ENTER("sp_eval_expr"); if (!*expr_item_ptr) - DBUG_RETURN(TRUE); + goto error; if (!(expr_item= sp_prepare_func_item(thd, expr_item_ptr))) - DBUG_RETURN(TRUE); - - bool err_status= FALSE; + goto error; /* Set THD flags to emit warnings/errors in case of overflow/type errors @@ -354,10 +356,6 @@ sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr) Save original values and restore them after save. */ - enum_check_fields save_count_cuted_fields= thd->count_cuted_fields; - bool save_abort_on_warning= thd->abort_on_warning; - bool save_stmt_modified_non_trans_table= thd->transaction.stmt.modified_non_trans_table; - thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL; thd->abort_on_warning= thd->variables.sql_mode & @@ -372,13 +370,18 @@ sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr) thd->abort_on_warning= save_abort_on_warning; thd->transaction.stmt.modified_non_trans_table= save_stmt_modified_non_trans_table; - if (thd->is_error()) - { - /* Return error status if something went wrong. */ - err_status= TRUE; - } + if (!thd->is_error()) + DBUG_RETURN(FALSE); - DBUG_RETURN(err_status); +error: + /* + In case of error during evaluation, leave the result field set to NULL. + Sic: we can't do it in the beginning of the function because the + result field might be needed for its own re-evaluation, e.g. case of + set x = x + 1; + */ + result_field->set_null(); + DBUG_RETURN (TRUE); } diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index f0286300704..46d7f3ce89d 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -263,8 +263,7 @@ my_bool acl_init(bool dont_read_acl_tables) acl_cache= new hash_filo(ACL_CACHE_SIZE, 0, 0, (my_hash_get_key) acl_entry_get_key, (my_hash_free_key) free, - lower_case_file_system ? - system_charset_info : &my_charset_bin); + &my_charset_utf8_bin); if (dont_read_acl_tables) { DBUG_RETURN(0); /* purecov: tested */ @@ -2252,10 +2251,13 @@ public: ulong sort; size_t key_length; GRANT_NAME(const char *h, const char *d,const char *u, - const char *t, ulong p); - GRANT_NAME (TABLE *form); + const char *t, ulong p, bool is_routine); + GRANT_NAME (TABLE *form, bool is_routine); virtual ~GRANT_NAME() {}; virtual bool ok() { return privs != 0; } + void set_user_details(const char *h, const char *d, + const char *u, const char *t, + bool is_routine); }; @@ -2273,38 +2275,48 @@ public: }; - -GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u, - const char *t, ulong p) - :privs(p) +void GRANT_NAME::set_user_details(const char *h, const char *d, + const char *u, const char *t, + bool is_routine) { /* Host given by user */ update_hostname(&host, strdup_root(&memex, h)); - db = strdup_root(&memex,d); + if (db != d) + { + db= strdup_root(&memex, d); + if (lower_case_table_names) + my_casedn_str(files_charset_info, db); + } user = strdup_root(&memex,u); sort= get_sort(3,host.hostname,db,user); - tname= strdup_root(&memex,t); - if (lower_case_table_names) + if (tname != t) { - my_casedn_str(files_charset_info, db); - my_casedn_str(files_charset_info, tname); + tname= strdup_root(&memex, t); + if (lower_case_table_names || is_routine) + my_casedn_str(files_charset_info, tname); } key_length= strlen(d) + strlen(u)+ strlen(t)+3; hash_key= (char*) alloc_root(&memex,key_length); strmov(strmov(strmov(hash_key,user)+1,db)+1,tname); } +GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u, + const char *t, ulong p, bool is_routine) + :db(0), tname(0), privs(p) +{ + set_user_details(h, d, u, t, is_routine); +} GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u, const char *t, ulong p, ulong c) - :GRANT_NAME(h,d,u,t,p), cols(c) + :GRANT_NAME(h,d,u,t,p, FALSE), cols(c) { (void) my_hash_init2(&hash_columns,4,system_charset_info, 0,0,0, (my_hash_get_key) get_key_column,0,0); } -GRANT_NAME::GRANT_NAME(TABLE *form) +GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine) { update_hostname(&host, get_field(&memex, form->field[0])); db= get_field(&memex,form->field[1]); @@ -2322,6 +2334,9 @@ GRANT_NAME::GRANT_NAME(TABLE *form) if (lower_case_table_names) { my_casedn_str(files_charset_info, db); + } + if (lower_case_table_names || is_routine) + { my_casedn_str(files_charset_info, tname); } key_length= (strlen(db) + strlen(user) + strlen(tname) + 3); @@ -2333,7 +2348,7 @@ GRANT_NAME::GRANT_NAME(TABLE *form) GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) - :GRANT_NAME(form) + :GRANT_NAME(form, FALSE) { uchar key[MAX_KEY_LENGTH]; @@ -3330,7 +3345,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, } grant_name= new GRANT_NAME(Str->host.str, db_name, Str->user.str, table_name, - rights); + rights, TRUE); if (!grant_name) { result= TRUE; @@ -3541,10 +3556,10 @@ static my_bool grant_load_procs_priv(TABLE *p_table) MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC); DBUG_ENTER("grant_load_procs_priv"); - (void) my_hash_init(&proc_priv_hash,system_charset_info, + (void) my_hash_init(&proc_priv_hash, &my_charset_utf8_bin, 0,0,0, (my_hash_get_key) get_grant_table, 0,0); - (void) my_hash_init(&func_priv_hash,system_charset_info, + (void) my_hash_init(&func_priv_hash, &my_charset_utf8_bin, 0,0,0, (my_hash_get_key) get_grant_table, 0,0); p_table->file->ha_index_init(0, 1); @@ -3558,7 +3573,7 @@ static my_bool grant_load_procs_priv(TABLE *p_table) { GRANT_NAME *mem_check; HASH *hash; - if (!(mem_check=new (memex_ptr) GRANT_NAME(p_table))) + if (!(mem_check=new (memex_ptr) GRANT_NAME(p_table, TRUE))) { /* This could only happen if we are out memory */ goto end_unlock; @@ -3642,7 +3657,7 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables) thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH; - (void) my_hash_init(&column_priv_hash,system_charset_info, + (void) my_hash_init(&column_priv_hash, &my_charset_utf8_bin, 0,0,0, (my_hash_get_key) get_grant_table, (my_hash_free_key) free_grant_table,0); @@ -5549,9 +5564,21 @@ static int handle_grant_struct(uint struct_no, bool drop, case 2: case 3: - grant_name->user= strdup_root(&mem, user_to->user.str); - update_hostname(&grant_name->host, - strdup_root(&mem, user_to->host.str)); + /* + Update the grant structure with the new user name and + host name + */ + grant_name->set_user_details(user_to->host.str, grant_name->db, + user_to->user.str, grant_name->tname, + TRUE); + + /* + Since username is part of the hash key, when the user name + is renamed, the hash key is changed. Update the hash to + ensure that the position matches the new hash key value + */ + my_hash_update(&column_priv_hash, (uchar*) grant_name, + (uchar*) grant_name->hash_key, grant_name->key_length); break; } } @@ -6238,7 +6265,7 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, for (counter= 0, revoked= 0 ; counter < hash->records ; ) { GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter); - if (!my_strcasecmp(system_charset_info, grant_proc->db, sp_db) && + if (!my_strcasecmp(&my_charset_utf8_bin, grant_proc->db, sp_db) && !my_strcasecmp(system_charset_info, grant_proc->tname, sp_name)) { LEX_USER lex_user; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 072c7c2aede..2c0ba87262a 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -26,6 +26,7 @@ #include <m_ctype.h> #include <my_dir.h> #include <hash.h> +#include "rpl_filter.h" #ifdef __WIN__ #include <io.h> #endif @@ -1555,6 +1556,7 @@ void close_temporary_tables(THD *thd) s_query.length() - 1 /* to remove trailing ',' */, 0, FALSE, 0); qinfo.db= db.ptr(); + qinfo.db_len= db.length(); thd->variables.character_set_client= cs_save; mysql_bin_log.write(&qinfo); thd->variables.pseudo_thread_id= save_pseudo_thread_id; @@ -5105,7 +5107,16 @@ static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table) int decide_logging_format(THD *thd, TABLE_LIST *tables) { - if (mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG)) + /* + In SBR mode, we are only proceeding if we are binlogging this + statement, ie, the filtering rules won't later filter this out. + + This check here is needed to prevent some spurious error to be + raised in some cases (See BUG#42829). + */ + if (mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG) && + (thd->variables.binlog_format != BINLOG_FORMAT_STMT || + binlog_filter->db_ok(thd->db))) { /* Compute the starting vectors for the computations by creating a diff --git a/sql/sql_class.cc b/sql/sql_class.cc index f05dd622c19..fb7c27f6267 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2804,9 +2804,11 @@ bool select_dumpvar::send_data(List<Item> &items) else { Item_func_set_user_var *suv= new Item_func_set_user_var(mv->s, item); - suv->fix_fields(thd, 0); + if (suv->fix_fields(thd, 0)) + DBUG_RETURN (1); suv->save_item_result(item); - suv->update(); + if (suv->update()) + DBUG_RETURN (1); } } DBUG_RETURN(thd->is_error()); @@ -3124,6 +3126,11 @@ extern "C" void thd_mark_transaction_to_rollback(MYSQL_THD thd, bool all) { mark_transaction_to_rollback(thd, all); } + +extern "C" bool thd_binlog_filter_ok(const MYSQL_THD thd) +{ + return binlog_filter->db_ok(thd->db); +} #endif // INNODB_COMPATIBILITY_HOOKS */ /**************************************************************************** @@ -3296,6 +3303,16 @@ void mark_transaction_to_rollback(THD *thd, bool all) { thd->is_fatal_sub_stmt_error= TRUE; thd->transaction_rollback_request= all; + /* + Aborted transactions can not be IGNOREd. + Switch off the IGNORE flag for the current + SELECT_LEX. This should allow my_error() + to report the error and abort the execution + flow, even in presence + of IGNORE clause. + */ + if (thd->lex->current_select) + thd->lex->current_select->no_error= FALSE; } } /*************************************************************************** diff --git a/sql/sql_class.h b/sql/sql_class.h index f72736ab692..255d7a4d63a 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -55,6 +55,8 @@ extern char internal_table_name[2]; extern char empty_c_string[1]; extern MYSQL_PLUGIN_IMPORT const char **errmesg; +extern bool volatile shutdown_in_progress; + #define TC_LOG_PAGE_SIZE 8192 #define TC_LOG_MIN_SIZE (3*TC_LOG_PAGE_SIZE) @@ -2034,7 +2036,11 @@ public: { int err= killed_errno(); if (err) + { + if ((err == KILL_CONNECTION) && !shutdown_in_progress) + err = KILL_QUERY; my_message(err, ER(err), MYF(0)); + } } /* return TRUE if we will abort query if we make a warning now */ inline bool really_abort_on_warning() diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 9c7433a5f82..34e899fc536 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -117,7 +117,7 @@ static int read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, #ifndef EMBEDDED_LIBRARY static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex, - const char* db_arg, + const char* db_arg, /* table's database */ const char* table_name_arg, enum enum_duplicates duplicates, bool ignore, @@ -538,7 +538,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, if (thd->transaction.stmt.modified_non_trans_table) write_execute_load_query_log_event(thd, ex, - tdb, table_list->table_name, + table_list->db, + table_list->table_name, handle_duplicates, ignore, transactional_table, errcode); @@ -586,7 +587,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, { int errcode= query_error_code(thd, killed_status == THD::NOT_KILLED); write_execute_load_query_log_event(thd, ex, - tdb, table_list->table_name, + table_list->db, table_list->table_name, handle_duplicates, ignore, transactional_table, errcode); @@ -611,7 +612,7 @@ err: /* Not a very useful function; just to avoid duplication of code */ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex, - const char* db_arg, + const char* db_arg, /* table's database */ const char* table_name_arg, enum enum_duplicates duplicates, bool ignore, @@ -628,8 +629,27 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex, Item *item, *val; String pfield, pfields; int n; + const char *tbl= table_name_arg; + const char *tdb= (thd->db != NULL ? thd->db : db_arg); + String string_buf; - Load_log_event lle(thd, ex, db_arg, table_name_arg, fv, duplicates, + if (!thd->db || strcmp(db_arg, thd->db)) + { + /* + If used database differs from table's database, + prefix table name with database name so that it + becomes a FQ name. + */ + string_buf.set_charset(system_charset_info); + string_buf.append(db_arg); + string_buf.append("`"); + string_buf.append("."); + string_buf.append("`"); + string_buf.append(table_name_arg); + tbl= string_buf.c_ptr_safe(); + } + + Load_log_event lle(thd, ex, tdb, tbl, fv, duplicates, ignore, transactional_table); /* diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 84ae17648c7..d989cbe3522 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -124,6 +124,14 @@ static bool xa_trans_rolled_back(XID_STATE *xid_state) */ static bool xa_trans_rollback(THD *thd) { + /* + Resource Manager error is meaningless at this point, as we perform + explicit rollback request by user. We must reset rm_error before + calling ha_rollback(), so thd->transaction.xid structure gets reset + by ha_rollback()/THD::transaction::cleanup(). + */ + thd->transaction.xid_state.rm_error= 0; + bool status= test(ha_rollback(thd)); thd->options&= ~(ulong) OPTION_BEGIN; @@ -131,7 +139,6 @@ static bool xa_trans_rollback(THD *thd) thd->server_status&= ~SERVER_STATUS_IN_TRANS; xid_cache_delete(&thd->transaction.xid_state); thd->transaction.xid_state.xa_state= XA_NOTR; - thd->transaction.xid_state.rm_error= 0; return status; } diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index b328de40c10..a4da72c0948 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -2065,7 +2065,7 @@ static int check_func_set(THD *thd, struct st_mysql_sys_var *var, const char *strvalue= "NULL", *str; TYPELIB *typelib; ulonglong result; - uint error_len; + uint error_len= 0; // init as only set on error bool not_used; int length; @@ -2664,7 +2664,9 @@ uchar* sys_var_pluginvar::value_ptr(THD *thd, enum_var_type type, { if (!(value & mask)) continue; - str.append(typelib->type_names[i], typelib->type_lengths[i]); + str.append(typelib->type_names[i], typelib->type_lengths + ? typelib->type_lengths[i] + : strlen(typelib->type_names[i])); str.append(','); } diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index af762d61c51..55b97a09617 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2389,6 +2389,9 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) /* Fix ORDER list */ for (order= (ORDER *)sl->order_list.first; order; order= order->next) order->item= &order->item_ptr; + + /* clear the no_error flag for INSERT/UPDATE IGNORE */ + sl->no_error= FALSE; } { SELECT_LEX_UNIT *unit= sl->master_unit(); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 549fbb653d1..73bdf63511f 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -637,6 +637,18 @@ JOIN::prepare(Item ***rref_pointer_array, MYF(0)); /* purecov: inspected */ goto err; /* purecov: inspected */ } + if (thd->lex->derived_tables) + { + my_error(ER_WRONG_USAGE, MYF(0), "PROCEDURE", + thd->lex->derived_tables & DERIVED_VIEW ? + "view" : "subquery"); + goto err; + } + if (thd->lex->sql_command != SQLCOM_SELECT) + { + my_error(ER_WRONG_USAGE, MYF(0), "PROCEDURE", "non-SELECT"); + goto err; + } } if (!procedure && result && result->prepare(fields_list, unit_arg)) @@ -973,6 +985,12 @@ JOIN::optimize() DBUG_RETURN(1); } + if (select_lex->olap == ROLLUP_TYPE && rollup_process_const_fields()) + { + DBUG_PRINT("error", ("Error: rollup_process_fields() failed")); + DBUG_RETURN(1); + } + /* Remove distinct if only const tables */ select_distinct= select_distinct && (const_tables != tables); thd_proc_info(thd, "preparing"); @@ -1103,7 +1121,7 @@ JOIN::optimize() join_tab[const_tables].select->quick->get_type() != QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)) { - if (group_list && + if (group_list && rollup.state == ROLLUP::STATE_NONE && list_contains_unique_index(join_tab[const_tables].table, find_field_in_order_list, (void *) group_list)) @@ -1147,7 +1165,8 @@ JOIN::optimize() if (! hidden_group_fields && rollup.state == ROLLUP::STATE_NONE) select_distinct=0; } - else if (select_distinct && tables - const_tables == 1) + else if (select_distinct && tables - const_tables == 1 && + rollup.state == ROLLUP::STATE_NONE) { /* We are only using one table. In this case we change DISTINCT to a @@ -3587,7 +3606,7 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array,KEY_FIELD *key_field) { if (!(form->keys_in_use_for_query.is_set(key))) continue; - if (form->key_info[key].flags & HA_FULLTEXT) + if (form->key_info[key].flags & (HA_FULLTEXT | HA_SPATIAL)) continue; // ToDo: ft-keys in non-ft queries. SerG uint key_parts= (uint) form->key_info[key].key_parts; @@ -9071,7 +9090,10 @@ static void restore_prev_nj_state(JOIN_TAB *last) join->cur_embedding_map&= ~last_emb->nested_join->nj_map; else if (last_emb->nested_join->join_list.elements-1 == last_emb->nested_join->counter) + { join->cur_embedding_map|= last_emb->nested_join->nj_map; + break; + } else break; last_emb= last_emb->embedding; @@ -9518,8 +9540,47 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table, new_field->set_derivation(item->collation.derivation); break; case DECIMAL_RESULT: - new_field= Field_new_decimal::new_decimal_field(item); + { + uint8 dec= item->decimals; + uint8 intg= ((Item_decimal *) item)->decimal_precision() - dec; + uint32 len= item->max_length; + + /* + Trying to put too many digits overall in a DECIMAL(prec,dec) + will always throw a warning. We must limit dec to + DECIMAL_MAX_SCALE however to prevent an assert() later. + */ + + if (dec > 0) + { + signed int overflow; + + dec= min(dec, DECIMAL_MAX_SCALE); + + /* + If the value still overflows the field with the corrected dec, + we'll throw out decimals rather than integers. This is still + bad and of course throws a truncation warning. + +1: for decimal point + */ + + const int required_length= + my_decimal_precision_to_length(intg + dec, dec, + item->unsigned_flag); + + overflow= required_length - len; + + if (overflow > 0) + dec= max(0, dec - overflow); // too long, discard fract + else + /* Corrected value fits. */ + len= required_length; + } + + new_field= new Field_new_decimal(len, maybe_null, item->name, + dec, item->unsigned_flag); break; + } case ROW_RESULT: default: // This case should never be choosen @@ -10321,6 +10382,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, for (; cur_group ; cur_group= cur_group->next, key_part_info++) { Field *field=(*cur_group->item)->get_tmp_table_field(); + DBUG_ASSERT(field->table == table); bool maybe_null=(*cur_group->item)->maybe_null; key_part_info->null_bit=0; key_part_info->field= field; @@ -11302,6 +11364,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, bool not_used_in_distinct=join_tab->not_used_in_distinct; ha_rows found_records=join->found_records; COND *select_cond= join_tab->select_cond; + bool select_cond_result= TRUE; if (error > 0 || (join->thd->is_error())) // Fatal error return NESTED_LOOP_ERROR; @@ -11313,7 +11376,17 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, return NESTED_LOOP_KILLED; /* purecov: inspected */ } DBUG_PRINT("info", ("select cond 0x%lx", (ulong)select_cond)); - if (!select_cond || select_cond->val_int()) + + if (select_cond) + { + select_cond_result= test(select_cond->val_int()); + + /* check for errors evaluating the condition */ + if (join->thd->is_error()) + return NESTED_LOOP_ERROR; + } + + if (!select_cond || select_cond_result) { /* There is no select condition or the attached pushed down @@ -15766,32 +15839,7 @@ bool JOIN::rollup_init() { item->maybe_null= 1; found_in_group= 1; - if (item->const_item()) - { - /* - For ROLLUP queries each constant item referenced in GROUP BY list - is wrapped up into an Item_func object yielding the same value - as the constant item. The objects of the wrapper class are never - considered as constant items and besides they inherit all - properties of the Item_result_field class. - This wrapping allows us to ensure writing constant items - into temporary tables whenever the result of the ROLLUP - operation has to be written into a temporary table, e.g. when - ROLLUP is used together with DISTINCT in the SELECT list. - Usually when creating temporary tables for a intermidiate - result we do not include fields for constant expressions. - */ - Item* new_item= new Item_func_rollup_const(item); - if (!new_item) - return 1; - new_item->fix_fields(thd, (Item **) 0); - thd->change_item_tree(it.ref(), new_item); - for (ORDER *tmp= group_tmp; tmp; tmp= tmp->next) - { - if (*tmp->item == item) - thd->change_item_tree(tmp->item, new_item); - } - } + break; } } if (item->type() == Item::FUNC_ITEM && !found_in_group) @@ -15810,6 +15858,59 @@ bool JOIN::rollup_init() } return 0; } + +/** + Wrap all constant Items in GROUP BY list. + + For ROLLUP queries each constant item referenced in GROUP BY list + is wrapped up into an Item_func object yielding the same value + as the constant item. The objects of the wrapper class are never + considered as constant items and besides they inherit all + properties of the Item_result_field class. + This wrapping allows us to ensure writing constant items + into temporary tables whenever the result of the ROLLUP + operation has to be written into a temporary table, e.g. when + ROLLUP is used together with DISTINCT in the SELECT list. + Usually when creating temporary tables for a intermidiate + result we do not include fields for constant expressions. + + @retval + 0 if ok + @retval + 1 on error +*/ + +bool JOIN::rollup_process_const_fields() +{ + ORDER *group_tmp; + Item *item; + List_iterator<Item> it(all_fields); + + for (group_tmp= group_list; group_tmp; group_tmp= group_tmp->next) + { + if (!(*group_tmp->item)->const_item()) + continue; + while ((item= it++)) + { + if (*group_tmp->item == item) + { + Item* new_item= new Item_func_rollup_const(item); + if (!new_item) + return 1; + new_item->fix_fields(thd, (Item **) 0); + thd->change_item_tree(it.ref(), new_item); + for (ORDER *tmp= group_tmp; tmp; tmp= tmp->next) + { + if (*tmp->item == item) + thd->change_item_tree(tmp->item, new_item); + } + break; + } + } + it.rewind(); + } + return 0; +} /** diff --git a/sql/sql_select.h b/sql/sql_select.h index 853931f437a..76b3d1717f4 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -519,6 +519,7 @@ public: } bool rollup_init(); + bool rollup_process_const_fields(); bool rollup_make_fields(List<Item> &all_fields, List<Item> &fields, Item_sum ***func); int rollup_send_data(uint idx); diff --git a/sql/sql_update.cc b/sql/sql_update.cc index f5f50f32b45..e7b4eb22e78 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1703,6 +1703,11 @@ bool multi_update::send_data(List<Item> ¬_used_values) TRG_EVENT_UPDATE)) DBUG_RETURN(1); + /* + Reset the table->auto_increment_field_not_null as it is valid for + only one row. + */ + table->auto_increment_field_not_null= FALSE; found++; if (!can_compare_record || compare_record(table)) { diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index e5dfc1104e0..a0026183245 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -9446,8 +9446,7 @@ procedure_clause: MYSQL_YYABORT; } - if (&lex->select_lex != lex->current_select || - lex->select_lex.get_table_list()->derived) + if (&lex->select_lex != lex->current_select) { my_error(ER_WRONG_USAGE, MYF(0), "PROCEDURE", "subquery"); MYSQL_YYABORT; diff --git a/sql/time.cc b/sql/time.cc index 810d6426a01..5f804072eb0 100644 --- a/sql/time.cc +++ b/sql/time.cc @@ -965,20 +965,22 @@ calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, longlong *s 0 - a == b 1 - a > b - NOTES - TIME.second_part is not considered during comparison */ -int -my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b) +int my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b) { - my_ulonglong a_t= TIME_to_ulonglong_datetime(a); - my_ulonglong b_t= TIME_to_ulonglong_datetime(b); + ulonglong a_t= TIME_to_ulonglong_datetime(a); + ulonglong b_t= TIME_to_ulonglong_datetime(b); + if (a_t < b_t) + return -1; if (a_t > b_t) return 1; - else if (a_t < b_t) + + if (a->second_part < b->second_part) return -1; + if (a->second_part > b->second_part) + return 1; return 0; } |