diff options
author | Sergey Petrunia <sergefp@mysql.com> | 2008-12-22 23:28:08 +0300 |
---|---|---|
committer | Sergey Petrunia <sergefp@mysql.com> | 2008-12-22 23:28:08 +0300 |
commit | 7658444b9716497fcbad8de74478f52ba3f37ca9 (patch) | |
tree | f5fec9ef87d487099c56f83728de6901395388a1 /sql | |
parent | 7976735a0c34a35a7f726a2e985f53582db09282 (diff) | |
parent | 45c97126aff027e6179f850cd5d9793af3d9ccc8 (diff) | |
download | mariadb-git-7658444b9716497fcbad8de74478f52ba3f37ca9.tar.gz |
Merge
Diffstat (limited to 'sql')
36 files changed, 635 insertions, 204 deletions
diff --git a/sql/examples/ha_tina.cc b/sql/examples/ha_tina.cc index 0b57fe86e62..aaaa3b8ffb4 100644 --- a/sql/examples/ha_tina.cc +++ b/sql/examples/ha_tina.cc @@ -416,37 +416,96 @@ int ha_tina::find_current_row(byte *buf) if ((end_ptr= find_eoln(share->mapped_file, current_position, share->file_stat.st_size)) == 0) DBUG_RETURN(HA_ERR_END_OF_FILE); + /* + Parse the line obtained using the following algorithm + + BEGIN + 1) Store the EOL (end of line) for the current row + 2) Until all the fields in the current query have not been + filled + 2.1) If the current character begins with a quote + 2.1.1) Until EOL has not been reached + a) If end of current field is reached, move + to next field and jump to step 2.3 + b) If current character begins with \\ handle + \\n, \\r, \\, \\" + c) else append the current character into the buffer + before checking that EOL has not been reached. + 2.2) If the current character does not begin with a quote + 2.2.1) Until EOL has not been reached + a) If the end of field has been reached move to the + next field and jump to step 2.3 + b) append the current character into the buffer + 2.3) Store the current field value and jump to 2) + TERMINATE + */ + for (Field **field=table->field ; *field ; field++) { buffer.length(0); - mapped_ptr++; // Increment past the first quote - for(;mapped_ptr != end_ptr; mapped_ptr++) + /* Handle the case where the first character begins with a quote */ + if (*mapped_ptr == '"') { - //Need to convert line feeds! - if (*mapped_ptr == '"' && - (((mapped_ptr[1] == ',') && (mapped_ptr[2] == '"')) || (mapped_ptr == end_ptr -1 ))) + /* Increment past the first quote */ + mapped_ptr++; + /* Loop through the row to extract the values for the current field */ + for(; mapped_ptr != end_ptr; mapped_ptr++) { - mapped_ptr += 2; // Move past the , and the " - break; - } - if (*mapped_ptr == '\\' && mapped_ptr != (end_ptr - 1)) - { - mapped_ptr++; - if (*mapped_ptr == 'r') - buffer.append('\r'); - else if (*mapped_ptr == 'n' ) - buffer.append('\n'); - else if ((*mapped_ptr == '\\') || (*mapped_ptr == '"')) - buffer.append(*mapped_ptr); - else /* This could only happed with an externally created file */ + /* check for end of the current field */ + if (*mapped_ptr == '"' && + (mapped_ptr[1] == ',' || mapped_ptr == end_ptr -1 )) { - buffer.append('\\'); + /* Move past the , and the " */ + mapped_ptr += 2; + break; + } + if (*mapped_ptr == '\\' && mapped_ptr != (end_ptr - 1)) + { + mapped_ptr++; + if (*mapped_ptr == 'r') + buffer.append('\r'); + else if (*mapped_ptr == 'n' ) + buffer.append('\n'); + else if ((*mapped_ptr == '\\') || (*mapped_ptr == '"')) + buffer.append(*mapped_ptr); + else /* This could only happed with an externally created file */ + { + buffer.append('\\'); + buffer.append(*mapped_ptr); + } + } + else + { + /* + If no last quote was found, but the end of row has been reached + it implies that there has been error. + */ + if (mapped_ptr == end_ptr -1) + DBUG_RETURN(HA_ERR_END_OF_FILE); + /* Store current character in the buffer for the field */ buffer.append(*mapped_ptr); } - } - else + } + } + else + { + /* Handle the case where the current row does not start with quotes */ + + /* Loop through the row to extract the values for the current field */ + for (; mapped_ptr != end_ptr; mapped_ptr++) + { + /* check for end of current field */ + if (*mapped_ptr == ',') + { + /* Increment past the current comma */ + mapped_ptr++; + break; + } + /* store the current character in the buffer for the field */ buffer.append(*mapped_ptr); + } } + /* Store the field value from the buffer */ (*field)->store(buffer.ptr(), buffer.length(), buffer.charset()); } next_position= (end_ptr - share->mapped_file)+1; diff --git a/sql/field.cc b/sql/field.cc index 3d3f698f912..f8ab4b852ec 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -3473,7 +3473,7 @@ int Field_longlong::store(double nr) error= 1; } else - res=(longlong) (ulonglong) nr; + res=(longlong) double2ulonglong(nr); } else { @@ -5372,6 +5372,7 @@ int Field_newdate::store_time(MYSQL_TIME *ltime, timestamp_type time_type) { char buff[MAX_DATE_STRING_REP_LENGTH]; String str(buff, sizeof(buff), &my_charset_latin1); + tmp= 0; make_date((DATE_TIME_FORMAT *) 0, ltime, &str); set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, str.ptr(), str.length(), MYSQL_TIMESTAMP_DATE, 1); @@ -5608,6 +5609,7 @@ int Field_datetime::store_time(MYSQL_TIME *ltime,timestamp_type time_type) { char buff[MAX_DATE_STRING_REP_LENGTH]; String str(buff, sizeof(buff), &my_charset_latin1); + tmp= 0; make_datetime((DATE_TIME_FORMAT *) 0, ltime, &str); set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, str.ptr(), str.length(), MYSQL_TIMESTAMP_DATETIME,1); diff --git a/sql/ha_federated.cc b/sql/ha_federated.cc index b4788dd9c87..d4144a41a2a 100644 --- a/sql/ha_federated.cc +++ b/sql/ha_federated.cc @@ -1320,6 +1320,14 @@ static FEDERATED_SHARE *get_share(const char *table_name, TABLE *table) thr_lock_init(&share->lock); pthread_mutex_init(&share->mutex, MY_MUTEX_INIT_FAST); } + else + { + /* + Free tmp_share.scheme allocated in the parse_url() + as we found share in the hash and tmp_share isn't needed anymore. + */ + my_free((gptr) tmp_share.scheme, MYF(MY_ALLOW_ZERO_PTR)); + } share->use_count++; pthread_mutex_unlock(&federated_mutex); diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 1c0f8a6e9b3..83e2d025f18 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -2158,6 +2158,14 @@ ha_innobase::open( UT_NOT_USED(test_if_locked); thd = current_thd; + + /* Under some cases MySQL seems to call this function while + holding btr_search_latch. This breaks the latching order as + we acquire dict_sys->mutex below and leads to a deadlock. */ + if (thd != NULL) { + innobase_release_temporary_latches(thd); + } + normalize_table_name(norm_name, name); user_thd = NULL; diff --git a/sql/handler.cc b/sql/handler.cc index 67ec5f3e759..71d184ad84b 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1957,8 +1957,53 @@ bool handler::get_error_message(int error, String* buf) } +/** + Check for incompatible collation changes. + + @retval + HA_ADMIN_NEEDS_UPGRADE Table may have data requiring upgrade. + @retval + 0 No upgrade required. +*/ + +int handler::check_collation_compatibility() +{ + ulong mysql_version= table->s->mysql_version; + + if (mysql_version < 50048) + { + KEY *key= table->key_info; + KEY *key_end= key + table->s->keys; + for (; key < key_end; key++) + { + KEY_PART_INFO *key_part= key->key_part; + KEY_PART_INFO *key_part_end= key_part + key->key_parts; + for (; key_part < key_part_end; key_part++) + { + if (!key_part->fieldnr) + continue; + Field *field= table->field[key_part->fieldnr - 1]; + uint cs_number= field->charset()->number; + if (mysql_version < 50048 && + (cs_number == 11 || /* ascii_general_ci - bug #29499, bug #27562 */ + cs_number == 41 || /* latin7_general_ci - bug #29461 */ + cs_number == 42 || /* latin7_general_cs - bug #29461 */ + cs_number == 20 || /* latin7_estonian_cs - bug #29461 */ + cs_number == 21 || /* latin2_hungarian_ci - bug #29461 */ + cs_number == 22 || /* koi8u_general_ci - bug #29461 */ + cs_number == 23 || /* cp1251_ukrainian_ci - bug #29461 */ + cs_number == 26)) /* cp1250_general_ci - bug #29461 */ + return HA_ADMIN_NEEDS_UPGRADE; + } + } + } + return 0; +} + + int handler::ha_check_for_upgrade(HA_CHECK_OPT *check_opt) { + int error; KEY *keyinfo, *keyend; KEY_PART_INFO *keypart, *keypartend; @@ -1987,6 +2032,10 @@ int handler::ha_check_for_upgrade(HA_CHECK_OPT *check_opt) } if (table->s->frm_version != FRM_VER_TRUE_VARCHAR) return HA_ADMIN_NEEDS_ALTER; + + if ((error= check_collation_compatibility())) + return error; + return check_for_upgrade(check_opt); } diff --git a/sql/handler.h b/sql/handler.h index aa3377c3868..8706aae5fce 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -787,6 +787,7 @@ protected: virtual int check_for_upgrade(HA_CHECK_OPT *check_opt) { return 0; } public: + int check_collation_compatibility(); int ha_check_for_upgrade(HA_CHECK_OPT *check_opt); int check_old_types(); /* to be actually called to get 'check()' functionality*/ diff --git a/sql/item.cc b/sql/item.cc index 182f4abdfe6..2a89c86cd88 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -4950,6 +4950,9 @@ int Item_hex_string::save_in_field(Field *field, bool no_conversions) ulonglong nr; uint32 length= str_value.length(); + if (!length) + return 1; + if (length > 8) { nr= field->flags & UNSIGNED_FLAG ? ULONGLONG_MAX : LONGLONG_MAX; @@ -6792,7 +6795,7 @@ enum_field_types Item_type_holder::get_real_type(Item *item) */ Item_sum *item_sum= (Item_sum *) item; if (item_sum->keep_field_type()) - return get_real_type(item_sum->args[0]); + return get_real_type(item_sum->get_arg(0)); break; } case FUNC_ITEM: @@ -7056,7 +7059,7 @@ void Item_type_holder::get_full_info(Item *item) if (item->type() == Item::SUM_FUNC_ITEM && (((Item_sum*)item)->sum_func() == Item_sum::MAX_FUNC || ((Item_sum*)item)->sum_func() == Item_sum::MIN_FUNC)) - item = ((Item_sum*)item)->args[0]; + item = ((Item_sum*)item)->get_arg(0); /* We can have enum/set type after merging only if we have one enum|set field (or MIN|MAX(enum|set field)) and number of NULL fields diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 0410c781590..3b1d18b4252 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -745,11 +745,11 @@ Arg_comparator::can_compare_as_dates(Item *a, Item *b, ulonglong *const_value) obtained value */ -ulonglong +longlong get_time_value(THD *thd, Item ***item_arg, Item **cache_arg, Item *warn_item, bool *is_null) { - ulonglong value; + longlong value; Item *item= **item_arg; MYSQL_TIME ltime; @@ -761,7 +761,7 @@ get_time_value(THD *thd, Item ***item_arg, Item **cache_arg, else { *is_null= item->get_time(<ime); - value= !*is_null ? TIME_to_ulonglong_datetime(<ime) : 0; + value= !*is_null ? (longlong) TIME_to_ulonglong_datetime(<ime) : 0; } /* Do not cache GET_USER_VAR() function as its const_item() may return TRUE @@ -886,11 +886,11 @@ void Arg_comparator::set_datetime_cmp_func(Item **a1, Item **b1) obtained value */ -ulonglong +longlong get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, Item *warn_item, bool *is_null) { - ulonglong value= 0; + longlong value= 0; String buf, *str= 0; Item *item= **item_arg; @@ -925,7 +925,7 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, enum_field_types f_type= warn_item->field_type(); timestamp_type t_type= f_type == MYSQL_TYPE_DATE ? MYSQL_TIMESTAMP_DATE : MYSQL_TIMESTAMP_DATETIME; - value= get_date_from_str(thd, str, t_type, warn_item->name, &error); + value= (longlong) get_date_from_str(thd, str, t_type, warn_item->name, &error); /* If str did not contain a valid date according to the current SQL_MODE, get_date_from_str() has already thrown a warning, @@ -979,7 +979,7 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, int Arg_comparator::compare_datetime() { bool a_is_null, b_is_null; - ulonglong a_value, b_value; + longlong a_value, b_value; /* Get DATE/DATETIME/TIME value of the 'a' item. */ a_value= (*get_value_func)(thd, &a, &a_cache, *b, &a_is_null); @@ -1434,7 +1434,8 @@ bool Item_in_optimizer::fix_left(THD *thd, Item **ref) } not_null_tables_cache= args[0]->not_null_tables(); with_sum_func= args[0]->with_sum_func; - const_item_cache= args[0]->const_item(); + if ((const_item_cache= args[0]->const_item())) + cache->store(args[0]); return 0; } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 1bd60ff37d9..db831c7030c 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -42,8 +42,8 @@ class Arg_comparator: public Sql_alloc bool is_nulls_eq; // TRUE <=> compare for the EQUAL_FUNC enum enum_date_cmp_type { CMP_DATE_DFLT= 0, CMP_DATE_WITH_DATE, CMP_DATE_WITH_STR, CMP_STR_WITH_DATE }; - ulonglong (*get_value_func)(THD *thd, Item ***item_arg, Item **cache_arg, - Item *warn_item, bool *is_null); + longlong (*get_value_func)(THD *thd, Item ***item_arg, Item **cache_arg, + Item *warn_item, bool *is_null); public: DTCollation cmp_collation; @@ -1028,7 +1028,7 @@ public: */ class cmp_item_datetime :public cmp_item { - ulonglong value; + longlong value; public: THD *thd; /* Item used for issuing warnings. */ diff --git a/sql/item_func.cc b/sql/item_func.cc index e663e1fcf83..c0d08d9b213 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -1316,8 +1316,10 @@ my_decimal *Item_func_div::decimal_op(my_decimal *decimal_value) void Item_func_div::result_precision() { - uint arg_prec= args[0]->decimal_precision() + prec_increment; - uint precision=min(arg_prec, DECIMAL_MAX_PRECISION); + uint precision=min(args[0]->decimal_precision() + + args[1]->decimals + prec_increment, + DECIMAL_MAX_PRECISION); + /* Integer operations keep unsigned_flag if one of arguments is unsigned */ if (result_type() == INT_RESULT) unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag; @@ -2273,7 +2275,7 @@ void Item_func_min_max::fix_length_and_dec() uint Item_func_min_max::cmp_datetimes(ulonglong *value) { - ulonglong min_max; + longlong min_max; uint min_max_idx= 0; LINT_INIT(min_max); @@ -2281,7 +2283,7 @@ uint Item_func_min_max::cmp_datetimes(ulonglong *value) { Item **arg= args + i; bool is_null; - ulonglong res= get_datetime_value(thd, &arg, 0, datetime_item, &is_null); + longlong res= get_datetime_value(thd, &arg, 0, datetime_item, &is_null); if ((null_value= args[i]->null_value)) return 0; if (i == 0 || (res < min_max ? cmp_sign : -cmp_sign) > 0) @@ -5022,7 +5024,9 @@ bool Item_func_match::fix_index() for (keynr=0 ; keynr < table->s->keys ; keynr++) { if ((table->key_info[keynr].flags & HA_FULLTEXT) && - (table->s->keys_in_use.is_set(keynr))) + (flags & FT_BOOL ? table->keys_in_use_for_query.is_set(keynr) : + table->s->keys_in_use.is_set(keynr))) + { ft_to_key[fts]=keynr; ft_cnt[fts]=0; diff --git a/sql/item_func.h b/sql/item_func.h index 6dcf32cba07..89c841abb75 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -351,7 +351,10 @@ public: Item_func_unsigned(Item *a) :Item_func_signed(a) {} const char *func_name() const { return "cast_as_unsigned"; } void fix_length_and_dec() - { max_length=args[0]->max_length; unsigned_flag=1; } + { + max_length= min(args[0]->max_length, DECIMAL_MAX_PRECISION + 2); + unsigned_flag=1; + } longlong val_int(); void print(String *str); }; diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 3648438a69b..23ac20a4017 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -516,8 +516,9 @@ public: { collation.set(default_charset()); uint char_length= args[0]->max_length/args[0]->collation.collation->mbmaxlen; - max_length= ((char_length + (char_length-args[0]->decimals)/3) * - collation.collation->mbmaxlen); + uint max_sep_count= char_length/3 + (decimals ? 1 : 0) + /*sign*/1; + max_length= (char_length + max_sep_count + decimals) * + collation.collation->mbmaxlen; } const char *func_name() const { return "format"; } void print(String *); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 91320d6b56b..d33d92a5238 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -370,6 +370,10 @@ Item_sum::Item_sum(List<Item> &list) :arg_count(list.elements), args[i++]= item; } } + if (!(orig_args= (Item **) sql_alloc(sizeof(Item *) * arg_count))) + { + args= NULL; + } mark_as_sum_func(); list.empty(); // Fields are used } @@ -380,18 +384,28 @@ Item_sum::Item_sum(List<Item> &list) :arg_count(list.elements), */ Item_sum::Item_sum(THD *thd, Item_sum *item): - Item_result_field(thd, item), arg_count(item->arg_count), + Item_result_field(thd, item), aggr_sel(item->aggr_sel), nest_level(item->nest_level), aggr_level(item->aggr_level), - quick_group(item->quick_group), used_tables_cache(item->used_tables_cache), + quick_group(item->quick_group), + arg_count(item->arg_count), orig_args(NULL), + used_tables_cache(item->used_tables_cache), forced_const(item->forced_const) { if (arg_count <= 2) + { args=tmp_args; + orig_args=tmp_orig_args; + } else + { if (!(args= (Item**) thd->alloc(sizeof(Item*)*arg_count))) return; + if (!(orig_args= (Item**) thd->alloc(sizeof(Item*)*arg_count))) + return; + } memcpy(args, item->args, sizeof(Item*)*arg_count); + memcpy(orig_args, item->orig_args, sizeof(Item*)*arg_count); } @@ -426,12 +440,13 @@ void Item_sum::make_field(Send_field *tmp_field) void Item_sum::print(String *str) { + Item **pargs= orig_args; str->append(func_name()); for (uint i=0 ; i < arg_count ; i++) { if (i) str->append(','); - args[i]->print(str); + pargs[i]->print(str); } str->append(')'); } @@ -532,6 +547,13 @@ void Item_sum::update_used_tables () } +Item *Item_sum::set_arg(int i, THD *thd, Item *new_val) +{ + thd->change_item_tree(args + i, new_val); + return new_val; +} + + String * Item_sum_num::val_str(String *str) { @@ -583,6 +605,7 @@ Item_sum_num::fix_fields(THD *thd, Item **ref) if (check_sum_func(thd, ref)) return TRUE; + memcpy (orig_args, args, sizeof (Item *) * arg_count); fixed= 1; return FALSE; } @@ -670,6 +693,7 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref) if (check_sum_func(thd, ref)) return TRUE; + orig_args[0]= args[0]; fixed= 1; return FALSE; } @@ -3107,6 +3131,12 @@ Item_func_group_concat(Name_resolution_context *context_arg, sizeof(ORDER*)*arg_count_order))) return; + if (!(orig_args= (Item **) sql_alloc(sizeof(Item *) * arg_count))) + { + args= NULL; + return; + } + order= (ORDER**)(args + arg_count); /* fill args items of show and sort */ @@ -3334,6 +3364,7 @@ Item_func_group_concat::fix_fields(THD *thd, Item **ref) if (check_sum_func(thd, ref)) return TRUE; + memcpy (orig_args, args, sizeof (Item *) * arg_count); fixed= 1; return FALSE; } diff --git a/sql/item_sum.h b/sql/item_sum.h index d39fc96e254..51a1eff9bbf 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -228,10 +228,8 @@ public: VARIANCE_FUNC, SUM_BIT_FUNC, UDF_SUM_FUNC, GROUP_CONCAT_FUNC }; - Item **args, *tmp_args[2]; Item **ref_by; /* pointer to a ref to the object used to register it */ Item_sum *next; /* next in the circular chain of registered objects */ - uint arg_count; Item_sum *in_sum_func; /* embedding set function if any */ st_select_lex * aggr_sel; /* select where the function is aggregated */ int8 nest_level; /* number of the nesting level of the set function */ @@ -248,24 +246,32 @@ public: List<Item_field> outer_fields; protected: + uint arg_count; + Item **args, *tmp_args[2]; + /* + Copy of the arguments list to hold the original set of arguments. + Used in EXPLAIN EXTENDED instead of the current argument list because + the current argument list can be altered by usage of temporary tables. + */ + Item **orig_args, *tmp_orig_args[2]; table_map used_tables_cache; bool forced_const; public: void mark_as_sum_func(); - Item_sum() :arg_count(0), quick_group(1), forced_const(FALSE) + Item_sum() :quick_group(1), arg_count(0), forced_const(FALSE) { mark_as_sum_func(); } - Item_sum(Item *a) :args(tmp_args), arg_count(1), quick_group(1), - forced_const(FALSE) + Item_sum(Item *a) :quick_group(1), arg_count(1), args(tmp_args), + orig_args(tmp_orig_args), forced_const(FALSE) { args[0]=a; mark_as_sum_func(); } - Item_sum( Item *a, Item *b ) :args(tmp_args), arg_count(2), quick_group(1), - forced_const(FALSE) + Item_sum( Item *a, Item *b ) :quick_group(1), arg_count(2), args(tmp_args), + orig_args(tmp_orig_args), forced_const(FALSE) { args[0]=a; args[1]=b; mark_as_sum_func(); @@ -374,6 +380,10 @@ public: bool register_sum_func(THD *thd, Item **ref); st_select_lex *depended_from() { return (nest_level == aggr_level ? 0 : aggr_sel); } + + Item *get_arg(int i) { return args[i]; } + Item *set_arg(int i, THD *thd, Item *new_val); + uint get_arg_count() { return arg_count; } }; @@ -981,6 +991,7 @@ public: if (udf.fix_fields(thd, this, this->arg_count, this->args)) return TRUE; + memcpy (orig_args, args, sizeof (Item *) * arg_count); return check_sum_func(thd, ref); } enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; } diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 0cb3c963dad..e9e92952908 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2550,6 +2550,8 @@ void Item_char_typecast::fix_length_and_dec() and thus avoid unnecessary character set conversion. - If the argument is not a number, then from_cs is set to the argument's charset. + + Note (TODO): we could use repertoire technique here. */ from_cs= (args[0]->result_type() == INT_RESULT || args[0]->result_type() == DECIMAL_RESULT || @@ -2557,12 +2559,13 @@ void Item_char_typecast::fix_length_and_dec() (cast_cs->mbminlen == 1 ? cast_cs : &my_charset_latin1) : args[0]->collation.collation; charset_conversion= (cast_cs->mbmaxlen > 1) || - !my_charset_same(from_cs, cast_cs) && - from_cs != &my_charset_bin && - cast_cs != &my_charset_bin; + (!my_charset_same(from_cs, cast_cs) && + from_cs != &my_charset_bin && + cast_cs != &my_charset_bin); collation.set(cast_cs, DERIVATION_IMPLICIT); - char_length= (cast_length >= 0) ? cast_length : - args[0]->max_length/from_cs->mbmaxlen; + char_length= (cast_length >= 0) ? + cast_length : + args[0]->max_length / args[0]->collation.collation->mbmaxlen; max_length= char_length * cast_cs->mbmaxlen; } diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 7960c03d2e5..81a6c3e98bd 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -408,6 +408,7 @@ public: { return save_time_in_field(field); } + bool result_as_longlong() { return TRUE; } }; diff --git a/sql/log.cc b/sql/log.cc index 5a1cfe46686..c411f7c8238 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -417,6 +417,7 @@ MYSQL_LOG::MYSQL_LOG() index_file_name[0] = 0; bzero((char*) &log_file,sizeof(log_file)); bzero((char*) &index_file, sizeof(index_file)); + bzero((char*) &purge_temp, sizeof(purge_temp)); } /* this is called only once */ @@ -1059,10 +1060,10 @@ err: IMPLEMENTATION - Protects index file with LOCK_index + - Read the next file name from the index file and store in rli->linfo - Delete relevant relay log files - Copy all file names after these ones to the front of the index file - If the OS has truncate, truncate the file, else fill it with \n' - - Read the next file name from the index file and store in rli->linfo RETURN VALUES 0 ok @@ -1076,6 +1077,7 @@ err: int MYSQL_LOG::purge_first_log(struct st_relay_log_info* rli, bool included) { int error; + char *to_purge_if_included= NULL; DBUG_ENTER("purge_first_log"); DBUG_ASSERT(is_open()); @@ -1083,36 +1085,20 @@ int MYSQL_LOG::purge_first_log(struct st_relay_log_info* rli, bool included) DBUG_ASSERT(!strcmp(rli->linfo.log_file_name,rli->event_relay_log_name)); pthread_mutex_lock(&LOCK_index); - pthread_mutex_lock(&rli->log_space_lock); - rli->relay_log.purge_logs(rli->group_relay_log_name, included, - 0, 0, &rli->log_space_total); - // Tell the I/O thread to take the relay_log_space_limit into account - rli->ignore_log_space_limit= 0; - pthread_mutex_unlock(&rli->log_space_lock); + to_purge_if_included= my_strdup(rli->group_relay_log_name, MYF(0)); /* - Ok to broadcast after the critical region as there is no risk of - the mutex being destroyed by this thread later - this helps save - context switches - */ - pthread_cond_broadcast(&rli->log_space_cond); - - /* Read the next log file name from the index file and pass it back to - the caller - If included is true, we want the first relay log; - otherwise we want the one after event_relay_log_name. + the caller. */ - if ((included && (error=find_log_pos(&rli->linfo, NullS, 0))) || - (!included && - ((error=find_log_pos(&rli->linfo, rli->event_relay_log_name, 0)) || - (error=find_next_log(&rli->linfo, 0))))) + if((error=find_log_pos(&rli->linfo, rli->event_relay_log_name, 0)) || + (error=find_next_log(&rli->linfo, 0))) { char buff[22]; sql_print_error("next log error: %d offset: %s log: %s included: %d", error, llstr(rli->linfo.index_file_offset,buff), - rli->group_relay_log_name, + rli->event_relay_log_name, included); goto err; } @@ -1140,7 +1126,42 @@ int MYSQL_LOG::purge_first_log(struct st_relay_log_info* rli, bool included) /* Store where we are in the new file for the execution thread */ flush_relay_log_info(rli); + DBUG_EXECUTE_IF("crash_before_purge_logs", abort();); + + pthread_mutex_lock(&rli->log_space_lock); + rli->relay_log.purge_logs(to_purge_if_included, included, + 0, 0, &rli->log_space_total); + // Tell the I/O thread to take the relay_log_space_limit into account + rli->ignore_log_space_limit= 0; + pthread_mutex_unlock(&rli->log_space_lock); + + /* + Ok to broadcast after the critical region as there is no risk of + the mutex being destroyed by this thread later - this helps save + context switches + */ + pthread_cond_broadcast(&rli->log_space_cond); + + /* + * Need to update the log pos because purge logs has been called + * after fetching initially the log pos at the begining of the method. + */ + if((error=find_log_pos(&rli->linfo, rli->event_relay_log_name, 0))) + { + char buff[22]; + sql_print_error("next log error: %d offset: %s log: %s included: %d", + error, + llstr(rli->linfo.index_file_offset,buff), + rli->group_relay_log_name, + included); + goto err; + } + + /* If included was passed, rli->linfo should be the first entry. */ + DBUG_ASSERT(!included || rli->linfo.index_file_start_offset == 0); + err: + my_free(to_purge_if_included, MYF(0)); pthread_mutex_unlock(&LOCK_index); DBUG_RETURN(error); } @@ -1199,8 +1220,36 @@ int MYSQL_LOG::purge_logs(const char *to_log, if (need_mutex) pthread_mutex_lock(&LOCK_index); - if ((error=find_log_pos(&log_info, to_log, 0 /*no mutex*/))) + if ((error=find_log_pos(&log_info, to_log, 0 /*no mutex*/))) + { + sql_print_error("MYSQL_LOG::purge_logs was called with file %s not " + "listed in the index.", to_log); goto err; + } + + /* + For crash recovery reasons the index needs to be updated before + any files are deleted. Move files to be deleted into a temp file + to be processed after the index is updated. + */ + if (!my_b_inited(&purge_temp)) + { + if ((error=open_cached_file(&purge_temp, mysql_tmpdir, TEMP_PREFIX, + DISK_BUFFER_SIZE, MYF(MY_WME)))) + { + sql_print_error("MYSQL_LOG::purge_logs failed to open purge_temp"); + goto err; + } + } + else + { + if ((error=reinit_io_cache(&purge_temp, WRITE_CACHE, 0, 0, 1))) + { + sql_print_error("MYSQL_LOG::purge_logs failed to reinit purge_temp " + "for write"); + goto err; + } + } /* File name exists in index file; delete until we find this file @@ -1211,6 +1260,59 @@ int MYSQL_LOG::purge_logs(const char *to_log, while ((strcmp(to_log,log_info.log_file_name) || (exit_loop=included)) && !log_in_use(log_info.log_file_name)) { + if ((error=my_b_write(&purge_temp, (byte*)log_info.log_file_name, + strlen(log_info.log_file_name))) || + (error=my_b_write(&purge_temp, (byte*)"\n", 1))) + { + sql_print_error("MYSQL_LOG::purge_logs failed to copy %s to purge_temp", + log_info.log_file_name); + goto err; + } + + if (find_next_log(&log_info, 0) || exit_loop) + break; + } + + /* We know how many files to delete. Update index file. */ + if ((error=update_log_index(&log_info, need_update_threads))) + { + sql_print_error("MSYQL_LOG::purge_logs failed to update the index file"); + goto err; + } + + DBUG_EXECUTE_IF("crash_after_update_index", abort();); + + /* Switch purge_temp for read. */ + if ((error=reinit_io_cache(&purge_temp, READ_CACHE, 0, 0, 0))) + { + sql_print_error("MSYQL_LOG::purge_logs failed to reinit purge_temp " + "for read"); + goto err; + } + + /* Read each entry from purge_temp and delete the file. */ + for (;;) + { + uint length; + + if ((length=my_b_gets(&purge_temp, log_info.log_file_name, + FN_REFLEN)) <= 1) + { + if (purge_temp.error) + { + error= purge_temp.error; + sql_print_error("MSYQL_LOG::purge_logs error %d reading from " + "purge_temp", error); + goto err; + } + + /* Reached EOF */ + break; + } + + /* Get rid of the trailing '\n' */ + log_info.log_file_name[length-1]= 0; + MY_STAT s; if (!my_stat(log_info.log_file_name, &s, MYF(0))) { @@ -1304,17 +1406,10 @@ int MYSQL_LOG::purge_logs(const char *to_log, } } } - if (find_next_log(&log_info, 0) || exit_loop) - break; } - - /* - If we get killed -9 here, the sysadmin would have to edit - the log index file after restart - otherwise, this should be safe - */ - error= update_log_index(&log_info, need_update_threads); err: + close_cached_file(&purge_temp); if (need_mutex) pthread_mutex_unlock(&LOCK_index); DBUG_RETURN(error); @@ -1326,7 +1421,6 @@ err: SYNOPSIS purge_logs_before_date() - thd Thread pointer before_date Delete all log files before given date. NOTES @@ -1343,6 +1437,7 @@ err: int MYSQL_LOG::purge_logs_before_date(time_t purge_time) { int error; + char to_log[FN_REFLEN]; LOG_INFO log_info; MY_STAT stat_area; THD *thd= current_thd; @@ -1350,12 +1445,8 @@ int MYSQL_LOG::purge_logs_before_date(time_t purge_time) DBUG_ENTER("purge_logs_before_date"); pthread_mutex_lock(&LOCK_index); + to_log[0]= 0; - /* - Delete until we find curren file - or a file that is used or a file - that is older than purge_time. - */ if ((error=find_log_pos(&log_info, NullS, 0 /*no mutex*/))) goto err; @@ -1405,54 +1496,18 @@ int MYSQL_LOG::purge_logs_before_date(time_t purge_time) } else { - if (stat_area.st_mtime >= purge_time) + if (stat_area.st_mtime < purge_time) + strmake(to_log, + log_info.log_file_name, + sizeof(log_info.log_file_name)); + else break; - if (my_delete(log_info.log_file_name, MYF(0))) - { - if (my_errno == ENOENT) - { - /* It's not fatal even if we can't delete a log file */ - if (thd) - { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE), - log_info.log_file_name); - } - sql_print_information("Failed to delete file '%s'", - log_info.log_file_name); - my_errno= 0; - } - else - { - if (thd) - { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, - ER_BINLOG_PURGE_FATAL_ERR, - "a problem with deleting %s; " - "consider examining correspondence " - "of your binlog index file " - "to the actual binlog files", - log_info.log_file_name); - } - else - { - sql_print_information("Failed to delete log file '%s'", - log_info.log_file_name); - } - error= LOG_INFO_FATAL; - goto err; - } - } } if (find_next_log(&log_info, 0)) break; } - /* - If we get killed -9 here, the sysadmin would have to edit - the log index file after restart - otherwise, this should be safe - */ - error= update_log_index(&log_info, 1); + error= (to_log[0] ? purge_logs(to_log, 1, 0, 1, (ulonglong *) 0) : 0); err: pthread_mutex_unlock(&LOCK_index); diff --git a/sql/my_decimal.cc b/sql/my_decimal.cc index 31a5b09370a..a235edbd73c 100644 --- a/sql/my_decimal.cc +++ b/sql/my_decimal.cc @@ -216,7 +216,7 @@ my_decimal *date2my_decimal(MYSQL_TIME *ltime, my_decimal *dec) date = (ltime->year*100L + ltime->month)*100L + ltime->day; if (ltime->time_type > MYSQL_TIMESTAMP_DATE) date= ((date*100L + ltime->hour)*100L+ ltime->minute)*100L + ltime->second; - if (int2my_decimal(E_DEC_FATAL_ERROR, date, FALSE, dec)) + if (int2my_decimal(E_DEC_FATAL_ERROR, ltime->neg ? -date : date, FALSE, dec)) return dec; if (ltime->second_part) { diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 1568f042b7e..520d97b5e9f 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -435,6 +435,7 @@ MY_LOCALE *my_locale_by_number(uint number); #define UNCACHEABLE_PREPARE 16 /* For uncorrelated SELECT in an UNION with some correlated SELECTs */ #define UNCACHEABLE_UNITED 32 +#define UNCACHEABLE_CHECKOPTION 64 /* Used to check GROUP BY list in the MODE_ONLY_FULL_GROUP_BY mode */ #define UNDEF_POS (-1) @@ -1556,8 +1557,8 @@ void make_date(const DATE_TIME_FORMAT *format, const MYSQL_TIME *l_time, String *str); void make_time(const DATE_TIME_FORMAT *format, const MYSQL_TIME *l_time, String *str); -ulonglong get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, - Item *warn_item, bool *is_null); +longlong get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, + Item *warn_item, bool *is_null); int test_if_number(char *str,int *res,bool allow_wildcards); void change_byte(byte *,uint,char,char); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c3e5449b22b..8232cefc880 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3035,12 +3035,14 @@ static int init_common_variables(const char *conf_file_name, int argc, sys_init_connect.value_length= strlen(opt_init_connect); else sys_init_connect.value=my_strdup("",MYF(0)); + sys_init_connect.is_os_charset= TRUE; sys_init_slave.value_length= 0; if ((sys_init_slave.value= opt_init_slave)) sys_init_slave.value_length= strlen(opt_init_slave); else sys_init_slave.value=my_strdup("",MYF(0)); + sys_init_slave.is_os_charset= TRUE; if (use_temp_pool && bitmap_init(&temp_pool,0,1024,1)) return 1; @@ -3840,6 +3842,9 @@ we force server id to 2, but this MySQL server will not act as a slave."); : mysqld_unix_port), mysqld_port, MYSQL_COMPILATION_COMMENT); +#if defined(_WIN32) && !defined(EMBEDDED_LIBRARY) + Service.SetRunning(); +#endif #if defined(__NT__) || defined(HAVE_SMEM) handle_connections_methods(); diff --git a/sql/nt_servc.cc b/sql/nt_servc.cc index a04f284a3de..e570898f373 100644 --- a/sql/nt_servc.cc +++ b/sql/nt_servc.cc @@ -245,10 +245,6 @@ void NTService::ServiceMain(DWORD argc, LPTSTR *argv) if (!pService->StartService()) goto error; - // Check that the service is now running. - if (!pService->SetStatus(SERVICE_RUNNING,NO_ERROR, 0, 0, 0)) - goto error; - // wait for exit event WaitForSingleObject (pService->hExitEvent, INFINITE); @@ -264,6 +260,14 @@ error: return; } + +void NTService::SetRunning() +{ + if (pService) + pService->SetStatus(SERVICE_RUNNING,NO_ERROR, 0, 0, 0); +} + + /* ------------------------------------------------------------------------ StartService() - starts the appliaction thread -------------------------------------------------------------------------- */ diff --git a/sql/nt_servc.h b/sql/nt_servc.h index a3c12569114..9b689e434e1 100644 --- a/sql/nt_servc.h +++ b/sql/nt_servc.h @@ -56,7 +56,19 @@ class NTService BOOL IsService(LPCSTR ServiceName); BOOL got_service_option(char **argv, char *service_option); BOOL is_super_user(); - void Stop(void); //to be called from app. to stop service + + /* + SetRunning() is to be called by the application + when initialization completes and it can accept + stop request + */ + void SetRunning(void); + + /* + Stop() is to be called by the application to stop + the service + */ + void Stop(void); protected: LPSTR ServiceName; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 744415fa2fe..ebebfafb5d8 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -7738,7 +7738,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) DBUG_RETURN(NULL); /* The argument of MIN/MAX. */ - Item *expr= min_max_item->args[0]->real_item(); + Item *expr= min_max_item->get_arg(0)->real_item(); if (expr->type() == Item::FIELD_ITEM) /* Is it an attribute? */ { if (! min_max_arg_item) diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 3fc62d05ae5..39db1344588 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -160,7 +160,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) to the number of rows in the tables if this number is exact and there are no outer joins. */ - if (!conds && !((Item_sum_count*) item)->args[0]->maybe_null && + if (!conds && !((Item_sum_count*) item)->get_arg(0)->maybe_null && !outer_tables && is_exact_count) { ((Item_sum_count*) item)->make_const(count); @@ -176,7 +176,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) parts of the key is found in the COND, then we can use indexes to find the key. */ - Item *expr=item_sum->args[0]; + Item *expr=item_sum->get_arg(0); if (expr->real_item()->type() == Item::FIELD_ITEM) { byte key_buff[MAX_KEY_LENGTH]; @@ -319,7 +319,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) parts of the key is found in the COND, then we can use indexes to find the key. */ - Item *expr=item_sum->args[0]; + Item *expr=item_sum->get_arg(0); if (expr->real_item()->type() == Item::FIELD_ITEM) { byte key_buff[MAX_KEY_LENGTH]; diff --git a/sql/set_var.cc b/sql/set_var.cc index 6bc19f2e6eb..59741e5683d 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -138,7 +138,7 @@ sys_var_thd_ulong sys_auto_increment_offset("auto_increment_offset", sys_var_bool_ptr sys_automatic_sp_privileges("automatic_sp_privileges", &sp_automatic_privileges); -sys_var_const_str sys_basedir("basedir", mysql_home); +sys_var_const_os_str sys_basedir("basedir", mysql_home); sys_var_long_ptr sys_binlog_cache_size("binlog_cache_size", &binlog_cache_size); sys_var_thd_ulong sys_bulk_insert_buff_size("bulk_insert_buffer_size", @@ -151,6 +151,8 @@ sys_var_character_set_client sys_character_set_client("character_set_client"); sys_var_character_set_connection sys_character_set_connection("character_set_connection"); sys_var_character_set_results sys_character_set_results("character_set_results"); sys_var_character_set_filesystem sys_character_set_filesystem("character_set_filesystem"); +sys_var_const_os_str sys_character_sets_dir("character_sets_dir", + mysql_charsets_dir); sys_var_thd_ulong sys_completion_type("completion_type", &SV::completion_type, check_completion_type, @@ -162,7 +164,7 @@ sys_var_long_ptr sys_concurrent_insert("concurrent_insert", &myisam_concurrent_insert); sys_var_long_ptr sys_connect_timeout("connect_timeout", &connect_timeout); -sys_var_const_str sys_datadir("datadir", mysql_real_data_home); +sys_var_const_os_str sys_datadir("datadir", mysql_real_data_home); sys_var_enum sys_delay_key_write("delay_key_write", &delay_key_write_options, &delay_key_write_typelib, @@ -311,6 +313,7 @@ sys_var_thd_ulong sys_optimizer_prune_level("optimizer_prune_level", &SV::optimizer_prune_level); sys_var_thd_ulong sys_optimizer_search_depth("optimizer_search_depth", &SV::optimizer_search_depth); +sys_var_const_os_str sys_plugin_dir("plugin_dir", opt_plugin_dir); sys_var_thd_ulong sys_preload_buff_size("preload_buffer_size", &SV::preload_buff_size); sys_var_thd_ulong sys_read_buff_size("read_buffer_size", @@ -338,7 +341,7 @@ sys_var_thd_ulong sys_query_alloc_block_size("query_alloc_block_size", sys_var_thd_ulong sys_query_prealloc_size("query_prealloc_size", &SV::query_prealloc_size, 0, fix_thd_mem_root); -sys_var_readonly sys_tmpdir("tmpdir", OPT_GLOBAL, SHOW_CHAR, get_tmpdir); +sys_var_readonly_os sys_tmpdir("tmpdir", OPT_GLOBAL, SHOW_CHAR, get_tmpdir); sys_var_thd_ulong sys_trans_alloc_block_size("transaction_alloc_block_size", &SV::trans_alloc_block_size, 0, fix_trans_mem_root); @@ -363,9 +366,11 @@ sys_var_bool_ptr sys_secure_auth("secure_auth", &opt_secure_auth); sys_var_const_str_ptr sys_secure_file_priv("secure_file_priv", &opt_secure_file_priv); sys_var_long_ptr sys_server_id("server_id", &server_id, fix_server_id); +#ifdef HAVE_REPLICATION sys_var_bool_ptr sys_slave_compressed_protocol("slave_compressed_protocol", &opt_slave_compressed_protocol); -#ifdef HAVE_REPLICATION +sys_var_const_os_str_ptr sys_slave_load_tmpdir("slave_load_tmpdir", + &slave_load_tmpdir); sys_var_long_ptr sys_slave_net_timeout("slave_net_timeout", &slave_net_timeout); sys_var_long_ptr sys_slave_trans_retries("slave_transaction_retries", @@ -380,17 +385,17 @@ sys_var_thd_sql_mode sys_sql_mode("sql_mode", #ifdef HAVE_OPENSSL extern char *opt_ssl_ca, *opt_ssl_capath, *opt_ssl_cert, *opt_ssl_cipher, *opt_ssl_key; -sys_var_const_str_ptr sys_ssl_ca("ssl_ca", &opt_ssl_ca); -sys_var_const_str_ptr sys_ssl_capath("ssl_capath", &opt_ssl_capath); -sys_var_const_str_ptr sys_ssl_cert("ssl_cert", &opt_ssl_cert); -sys_var_const_str_ptr sys_ssl_cipher("ssl_cipher", &opt_ssl_cipher); -sys_var_const_str_ptr sys_ssl_key("ssl_key", &opt_ssl_key); +sys_var_const_os_str_ptr sys_ssl_ca("ssl_ca", &opt_ssl_ca); +sys_var_const_os_str_ptr sys_ssl_capath("ssl_capath", &opt_ssl_capath); +sys_var_const_os_str_ptr sys_ssl_cert("ssl_cert", &opt_ssl_cert); +sys_var_const_os_str_ptr sys_ssl_cipher("ssl_cipher", &opt_ssl_cipher); +sys_var_const_os_str_ptr sys_ssl_key("ssl_key", &opt_ssl_key); #else -sys_var_const_str sys_ssl_ca("ssl_ca", NULL); -sys_var_const_str sys_ssl_capath("ssl_capath", NULL); -sys_var_const_str sys_ssl_cert("ssl_cert", NULL); -sys_var_const_str sys_ssl_cipher("ssl_cipher", NULL); -sys_var_const_str sys_ssl_key("ssl_key", NULL); +sys_var_const_os_str sys_ssl_ca("ssl_ca", NULL); +sys_var_const_os_str sys_ssl_capath("ssl_capath", NULL); +sys_var_const_os_str sys_ssl_cert("ssl_cert", NULL); +sys_var_const_os_str sys_ssl_cipher("ssl_cipher", NULL); +sys_var_const_os_str sys_ssl_key("ssl_key", NULL); #endif sys_var_thd_enum sys_updatable_views_with_limit("updatable_views_with_limit", @@ -460,6 +465,14 @@ sys_var_long_ptr sys_innodb_commit_concurrency("innodb_commit_concurrency", sys_var_long_ptr sys_innodb_flush_log_at_trx_commit( "innodb_flush_log_at_trx_commit", &srv_flush_log_at_trx_commit); +sys_var_const_os_str_ptr sys_innodb_data_file_path("innodb_data_file_path", + &innobase_data_file_path); +sys_var_const_os_str_ptr sys_innodb_data_home_dir("innodb_data_home_dir", + &innobase_data_home_dir); +sys_var_const_os_str_ptr sys_innodb_log_arch_dir("innodb_log_arch_dir", + &innobase_log_arch_dir); +sys_var_const_os_str_ptr sys_innodb_log_group_home_dir("innodb_log_group_home_dir", + &innobase_log_group_home_dir); #endif /* Condition pushdown to storage engine */ @@ -844,7 +857,7 @@ struct show_var_st init_vars[]= { {sys_character_set_results.name,(char*) &sys_character_set_results, SHOW_SYS}, {sys_character_set_server.name, (char*) &sys_character_set_server,SHOW_SYS}, {sys_charset_system.name, (char*) &sys_charset_system, SHOW_SYS}, - {"character_sets_dir", mysql_charsets_dir, SHOW_CHAR}, + {sys_character_sets_dir.name, (char *) &sys_character_sets_dir, SHOW_SYS}, {sys_collation_connection.name,(char*) &sys_collation_connection, SHOW_SYS}, {sys_collation_database.name,(char*) &sys_collation_database, SHOW_SYS}, {sys_collation_server.name,(char*) &sys_collation_server, SHOW_SYS}, @@ -905,8 +918,8 @@ struct show_var_st init_vars[]= { {"innodb_checksums", (char*) &innobase_use_checksums, SHOW_MY_BOOL}, {sys_innodb_commit_concurrency.name, (char*) &sys_innodb_commit_concurrency, SHOW_SYS}, {sys_innodb_concurrency_tickets.name, (char*) &sys_innodb_concurrency_tickets, SHOW_SYS}, - {"innodb_data_file_path", (char*) &innobase_data_file_path, SHOW_CHAR_PTR}, - {"innodb_data_home_dir", (char*) &innobase_data_home_dir, SHOW_CHAR_PTR}, + {sys_innodb_data_file_path.name, (char*) &sys_innodb_data_file_path, SHOW_SYS}, + {sys_innodb_data_home_dir.name, (char*) &sys_innodb_data_home_dir, SHOW_SYS}, {"innodb_adaptive_hash_index", (char*) &innobase_adaptive_hash_index, SHOW_MY_BOOL}, {"innodb_doublewrite", (char*) &innobase_use_doublewrite, SHOW_MY_BOOL}, {sys_innodb_fast_shutdown.name,(char*) &sys_innodb_fast_shutdown, SHOW_SYS}, @@ -917,12 +930,12 @@ struct show_var_st init_vars[]= { {"innodb_force_recovery", (char*) &innobase_force_recovery, SHOW_LONG }, {"innodb_lock_wait_timeout", (char*) &innobase_lock_wait_timeout, SHOW_LONG }, {"innodb_locks_unsafe_for_binlog", (char*) &innobase_locks_unsafe_for_binlog, SHOW_MY_BOOL}, - {"innodb_log_arch_dir", (char*) &innobase_log_arch_dir, SHOW_CHAR_PTR}, + {sys_innodb_log_arch_dir.name, (char*) &sys_innodb_log_arch_dir, SHOW_SYS}, {"innodb_log_archive", (char*) &innobase_log_archive, SHOW_MY_BOOL}, {"innodb_log_buffer_size", (char*) &innobase_log_buffer_size, SHOW_LONG }, {"innodb_log_file_size", (char*) &innobase_log_file_size, SHOW_LONGLONG}, {"innodb_log_files_in_group", (char*) &innobase_log_files_in_group, SHOW_LONG}, - {"innodb_log_group_home_dir", (char*) &innobase_log_group_home_dir, SHOW_CHAR_PTR}, + {sys_innodb_log_group_home_dir.name, (char*) &sys_innodb_log_group_home_dir, SHOW_SYS}, {sys_innodb_max_dirty_pages_pct.name, (char*) &sys_innodb_max_dirty_pages_pct, SHOW_SYS}, {sys_innodb_max_purge_lag.name, (char*) &sys_innodb_max_purge_lag, SHOW_SYS}, {"innodb_mirrored_log_groups", (char*) &innobase_mirrored_log_groups, SHOW_LONG}, @@ -1026,7 +1039,7 @@ struct show_var_st init_vars[]= { {sys_optimizer_search_depth.name,(char*) &sys_optimizer_search_depth, SHOW_SYS}, {"pid_file", (char*) pidfile_name, SHOW_CHAR}, - {"plugin_dir", (char*) opt_plugin_dir, SHOW_CHAR}, + {sys_plugin_dir.name, (char*) &sys_plugin_dir, SHOW_SYS}, {"port", (char*) &mysqld_port, SHOW_INT}, {sys_preload_buff_size.name, (char*) &sys_preload_buff_size, SHOW_SYS}, {"protocol_version", (char*) &protocol_version, SHOW_INT}, @@ -1068,7 +1081,7 @@ struct show_var_st init_vars[]= { #ifdef HAVE_REPLICATION {sys_slave_compressed_protocol.name, (char*) &sys_slave_compressed_protocol, SHOW_SYS}, - {"slave_load_tmpdir", (char*) &slave_load_tmpdir, SHOW_CHAR_PTR}, + {sys_slave_load_tmpdir.name,(char*) &sys_slave_load_tmpdir, SHOW_SYS}, {sys_slave_net_timeout.name,(char*) &sys_slave_net_timeout, SHOW_SYS}, {"slave_skip_errors", (char*) &slave_error_mask, SHOW_SLAVE_SKIP_ERRORS}, {sys_slave_trans_retries.name,(char*) &sys_slave_trans_retries, SHOW_SYS}, @@ -1175,6 +1188,7 @@ bool update_sys_var_str(sys_var_str *var_str, rw_lock_t *var_mutex, old_value= var_str->value; var_str->value= res; var_str->value_length= new_length; + var_str->is_os_charset= FALSE; rw_unlock(var_mutex); my_free(old_value, MYF(MY_ALLOW_ZERO_PTR)); return 0; @@ -1914,11 +1928,11 @@ Item *sys_var::item(THD *thd, enum_var_type var_type, LEX_STRING *base) char *str= (char*) value_ptr(thd, var_type, base); if (str) tmp= new Item_string(str, strlen(str), - system_charset_info, DERIVATION_SYSCONST); + charset(thd), DERIVATION_SYSCONST); else { tmp= new Item_null(); - tmp->collation.set(system_charset_info, DERIVATION_SYSCONST); + tmp->collation.set(charset(thd), DERIVATION_SYSCONST); } pthread_mutex_unlock(&LOCK_global_system_variables); return tmp; @@ -1930,6 +1944,13 @@ Item *sys_var::item(THD *thd, enum_var_type var_type, LEX_STRING *base) } +CHARSET_INFO *sys_var::charset(THD *thd) +{ + return is_os_charset ? thd->variables.character_set_filesystem : + system_charset_info; +} + + bool sys_var_thd_enum::update(THD *thd, set_var *var) { if (var->type == OPT_GLOBAL) diff --git a/sql/set_var.h b/sql/set_var.h index c37cc400e43..f43d3b75cee 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -46,9 +46,17 @@ public: sys_after_update_func after_update; bool no_support_one_shot; + /* + true if the value is in character_set_filesystem, + false otherwise. + Note that we can't use a pointer to the charset as the system var is + instantiated in global scope and the charset pointers are initialized + later. + */ + bool is_os_charset; sys_var(const char *name_arg, sys_after_update_func func= NULL) :name(name_arg), after_update(func) - , no_support_one_shot(1) + , no_support_one_shot(1), is_os_charset(FALSE) {} virtual ~sys_var() {} virtual bool check(THD *thd, set_var *var); @@ -68,6 +76,7 @@ public: Item *item(THD *thd, enum_var_type type, LEX_STRING *base); virtual bool is_struct() { return 0; } virtual bool is_readonly() const { return 0; } + CHARSET_INFO *charset(THD *thd); }; @@ -247,6 +256,17 @@ public: }; +class sys_var_const_os_str: public sys_var_const_str +{ +public: + sys_var_const_os_str(const char *name_arg, const char *value_arg) + :sys_var_const_str(name_arg, value_arg) + { + is_os_charset= TRUE; + } +}; + + class sys_var_const_str_ptr :public sys_var { public: @@ -276,6 +296,17 @@ public: }; +class sys_var_const_os_str_ptr :public sys_var_const_str_ptr +{ +public: + sys_var_const_os_str_ptr(const char *name_arg, char **value_arg) + :sys_var_const_str_ptr(name_arg, value_arg) + { + is_os_charset= TRUE; + } +}; + + class sys_var_enum :public sys_var { uint *value; @@ -791,6 +822,20 @@ public: bool is_readonly() const { return 1; } }; + +class sys_var_readonly_os: public sys_var_readonly +{ +public: + sys_var_readonly_os(const char *name_arg, enum_var_type type, + SHOW_TYPE show_type_arg, + sys_value_ptr_func value_ptr_func_arg) + :sys_var_readonly(name_arg, type, show_type_arg, value_ptr_func_arg) + { + is_os_charset= TRUE; + } +}; + + class sys_var_thd_time_zone :public sys_var_thd { public: diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 873a3eac24e..881c6a421e8 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2102,7 +2102,10 @@ bool reopen_table(TABLE *table,bool locked) for (key=0 ; key < table->s->keys ; key++) { for (part=0 ; part < table->key_info[key].usable_key_parts ; part++) + { table->key_info[key].key_part[part].field->table= table; + table->key_info[key].key_part[part].field->orig_table= table; + } } if (table->triggers) table->triggers->set_table(table); diff --git a/sql/sql_class.h b/sql/sql_class.h index c8d42d44df7..cc7ef7809d4 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -205,6 +205,13 @@ class MYSQL_LOG: public TC_LOG time_t last_time,query_start; IO_CACHE log_file; IO_CACHE index_file; + /* + purge_temp is a temp file used in purge_logs so that the index file + can be updated before deleting files from disk, yielding better crash + recovery. It is created on demand the first time purge_logs is called + and then reused for subsequent calls. It is cleaned up in cleanup(). + */ + IO_CACHE purge_temp; char *name; char time_buff[20],db[NAME_LEN+1]; char log_file_name[FN_REFLEN],index_file_name[FN_REFLEN]; diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc index 16567765ba6..83c60814cf3 100644 --- a/sql/sql_cursor.cc +++ b/sql/sql_cursor.cc @@ -85,6 +85,7 @@ class Materialized_cursor: public Server_side_cursor List<Item> item_list; ulong fetch_limit; ulong fetch_count; + bool is_rnd_inited; public: Materialized_cursor(select_result *result, TABLE *table); @@ -191,7 +192,11 @@ int mysql_open_cursor(THD *thd, uint flags, select_result *result, such command is SHOW VARIABLES or SHOW STATUS. */ if (rc) + { + if (result_materialize->materialized_cursor) + delete result_materialize->materialized_cursor; goto err_open; + } if (sensitive_cursor->is_open()) { @@ -532,7 +537,8 @@ Materialized_cursor::Materialized_cursor(select_result *result_arg, :Server_side_cursor(&table_arg->mem_root, result_arg), table(table_arg), fetch_limit(0), - fetch_count(0) + fetch_count(0), + is_rnd_inited(0) { fake_unit.init_query(); fake_unit.thd= table->in_use; @@ -589,11 +595,12 @@ int Materialized_cursor::open(JOIN *join __attribute__((unused))) THD *thd= fake_unit.thd; int rc; Query_arena backup_arena; - thd->set_n_backup_active_arena(this, &backup_arena); /* Create a list of fields and start sequential scan */ - rc= (result->prepare(item_list, &fake_unit) || - table->file->ha_rnd_init(TRUE)); + rc= result->prepare(item_list, &fake_unit); + if (!rc && !(rc= table->file->ha_rnd_init(TRUE))) + is_rnd_inited= 1; + thd->restore_active_arena(this, &backup_arena); if (rc == 0) { @@ -673,7 +680,8 @@ void Materialized_cursor::close() { /* Free item_list items */ free_items(); - (void) table->file->ha_rnd_end(); + if (is_rnd_inited) + (void) table->file->ha_rnd_end(); /* We need to grab table->mem_root to prevent free_tmp_table from freeing: the cursor object was allocated in this memory. diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 428d1709f94..d2c469f99da 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -390,11 +390,21 @@ inline int setup_without_group(THD *thd, Item **ref_pointer_array, { int res; nesting_map save_allow_sum_func=thd->lex->allow_sum_func ; + /* + Need to save the value, so we can turn off only the new NON_AGG_FIELD + additions coming from the WHERE + */ + uint8 saved_flag= thd->lex->current_select->full_group_by_flag; DBUG_ENTER("setup_without_group"); thd->lex->allow_sum_func&= ~(1 << thd->lex->current_select->nest_level); res= setup_conds(thd, tables, leaves, conds); + /* it's not wrong to have non-aggregated columns in a WHERE */ + if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY) + thd->lex->current_select->full_group_by_flag= saved_flag | + (thd->lex->current_select->full_group_by_flag & ~NON_AGG_FIELD_USED); + thd->lex->allow_sum_func|= 1 << thd->lex->current_select->nest_level; res= res || setup_order(thd, ref_pointer_array, tables, fields, all_fields, order); @@ -1589,8 +1599,11 @@ JOIN::exec() (zero_result_cause?zero_result_cause:"No tables used")); else { - result->send_fields(*columns_list, - Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); + if (result->send_fields(*columns_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) + { + DBUG_VOID_RETURN; + } /* We have to test for 'conds' here as the WHERE may not be constant even if we don't have any tables for prepared statements or if @@ -9434,11 +9447,11 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, } if (type == Item::SUM_FUNC_ITEM && !group && !save_sum_fields) { /* Can't calc group yet */ - ((Item_sum*) item)->result_field=0; - for (i=0 ; i < ((Item_sum*) item)->arg_count ; i++) + Item_sum *sum_item= (Item_sum *) item; + sum_item->result_field=0; + for (i=0 ; i < sum_item->get_arg_count() ; i++) { - Item **argp= ((Item_sum*) item)->args + i; - Item *arg= *argp; + Item *arg= sum_item->get_arg(i); if (!arg->const_item()) { uint field_index= (uint) (reg_field - table->field); @@ -9468,7 +9481,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, string_total_length+= new_field->pack_length(); } thd->mem_root= mem_root_save; - thd->change_item_tree(argp, new Item_field(new_field)); + arg= sum_item->set_arg(i, thd, new Item_field(new_field)); thd->mem_root= &table->mem_root; if (!(new_field->flags & NOT_NULL_FLAG)) { @@ -9477,7 +9490,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, new_field->maybe_null() is still false, it will be changed below. But we have to setup Item_field correctly */ - (*argp)->maybe_null=1; + arg->maybe_null=1; } new_field->query_id= thd->query_id; } @@ -13223,6 +13236,7 @@ join_init_cache(THD *thd,JOIN_TAB *tables,uint table_count) length=0; for (i=0 ; i < table_count ; i++) { + bool have_bit_fields= FALSE; uint null_fields=0,used_fields; Field **f_ptr,*field; @@ -13237,13 +13251,16 @@ join_init_cache(THD *thd,JOIN_TAB *tables,uint table_count) length+=field->fill_cache_field(copy); if (copy->blob_field) (*blob_ptr++)=copy; - if (field->maybe_null()) + if (field->real_maybe_null()) null_fields++; + if (field->type() == MYSQL_TYPE_BIT && + ((Field_bit*)field)->bit_len) + have_bit_fields= TRUE; copy++; } } /* Copy null bits from table */ - if (null_fields && tables[i].table->s->null_fields) + if (null_fields || have_bit_fields) { /* must copy null bits */ copy->str=(char*) tables[i].table->null_flags; copy->length= tables[i].table->s->null_bytes; @@ -13908,9 +13925,9 @@ count_field_types(SELECT_LEX *select_lex, TMP_TABLE_PARAM *param, param->quick_group=0; // UDF SUM function param->sum_func_count++; - for (uint i=0 ; i < sum_item->arg_count ; i++) + for (uint i=0 ; i < sum_item->get_arg_count() ; i++) { - if (sum_item->args[0]->real_item()->type() == Item::FIELD_ITEM) + if (sum_item->get_arg(i)->real_item()->type() == Item::FIELD_ITEM) param->field_count++; else param->func_count++; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 5a4772e9847..59082e0a295 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -798,7 +798,7 @@ static bool get_field_default_value(THD *thd, TABLE *table, { bool has_default; bool has_now_default; - + enum enum_field_types field_type= field->type(); /* We are using CURRENT_TIMESTAMP instead of NOW because it is more standard @@ -806,7 +806,7 @@ static bool get_field_default_value(THD *thd, TABLE *table, has_now_default= table->timestamp_field == field && field->unireg_check != Field::TIMESTAMP_UN_FIELD; - has_default= (field->type() != FIELD_TYPE_BLOB && + has_default= (field_type != FIELD_TYPE_BLOB && !(field->flags & NO_DEFAULT_VALUE_FLAG) && field->unireg_check != Field::NEXT_NUMBER && !((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) @@ -821,7 +821,19 @@ static bool get_field_default_value(THD *thd, TABLE *table, { // Not null by default char tmp[MAX_FIELD_WIDTH]; String type(tmp, sizeof(tmp), field->charset()); - field->val_str(&type); + if (field_type == MYSQL_TYPE_BIT) + { + longlong dec= field->val_int(); + char *ptr= longlong2str(dec, tmp + 2, 2); + uint32 length= (uint32) (ptr - tmp); + tmp[0]= 'b'; + tmp[1]= '\''; + tmp[length]= '\''; + type.length(length + 1); + quoted= 0; + } + else + field->val_str(&type); if (type.length()) { String def_val; @@ -1441,6 +1453,7 @@ static bool show_status_array(THD *thd, const char *wild, char name_buffer[80]; int len; LEX_STRING null_lex_str; + CHARSET_INFO *charset= system_charset_info; DBUG_ENTER("show_status_array"); null_lex_str.str= 0; // For sys_var->value_ptr() @@ -1469,9 +1482,10 @@ static bool show_status_array(THD *thd, const char *wild, long nr; if (show_type == SHOW_SYS) { - show_type= ((sys_var*) value)->show_type(); - value= (char*) ((sys_var*) value)->value_ptr(thd, value_type, - &null_lex_str); + sys_var *var= ((sys_var *) value); + show_type= var->show_type(); + value= (char*) var->value_ptr(thd, value_type, &null_lex_str); + charset= var->charset(thd); } pos= end= buff; @@ -1794,7 +1808,7 @@ static bool show_status_array(THD *thd, const char *wild, restore_record(table, s->default_values); table->field[0]->store(name_buffer, strlen(name_buffer), system_charset_info); - table->field[1]->store(pos, (uint32) (end - pos), system_charset_info); + table->field[1]->store(pos, (uint32) (end - pos), charset); if (schema_table_store_record(thd, table)) DBUG_RETURN(TRUE); } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 8baeca9ccf7..eefe2a5596e 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1535,7 +1535,9 @@ static bool prepare_blob_field(THD *thd, create_field *sql_field) if ((sql_field->flags & BLOB_FLAG) && sql_field->length) { - if (sql_field->sql_type == FIELD_TYPE_BLOB) + if (sql_field->sql_type == FIELD_TYPE_BLOB || + sql_field->sql_type == FIELD_TYPE_TINY_BLOB || + sql_field->sql_type == FIELD_TYPE_MEDIUM_BLOB) { /* The user has given a length to the blob column */ sql_field->sql_type= get_blob_type_from_length(sql_field->length); diff --git a/sql/sql_update.cc b/sql/sql_update.cc index eb4e9b7ed73..f15db220a3b 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1249,6 +1249,32 @@ multi_update::initialize_tables(JOIN *join) } } + /* + enable uncacheable flag if we update a view with check option + and check option has a subselect, otherwise, the check option + can be evaluated after the subselect was freed as independent + (See full_local in JOIN::join_free()). + */ + if (table_ref->check_option && !join->select_lex->uncacheable) + { + SELECT_LEX_UNIT *tmp_unit; + SELECT_LEX *sl; + for (tmp_unit= join->select_lex->first_inner_unit(); + tmp_unit; + tmp_unit= tmp_unit->next_unit()) + { + for (sl= tmp_unit->first_select(); sl; sl= sl->next_select()) + { + if (sl->master_unit()->item) + { + join->select_lex->uncacheable|= UNCACHEABLE_CHECKOPTION; + goto loop_end; + } + } + } + } +loop_end: + if (table == first_table_for_update && table_ref->check_option) { table_map unupdated_tables= table_ref->check_option->used_tables() & diff --git a/sql/sql_view.cc b/sql/sql_view.cc index f65a62bed75..41a638b2618 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -980,13 +980,14 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, DBUG_RETURN(0); } - if (table->use_index || table->ignore_index) + List<String> *index_list= table->use_index ? table->use_index + : table->ignore_index; + if (index_list) { - my_error(ER_WRONG_USAGE, MYF(0), - table->ignore_index ? "IGNORE INDEX" : - (table->force_index ? "FORCE INDEX" : "USE INDEX"), - "VIEW"); - DBUG_RETURN(TRUE); + DBUG_ASSERT(index_list->head()); // should never fail + my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), index_list->head()->c_ptr(), + table->table_name); + DBUG_RETURN(TRUE); } /* check loop via view definition */ diff --git a/sql/tztime.cc b/sql/tztime.cc index 709e3b64752..d3d952e3c1e 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -1073,6 +1073,7 @@ Time_zone_system::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const localtime_r(&tmp_t, &tmp_tm); localtime_to_TIME(tmp, &tmp_tm); tmp->time_type= MYSQL_TIMESTAMP_DATETIME; + adjust_leap_second(tmp); } @@ -1157,6 +1158,7 @@ Time_zone_utc::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const gmtime_r(&tmp_t, &tmp_tm); localtime_to_TIME(tmp, &tmp_tm); tmp->time_type= MYSQL_TIMESTAMP_DATETIME; + adjust_leap_second(tmp); } @@ -1260,6 +1262,7 @@ void Time_zone_db::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const { ::gmt_sec_to_TIME(tmp, t, tz_info); + adjust_leap_second(tmp); } @@ -2373,6 +2376,25 @@ Time_zone *my_tz_find_with_opening_tz_tables(THD *thd, const String *name) DBUG_RETURN(tz); } + +/** + Convert leap seconds into non-leap + + This function will convert the leap seconds added by the OS to + non-leap seconds, e.g. 23:59:59, 23:59:60 -> 23:59:59, 00:00:01 ... + This check is not checking for years on purpose : although it's not a + complete check this way it doesn't require looking (and having installed) + the leap seconds table. + + @param[in,out] broken down time structure as filled in by the OS +*/ + +void Time_zone::adjust_leap_second(MYSQL_TIME *t) +{ + if (t->second == 60 || t->second == 61) + t->second= 59; +} + #endif /* !defined(TESTTIME) && !defined(TZINFO2SQL) */ diff --git a/sql/tztime.h b/sql/tztime.h index 32a942a26e1..750b8dacbe1 100644 --- a/sql/tztime.h +++ b/sql/tztime.h @@ -55,6 +55,9 @@ public: allocated on MEM_ROOT and should not require destruction. */ virtual ~Time_zone() {}; + +protected: + static inline void adjust_leap_second(MYSQL_TIME *t); }; extern Time_zone * my_tz_UTC; |