diff options
Diffstat (limited to 'sql/field.cc')
-rw-r--r-- | sql/field.cc | 2008 |
1 files changed, 1363 insertions, 645 deletions
diff --git a/sql/field.cc b/sql/field.cc index 1427e055324..ecb383a9575 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -71,7 +71,7 @@ const char field_separator=','; ((ulong) ((1LL << MY_MIN(arg, 4) * 8) - 1)) #define ASSERT_COLUMN_MARKED_FOR_READ DBUG_ASSERT(!table || (!table->read_set || bitmap_is_set(table->read_set, field_index))) -#define ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED DBUG_ASSERT(is_stat_field || !table || (!table->write_set || bitmap_is_set(table->write_set, field_index) || bitmap_is_set(table->vcol_set, field_index))) +#define ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED DBUG_ASSERT(is_stat_field || !table || (!table->write_set || bitmap_is_set(table->write_set, field_index) || (table->vcol_set && bitmap_is_set(table->vcol_set, field_index)))) #define FLAGSTR(S,F) ((S) & (F) ? #F " " : "") @@ -1055,37 +1055,6 @@ Item_result Field::result_merge_type(enum_field_types field_type) *****************************************************************************/ /** - Output a warning for erroneous conversion of strings to numerical - values. For use with ER_TRUNCATED_WRONG_VALUE[_FOR_FIELD] - - @param thd THD object - @param str pointer to string that failed to be converted - @param length length of string - @param cs charset for string - @param typestr string describing type converted to - @param error error value to output - @param field_name (for *_FOR_FIELD) name of field - @param row_num (for *_FOR_FIELD) row number - */ -static void push_numerical_conversion_warning(THD* thd, const char* str, - uint length, CHARSET_INFO* cs, - const char* typestr, int error, - const char* field_name="UNKNOWN", - ulong row_num=0) -{ - char buf[MY_MAX(MY_MAX(DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE, - LONGLONG_TO_STRING_CONVERSION_BUFFER_SIZE), - DECIMAL_TO_STRING_CONVERSION_BUFFER_SIZE)]; - - String tmp(buf, sizeof(buf), cs); - tmp.copy(str, length, cs); - push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, - error, ER(error), typestr, tmp.c_ptr(), - field_name, row_num); -} - - -/** Check whether a field type can be partially indexed by a key. This is a static method, rather than a virtual function, because we need @@ -1246,6 +1215,113 @@ double Field::pos_in_interval_val_str(Field *min, Field *max, uint data_offset) } +bool Field::test_if_equality_guarantees_uniqueness(const Item *item) const +{ + DBUG_ASSERT(cmp_type() != STRING_RESULT); // For STRING_RESULT see Field_str + /* + We use result_type() rather than cmp_type() in the below condition, + because it covers a special case that string literals guarantee uniqueness + for temporal columns, so the query: + WHERE temporal_column='string' + cannot return multiple distinct temporal values. + QQ: perhaps we could allow INT/DECIMAL/DOUBLE types for temporal items. + */ + return result_type() == item->result_type(); +} + + +/** + Check whether a field item can be substituted for an equal item + + @details + The function checks whether a substitution of a field item for + an equal item is valid. + + @param arg *arg != NULL <-> the field is in the context + where substitution for an equal item is valid + + @note + The following statement is not always true: + @n + x=y => F(x)=F(x/y). + @n + This means substitution of an item for an equal item not always + yields an equavalent condition. Here's an example: + @code + 'a'='a ' + (LENGTH('a')=1) != (LENGTH('a ')=2) + @endcode + Such a substitution is surely valid if either the substituted + field is not of a STRING type or if it is an argument of + a comparison predicate. + + @retval + TRUE substitution is valid + @retval + FALSE otherwise +*/ + +bool Field::can_be_substituted_to_equal_item(const Context &ctx, + const Item_equal *item_equal) +{ + DBUG_ASSERT(item_equal->compare_type() != STRING_RESULT); + DBUG_ASSERT(cmp_type() != STRING_RESULT); + switch (ctx.subst_constraint()) { + case ANY_SUBST: + /* + Disable const propagation for items used in different comparison contexts. + This must be done because, for example, Item_hex_string->val_int() is not + the same as (Item_hex_string->val_str() in BINARY column)->val_int(). + We cannot simply disable the replacement in a particular context ( + e.g. <bin_col> = <int_col> AND <bin_col> = <hex_string>) since + Items don't know the context they are in and there are functions like + IF (<hex_string>, 'yes', 'no'). + */ + return ctx.compare_type() == item_equal->compare_type(); + case IDENTITY_SUBST: + return true; + } + return false; +} + + +/* + This handles all numeric and BIT data types. +*/ +bool Field::can_optimize_keypart_ref(const Item_bool_func *cond, + const Item *item) const +{ + DBUG_ASSERT(cmp_type() != STRING_RESULT); + DBUG_ASSERT(cmp_type() != TIME_RESULT); + return item->cmp_type() != TIME_RESULT; +} + + +/* + This handles all numeric and BIT data types. +*/ +bool Field::can_optimize_group_min_max(const Item_bool_func *cond, + const Item *const_item) const +{ + DBUG_ASSERT(cmp_type() != STRING_RESULT); + DBUG_ASSERT(cmp_type() != TIME_RESULT); + return const_item->cmp_type() != TIME_RESULT; +} + + +/* + This covers all numeric types, ENUM, SET, BIT +*/ +bool Field::can_optimize_range(const Item_bool_func *cond, + const Item *item, + bool is_eq_func) const +{ + DBUG_ASSERT(cmp_type() != TIME_RESULT); // Handled in Field_temporal + DBUG_ASSERT(cmp_type() != STRING_RESULT); // Handled in Field_longstr + return item->cmp_type() != TIME_RESULT; +} + + /** Numeric fields base class constructor. */ @@ -1264,7 +1340,7 @@ Field_num::Field_num(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, } -void Field_num::prepend_zeros(String *value) +void Field_num::prepend_zeros(String *value) const { int diff; if ((diff= (int) (field_length - value->length())) > 0) @@ -1281,40 +1357,145 @@ void Field_num::prepend_zeros(String *value) } } + +Item *Field_num::get_equal_zerofill_const_item(THD *thd, const Context &ctx, + Item *const_item) +{ + switch (ctx.subst_constraint()) { + case IDENTITY_SUBST: + return NULL; // Not safe to propagate if not in comparison. See MDEV-8369. + case ANY_SUBST: + break; + } + DBUG_ASSERT(const_item->const_item()); + DBUG_ASSERT(ctx.compare_type() != STRING_RESULT); + return const_item; +} + + /** - Test if given number is a int. + Contruct warning parameters using thd->no_errors + to determine whether to generate or suppress warnings. + We can get here in a query like this: + SELECT COUNT(@@basedir); + from Item_func_get_system_var::update_null_value(). +*/ +Value_source::Warn_filter::Warn_filter(const THD *thd) + :m_want_warning_edom(!thd->no_errors), + m_want_note_truncated_spaces(!thd->no_errors) +{ } - @todo - Make this multi-byte-character safe - @param str String to test +/** + Check string-to-number conversion and produce a warning if + - could not convert any digits (EDOM-alike error) + - found garbage at the end of the string + - found trailing spaces (a note) + See also Field_num::check_edom_and_truncation() for a similar function. + + @param thd - the thread + @param filter - which warnings/notes are allowed + @param type - name of the data type (e.g. "INTEGER", "DECIMAL", "DOUBLE") + @param cs - character set of the original string + @param str - the original string + @param end - the end of the string + + Unlike Field_num::check_edom_and_truncation(), this function does not + distinguish between EDOM and truncation and reports the same warning for + both cases. Perhaps we should eventually print different warnings, to make + the explicit CAST work closer to the implicit cast in Field_xxx::store(). +*/ +void +Value_source::Converter_string_to_number::check_edom_and_truncation(THD *thd, + Warn_filter filter, + const char *type, + CHARSET_INFO *cs, + const char *str, + size_t length) const +{ + DBUG_ASSERT(str <= m_end_of_num); + DBUG_ASSERT(m_end_of_num <= str + length); + if (m_edom || (m_end_of_num < str + length && + !check_if_only_end_space(cs, m_end_of_num, str + length))) + { + // EDOM or important trailing data truncation + if (filter.want_warning_edom()) + { + /* + We can use err.ptr() here as ErrConvString is guranteed to put an + end \0 here. + */ + THD *wthd= thd ? thd : current_thd; + push_warning_printf(wthd, Sql_condition::WARN_LEVEL_WARN, + ER_TRUNCATED_WRONG_VALUE, + ER_THD(wthd, ER_TRUNCATED_WRONG_VALUE), type, + ErrConvString(str, length, cs).ptr()); + } + } + else if (m_end_of_num < str + length) + { + // Unimportant trailing data (spaces) truncation + if (filter.want_note_truncated_spaces()) + { + THD *wthd= thd ? thd : current_thd; + push_warning_printf(wthd, Sql_condition::WARN_LEVEL_NOTE, + ER_TRUNCATED_WRONG_VALUE, + ER_THD(wthd, ER_TRUNCATED_WRONG_VALUE), type, + ErrConvString(str, length, cs).ptr()); + } + } +} + + +/** + Check a string-to-number conversion routine result and generate warnings + in case when it: + - could not convert any digits + - found garbage at the end of the string. + + @param type Data type name (e.g. "decimal", "integer", "double") + @param edom Indicates that the string-to-number routine retuned + an error code equivalent to EDOM (value out of domain), + i.e. the string fully consisted of garbage and the + conversion routine could not get any digits from it. + @param str The original string @param length Length of 'str' - @param int_end Pointer to char after last used digit - @param cs Character set + @param cs Character set + @param end Pointer to char after last used digit @note - This is called after one has called strntoull10rnd() function. + This is called after one has called one of the following functions: + - strntoull10rnd() + - my_strntod() + - str2my_decimal() @retval - 0 OK + 0 OK @retval - 1 error: empty string or wrong integer. + 1 error: could not scan any digits (EDOM), + e.g. empty string, or garbage. @retval - 2 error: garbage at the end of string. + 2 error: scanned some digits, + but then found garbage at the end of the string. */ -int Field_num::check_int(CHARSET_INFO *cs, const char *str, int length, - const char *int_end, int error) + +int Field_num::check_edom_and_important_data_truncation(const char *type, + bool edom, + CHARSET_INFO *cs, + const char *str, + uint length, + const char *end) { - /* Test if we get an empty string or wrong integer */ - if (str == int_end || error == MY_ERRNO_EDOM) + /* Test if we get an empty string or garbage */ + if (edom) { ErrConvString err(str, length, cs); - set_warning_truncated_wrong_value("integer", err.ptr()); + set_warning_truncated_wrong_value(type, err.ptr()); return 1; } /* Test if we have garbage at the end of the given string. */ - if (test_if_important_data(cs, int_end, str + length)) + if (test_if_important_data(cs, end, str + length)) { set_warning(WARN_DATA_TRUNCATED, 1); return 2; @@ -1323,6 +1504,19 @@ int Field_num::check_int(CHARSET_INFO *cs, const char *str, int length, } +int Field_num::check_edom_and_truncation(const char *type, bool edom, + CHARSET_INFO *cs, + const char *str, uint length, + const char *end) +{ + int rc= check_edom_and_important_data_truncation(type, edom, + cs, str, length, end); + if (!rc && end < str + length) + set_note(WARN_DATA_TRUNCATED, 1); + return rc; +} + + /* Conver a string to an integer then check bounds. @@ -1390,6 +1584,24 @@ out_of_range: } +double Field_real::get_double(const char *str, uint length, CHARSET_INFO *cs, + int *error) +{ + char *end; + double nr= my_strntod(cs,(char*) str, length, &end, error); + if (*error) + { + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + *error= 1; + } + else if (get_thd()->count_cuted_fields && + check_edom_and_truncation("double", str == end, + cs, str, length, end)) + *error= 1; + return nr; +} + + /** Process decimal library return codes and issue warnings for overflow and truncation. @@ -1809,6 +2021,16 @@ my_decimal* Field_num::val_decimal(my_decimal *decimal_value) } +bool Field_num::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + longlong nr= val_int(); + bool neg= !(flags & UNSIGNED_FLAG) && nr < 0; + return int_to_datetime_with_warn(neg, neg ? -nr : nr, ltime, fuzzydate, + field_name); +} + + Field_str::Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, const char *field_name_arg, CHARSET_INFO *charset_arg) @@ -1823,6 +2045,49 @@ Field_str::Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, } +bool Field_str::test_if_equality_guarantees_uniqueness(const Item *item) const +{ + /* + Can't guarantee uniqueness when comparing a CHAR/VARCHAR/TEXT, + BINARY/VARBINARY/BLOB, ENUM,SET columns to an item with cmp_type() + of INT_RESULT, DOUBLE_RESULT, DECIMAL_RESULT or TIME_RESULT. + Example: + SELECT * FROM t1 WHERE varchar_column=DATE'2001-01-01' + return non-unuque values, e.g. '2001-01-01' and '2001-01-01x'. + */ + if (!field_charset->coll->propagate(field_charset, 0, 0) || + item->cmp_type() != STRING_RESULT) + return false; + /* + Can't guarantee uniqueness when comparing to + an item of a different collation. + Example: + SELECT * FROM t1 + WHERE latin1_bin_column = _latin1'A' COLLATE latin1_swedish_ci + return non-unique values 'a' and 'A'. + */ + DTCollation tmp(field_charset, field_derivation, repertoire()); + return !tmp.aggregate(item->collation) && tmp.collation == field_charset; +} + + +bool Field_str::can_be_substituted_to_equal_item(const Context &ctx, + const Item_equal *item_equal) +{ + DBUG_ASSERT(item_equal->compare_type() == STRING_RESULT); + switch (ctx.subst_constraint()) { + case ANY_SUBST: + return ctx.compare_type() == item_equal->compare_type() && + (ctx.compare_type() != STRING_RESULT || + ctx.compare_collation() == item_equal->compare_collation()); + case IDENTITY_SUBST: + return ((charset()->state & MY_CS_BINSORT) && + (charset()->state & MY_CS_NOPAD)); + } + return false; +} + + void Field_num::make_field(Send_field *field) { Field::make_field(field); @@ -1938,8 +2203,8 @@ bool Field::optimize_range(uint idx, uint part) } -Field *Field::new_field(MEM_ROOT *root, TABLE *new_table, - bool keep_type __attribute__((unused))) +Field *Field::make_new_field(MEM_ROOT *root, TABLE *new_table, + bool keep_type __attribute__((unused))) { Field *tmp; if (!(tmp= (Field*) memdup_root(root,(char*) this,size_of()))) @@ -1968,7 +2233,7 @@ Field *Field::new_key_field(MEM_ROOT *root, TABLE *new_table, uchar *new_null_ptr, uint new_null_bit) { Field *tmp; - if ((tmp= new_field(root, new_table, table == new_table))) + if ((tmp= make_new_field(root, new_table, table == new_table))) { tmp->ptr= new_ptr; tmp->null_ptr= new_null_ptr; @@ -2675,7 +2940,7 @@ Field_new_decimal::Field_new_decimal(uint32 len_arg, } -Field *Field_new_decimal::create_from_item (Item *item) +Field *Field_new_decimal::create_from_item(MEM_ROOT *mem_root, Item *item) { uint8 dec= item->decimals; uint8 intg= item->decimal_precision() - dec; @@ -2714,8 +2979,9 @@ Field *Field_new_decimal::create_from_item (Item *item) /* Corrected value fits. */ len= required_length; } - return new Field_new_decimal(len, item->maybe_null, item->name, - dec, item->unsigned_flag); + return new (mem_root) + Field_new_decimal(len, item->maybe_null, item->name, + dec, item->unsigned_flag); } @@ -2756,7 +3022,8 @@ void Field_new_decimal::set_value_on_overflow(my_decimal *decimal_value, If it does, stores the decimal in the buffer using binary format. Otherwise sets maximal number that can be stored in the field. - @param decimal_value my_decimal + @param decimal_value my_decimal + @param [OUT] native_error the error returned by my_decimal2binary(). @retval 0 ok @@ -2764,7 +3031,8 @@ void Field_new_decimal::set_value_on_overflow(my_decimal *decimal_value, 1 error */ -bool Field_new_decimal::store_value(const my_decimal *decimal_value) +bool Field_new_decimal::store_value(const my_decimal *decimal_value, + int *native_error) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; @@ -2793,11 +3061,14 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value) } #endif - if (warn_if_overflow(my_decimal2binary(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW, - decimal_value, ptr, precision, dec))) + *native_error= my_decimal2binary(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW, + decimal_value, ptr, precision, dec); + + if (*native_error == E_DEC_OVERFLOW) { my_decimal buff; DBUG_PRINT("info", ("overflow")); + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); set_value_on_overflow(&buff, decimal_value->sign()); my_decimal2binary(E_DEC_FATAL_ERROR, &buff, ptr, precision, dec); error= 1; @@ -2808,40 +3079,68 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value) } +bool Field_new_decimal::store_value(const my_decimal *decimal_value) +{ + int native_error; + bool rc= store_value(decimal_value, &native_error); + if (!rc && native_error == E_DEC_TRUNCATED) + set_note(WARN_DATA_TRUNCATED, 1); + return rc; +} + + int Field_new_decimal::store(const char *from, uint length, CHARSET_INFO *charset_arg) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; - int err; my_decimal decimal_value; THD *thd= get_thd(); DBUG_ENTER("Field_new_decimal::store(char*)"); - if ((err= str2my_decimal(E_DEC_FATAL_ERROR & - ~(E_DEC_OVERFLOW | E_DEC_BAD_NUM), + const char *end; + int err= str2my_decimal(E_DEC_FATAL_ERROR & + ~(E_DEC_OVERFLOW | E_DEC_BAD_NUM), from, length, charset_arg, - &decimal_value)) && - thd->abort_on_warning) + &decimal_value, &end); + + if (err == E_DEC_OVERFLOW) // Too many digits (>81) in the integer part { - ErrConvString errmsg(from, length, charset_arg); - set_warning_truncated_wrong_value("decimal", errmsg.ptr()); - DBUG_RETURN(err); + set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); + if (!thd->abort_on_warning) + { + set_value_on_overflow(&decimal_value, decimal_value.sign()); + store_decimal(&decimal_value); + } + DBUG_RETURN(1); } - switch (err) { - case E_DEC_TRUNCATED: - set_note(WARN_DATA_TRUNCATED, 1); - break; - case E_DEC_OVERFLOW: - set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); - set_value_on_overflow(&decimal_value, decimal_value.sign()); - break; - case E_DEC_BAD_NUM: + if (thd->count_cuted_fields) + { + if (check_edom_and_important_data_truncation("decimal", + err && err != E_DEC_TRUNCATED, + charset_arg, + from, length, end)) { - ErrConvString errmsg(from, length, charset_arg); - set_warning_truncated_wrong_value("decimal", errmsg.ptr()); - my_decimal_set_zero(&decimal_value); - break; + if (!thd->abort_on_warning) + { + if (err && err != E_DEC_TRUNCATED) + { + /* + If check_decimal() failed because of EDOM-alike error, + (e.g. E_DEC_BAD_NUM), we have to initialize decimal_value to zero. + Note: if check_decimal() failed because of truncation, + decimal_value is alreay properly initialized. + */ + my_decimal_set_zero(&decimal_value); + /* + TODO: check str2my_decimal() with HF. It seems to do + decimal_make_zero() on fatal errors, so my_decimal_set_zero() + is probably not needed here. + */ + } + store_decimal(&decimal_value); + } + DBUG_RETURN(1); } } @@ -2850,8 +3149,22 @@ int Field_new_decimal::store(const char *from, uint length, DBUG_PRINT("enter", ("value: %s", dbug_decimal_as_string(dbug_buff, &decimal_value))); #endif - store_value(&decimal_value); - DBUG_RETURN(err); + int err2; + if (store_value(&decimal_value, &err2)) + DBUG_RETURN(1); + + /* + E_DEC_TRUNCATED means minor truncation, a note should be enough: + - in err: str2my_decimal() truncated '1e-1000000000000' to 0.0 + - in err2: store_value() truncated 1.123 to 1.12, e.g. for DECIMAL(10,2) + Also, we send a note if a string had some trailing spaces: '1.12 ' + */ + if (thd->count_cuted_fields && + (err == E_DEC_TRUNCATED || + err2 == E_DEC_TRUNCATED || + end < from + length)) + set_note(WARN_DATA_TRUNCATED, 1); + DBUG_RETURN(0); } @@ -2915,7 +3228,7 @@ int Field_new_decimal::store_decimal(const my_decimal *decimal_value) } -int Field_new_decimal::store_time_dec(MYSQL_TIME *ltime, uint dec) +int Field_new_decimal::store_time_dec(MYSQL_TIME *ltime, uint dec_arg) { my_decimal decimal_value; return store_value(date2my_decimal(ltime, &decimal_value)); @@ -2968,6 +3281,14 @@ String *Field_new_decimal::val_str(String *val_buffer, } +bool Field_new_decimal::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +{ + my_decimal value; + return decimal_to_datetime_with_warn(val_decimal(&value), + ltime, fuzzydate, field_name); +} + + int Field_new_decimal::cmp(const uchar *a,const uchar*b) { return memcmp(a, b, bin_size); @@ -3111,7 +3432,41 @@ Field_new_decimal::unpack(uchar* to, const uchar *from, const uchar *from_end, return from+len; } -int Field_num::store_time_dec(MYSQL_TIME *ltime, uint dec) + +Item *Field_new_decimal::get_equal_const_item(THD *thd, const Context &ctx, + Item *const_item) +{ + if (flags & ZEROFILL_FLAG) + return Field_num::get_equal_zerofill_const_item(thd, ctx, const_item); + switch (ctx.subst_constraint()) { + case IDENTITY_SUBST: + if (const_item->field_type() != MYSQL_TYPE_NEWDECIMAL || + const_item->decimal_scale() != decimals()) + { + my_decimal *val, val_buffer, val_buffer2; + if (!(val= const_item->val_decimal(&val_buffer))) + { + DBUG_ASSERT(0); + return const_item; + } + /* + Truncate or extend the decimal value to the scale of the field. + See comments about truncation in the same place in + Field_time::get_equal_const_item(). + */ + my_decimal_round(E_DEC_FATAL_ERROR, val, decimals(), true, &val_buffer2); + return new (thd->mem_root) Item_decimal(thd, field_name, &val_buffer2, + decimals(), field_length); + } + break; + case ANY_SUBST: + break; + } + return const_item; +} + + +int Field_num::store_time_dec(MYSQL_TIME *ltime, uint dec_arg) { longlong v= TIME_to_ulonglong(ltime); if (ltime->neg == 0) @@ -4029,15 +4384,7 @@ void Field_longlong::sql_type(String &res) const int Field_float::store(const char *from,uint len,CHARSET_INFO *cs) { int error; - char *end; - double nr= my_strntod(cs,(char*) from,len,&end,&error); - if (error || (!len || ((uint) (end-from) != len && - get_thd()->count_cuted_fields))) - { - set_warning(error ? ER_WARN_DATA_OUT_OF_RANGE : WARN_DATA_TRUNCATED, 1); - error= error ? 1 : 2; - } - Field_float::store(nr); + Field_float::store(get_double(from, len, cs, &error)); return error; } @@ -4216,15 +4563,7 @@ void Field_float::sql_type(String &res) const int Field_double::store(const char *from,uint len,CHARSET_INFO *cs) { int error; - char *end; - double nr= my_strntod(cs,(char*) from, len, &end, &error); - if (error || (!len || ((uint) (end-from) != len && - get_thd()->count_cuted_fields))) - { - set_warning(error ? ER_WARN_DATA_OUT_OF_RANGE : WARN_DATA_TRUNCATED, 1); - error= error ? 1 : 2; - } - Field_double::store(nr); + Field_double::store(get_double(from, len, cs, &error)); return error; } @@ -4376,7 +4715,7 @@ int Field_real::store_decimal(const my_decimal *dm) return store(dbl); } -int Field_real::store_time_dec(MYSQL_TIME *ltime, uint dec) +int Field_real::store_time_dec(MYSQL_TIME *ltime, uint dec_arg) { return store(TIME_to_double(ltime)); } @@ -4408,10 +4747,11 @@ longlong Field_double::val_int_from_real(bool want_unsigned_result) */ if (error && !want_unsigned_result) { + THD *thd= get_thd(); ErrConvDouble err(j); - push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE, - ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER", + ER_THD(thd, ER_TRUNCATED_WRONG_VALUE), "INTEGER", err.ptr()); } return res; @@ -4434,6 +4774,26 @@ bool Field_real::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) } +Item *Field_real::get_equal_const_item(THD *thd, const Context &ctx, + Item *const_item) +{ + if (flags & ZEROFILL_FLAG) + return Field_num::get_equal_zerofill_const_item(thd, ctx, const_item); + switch (ctx.subst_constraint()) { + case IDENTITY_SUBST: + if (const_item->decimal_scale() != Field_real::decimals()) + { + double val= const_item->val_real(); + return new (thd->mem_root) Item_float(thd, val, Field_real::decimals()); + } + break; + case ANY_SUBST: + break; + } + return const_item; +} + + String *Field_double::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { @@ -4585,11 +4945,12 @@ Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg, } -my_time_t Field_timestamp::get_timestamp(ulong *sec_part) const +my_time_t Field_timestamp::get_timestamp(const uchar *pos, + ulong *sec_part) const { ASSERT_COLUMN_MARKED_FOR_READ; *sec_part= 0; - return sint4korr(ptr); + return sint4korr(pos); } @@ -4710,6 +5071,23 @@ int Field_timestamp::store(longlong nr, bool unsigned_val) } +int Field_timestamp::store_timestamp(Field_timestamp *from) +{ + ulong sec_part; + my_time_t ts= from->get_timestamp(&sec_part); + store_TIME(ts, sec_part); + if (!ts && !sec_part && get_thd()->variables.sql_mode & MODE_NO_ZERO_DATE) + { + ErrConvString s( + STRING_WITH_LEN("0000-00-00 00:00:00.000000") - (decimals() ? 6 - decimals() : 7), + system_charset_info); + set_datetime_warning(WARN_DATA_TRUNCATED, &s, MYSQL_TIMESTAMP_DATETIME, 1); + return 1; + } + return 0; +} + + double Field_timestamp::val_real(void) { return (double) Field_timestamp::val_int(); @@ -4801,6 +5179,16 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) } +bool +Field_timestamp::validate_value_in_record(THD *thd, const uchar *record) const +{ + DBUG_ASSERT(!is_null_in_record(record)); + ulong sec_part; + return !get_timestamp(ptr_in_record(record), &sec_part) && !sec_part && + (sql_mode_for_dates(thd) & TIME_NO_ZERO_DATE) != 0; +} + + bool Field_timestamp::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) { THD *thd= get_thd(); @@ -4811,7 +5199,7 @@ bool Field_timestamp::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) { /* Zero time is "000000" */ if (fuzzydate & TIME_NO_ZERO_DATE) return 1; - bzero((char*) ltime,sizeof(*ltime)); + set_zero_time(ltime, MYSQL_TIMESTAMP_DATETIME); } else { @@ -4863,9 +5251,8 @@ void Field_timestamp::sql_type(String &res) const int Field_timestamp::set_time() { - THD *thd= get_thd(); set_notnull(); - store_TIME(thd->query_start(), 0); + store_TIME(get_thd()->query_start(), 0); return 0; } @@ -4983,11 +5370,12 @@ void Field_timestamp_hires::store_TIME(my_time_t timestamp, ulong sec_part) store_bigendian(sec_part_shift(sec_part, dec), ptr+4, sec_part_bytes[dec]); } -my_time_t Field_timestamp_hires::get_timestamp(ulong *sec_part) const +my_time_t Field_timestamp_hires::get_timestamp(const uchar *pos, + ulong *sec_part) const { ASSERT_COLUMN_MARKED_FOR_READ; - *sec_part= (long)sec_part_unshift(read_bigendian(ptr+4, sec_part_bytes[dec]), dec); - return mi_uint4korr(ptr); + *sec_part= (long)sec_part_unshift(read_bigendian(pos+4, sec_part_bytes[dec]), dec); + return mi_uint4korr(pos); } double Field_timestamp_with_dec::val_real(void) @@ -5025,8 +5413,8 @@ int Field_timestamp::store_decimal(const my_decimal *d) } else tmp= number_to_datetime(nr, sec_part, <ime, TIME_NO_ZERO_IN_DATE | - (thd->variables.sql_mode & - MODE_NO_ZERO_DATE), &error); + (thd->variables.sql_mode & + MODE_NO_ZERO_DATE), &error); return store_TIME_with_warning(thd, <ime, &str, error, tmp != -1); } @@ -5035,7 +5423,8 @@ int Field_timestamp_with_dec::set_time() { THD *thd= get_thd(); set_notnull(); - store_TIME(thd->query_start(), thd->query_start_sec_part()); + // Avoid writing microseconds into binlog for FSP=0 + store_TIME(thd->query_start(), decimals() ? thd->query_start_sec_part() : 0); return 0; } @@ -5086,10 +5475,11 @@ void Field_timestampf::store_TIME(my_time_t timestamp, ulong sec_part) } -my_time_t Field_timestampf::get_timestamp(ulong *sec_part) const +my_time_t Field_timestampf::get_timestamp(const uchar *pos, + ulong *sec_part) const { struct timeval tm; - my_timestamp_from_binary(&tm, ptr, dec); + my_timestamp_from_binary(&tm, pos, dec); *sec_part= tm.tv_usec; return tm.tv_sec; } @@ -5225,19 +5615,30 @@ int Field_temporal_with_date::store_time_dec(MYSQL_TIME *ltime, uint dec) */ have_smth_to_conv= false; error= MYSQL_TIME_WARN_OUT_OF_RANGE; - goto store; } - - /* - We don't perform range checking here since values stored in TIME - structure always fit into DATETIME range. - */ - have_smth_to_conv= !check_date(&l_time, pack_time(&l_time) != 0, - sql_mode_for_dates(current_thd), &error); -store: + else + { + /* + We don't perform range checking here since values stored in TIME + structure always fit into DATETIME range. + */ + have_smth_to_conv= !check_date(&l_time, pack_time(&l_time) != 0, + sql_mode_for_dates(get_thd()), &error); + } return store_TIME_with_warning(&l_time, &str, error, have_smth_to_conv); } + +bool +Field_temporal_with_date::validate_value_in_record(THD *thd, + const uchar *record) const +{ + DBUG_ASSERT(!is_null_in_record(record)); + MYSQL_TIME ltime; + return get_TIME(<ime, ptr_in_record(record), sql_mode_for_dates(thd)); +} + + my_decimal *Field_temporal::val_decimal(my_decimal *d) { MYSQL_TIME ltime; @@ -5249,6 +5650,63 @@ my_decimal *Field_temporal::val_decimal(my_decimal *d) return TIME_to_my_decimal(<ime, d); } + +bool Field_temporal::can_optimize_keypart_ref(const Item_bool_func *cond, + const Item *value) const +{ + return true; // Field is of TIME_RESULT, which supersedes everything else. +} + + +bool Field_temporal::can_optimize_group_min_max(const Item_bool_func *cond, + const Item *const_item) const +{ + return true; // Field is of TIME_RESULT, which supersedes everything else. +} + + +Item *Field_temporal::get_equal_const_item_datetime(THD *thd, + const Context &ctx, + Item *const_item) +{ + switch (ctx.subst_constraint()) { + case IDENTITY_SUBST: + if ((const_item->field_type() != MYSQL_TYPE_DATETIME && + const_item->field_type() != MYSQL_TYPE_TIMESTAMP) || + const_item->decimals != decimals()) + { + MYSQL_TIME ltime; + if (const_item->field_type() == MYSQL_TYPE_TIME ? + const_item->get_date_with_conversion(<ime, 0) : + const_item->get_date(<ime, 0)) + return NULL; + /* + See comments about truncation in the same place in + Field_time::get_equal_const_item(). + */ + return new (thd->mem_root) Item_datetime_literal(thd, <ime, + decimals()); + } + break; + case ANY_SUBST: + if (!is_temporal_type_with_date(const_item->field_type())) + { + MYSQL_TIME ltime; + if (const_item->get_date_with_conversion(<ime, + TIME_FUZZY_DATES | + TIME_INVALID_DATES)) + return NULL; + return new (thd->mem_root) + Item_datetime_literal_for_invalid_dates(thd, <ime, + ltime.second_part ? + TIME_SECOND_PART_DIGITS : 0); + } + break; + } + return const_item; +} + + /**************************************************************************** ** time type ** In string context: HH:MM:SS @@ -5449,7 +5907,7 @@ bool Field_time::check_zero_in_date_with_warn(ulonglong fuzzydate) THD *thd= get_thd(); push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, - ER(ER_WARN_DATA_OUT_OF_RANGE), field_name, + ER_THD(thd, ER_WARN_DATA_OUT_OF_RANGE), field_name, thd->get_stmt_da()->current_row_for_warning()); return true; } @@ -5516,7 +5974,7 @@ void Field_time::sql_type(String &res) const res.set_ascii(STRING_WITH_LEN("time")); return; } - const CHARSET_INFO *cs= res.charset(); + CHARSET_INFO *cs= res.charset(); res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(), "time(%d)", decimals())); } @@ -5548,6 +6006,66 @@ int Field_time::store_decimal(const my_decimal *d) return store_TIME_with_warning(<ime, &str, was_cut, have_smth_to_conv); } + +Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx, + Item *const_item) +{ + switch (ctx.subst_constraint()) { + case ANY_SUBST: + if (const_item->field_type() != MYSQL_TYPE_TIME) + { + MYSQL_TIME ltime; + // Get the value of const_item with conversion from DATETIME to TIME + if (const_item->get_time_with_conversion(thd, <ime, + TIME_TIME_ONLY | + TIME_FUZZY_DATES | + TIME_INVALID_DATES)) + return NULL; + /* + Replace a DATE/DATETIME constant to a TIME constant: + WHERE LENGTH(time_column)=8 + AND time_column=TIMESTAMP'2015-08-30 10:20:30'; + to: + WHERE LENGTH(time_column)=10 + AND time_column=TIME'10:20:30' + + (assuming CURRENT_DATE is '2015-08-30' + */ + return new (thd->mem_root) Item_time_literal(thd, <ime, + ltime.second_part ? + TIME_SECOND_PART_DIGITS : + 0); + } + break; + case IDENTITY_SUBST: + if (const_item->field_type() != MYSQL_TYPE_TIME || + const_item->decimals != decimals()) + { + MYSQL_TIME ltime; + if (const_item->get_time_with_conversion(thd, <ime, TIME_TIME_ONLY)) + return NULL; + /* + Note, the value returned in "ltime" can have more fractional + digits that decimals(). The Item_time_literal constructor will + truncate these digits. We could disallow propagation is such + cases, but it's still useful (and safe) to optimize: + WHERE time0_column='00:00:00.123' AND LENGTH(a)=12 + to + WHERE time0_column='00:00:00.123' AND LENGTH(TIME'00:00:00')=12 + and then to + WHERE FALSE + The original WHERE would do the full table scan (in case of no keys). + The optimized WHERE will return with "Impossible WHERE", without + having to do the full table scan. + */ + return new (thd->mem_root) Item_time_literal(thd, <ime, decimals()); + } + break; + } + return const_item; +} + + uint32 Field_time_hires::pack_length() const { return time_hires_bytes[dec]; @@ -5714,7 +6232,7 @@ int Field_year::store(longlong nr, bool unsigned_val) } -int Field_year::store_time_dec(MYSQL_TIME *ltime, uint dec) +int Field_year::store_time_dec(MYSQL_TIME *ltime, uint dec_arg) { ErrConvTime str(ltime); if (Field_year::store(ltime->year, 0)) @@ -5824,18 +6342,25 @@ longlong Field_date::val_int(void) } +bool Field_date::get_TIME(MYSQL_TIME *ltime, const uchar *pos, + ulonglong fuzzydate) const +{ + ASSERT_COLUMN_MARKED_FOR_READ; + int32 tmp= sint4korr(pos); + ltime->year= (int) ((uint32) tmp/10000L % 10000); + ltime->month= (int) ((uint32) tmp/100 % 100); + ltime->day= (int) ((uint32) tmp % 100); + ltime->time_type= MYSQL_TIMESTAMP_DATE; + ltime->hour= ltime->minute= ltime->second= ltime->second_part= ltime->neg= 0; + return validate_MMDD(tmp, ltime->month, ltime->day, fuzzydate); +} + + String *Field_date::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { - ASSERT_COLUMN_MARKED_FOR_READ; MYSQL_TIME ltime; - int32 tmp; - tmp=sint4korr(ptr); - ltime.neg= 0; - ltime.year= (int) ((uint32) tmp/10000L % 10000); - ltime.month= (int) ((uint32) tmp/100 % 100); - ltime.day= (int) ((uint32) tmp % 100); - + get_TIME(<ime, ptr, 0); val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH); uint length= (uint) my_date_to_str(<ime, const_cast<char*>(val_buffer->ptr())); @@ -5936,19 +6461,17 @@ String *Field_newdate::val_str(String *val_buffer, } -bool Field_newdate::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) +bool Field_newdate::get_TIME(MYSQL_TIME *ltime, const uchar *pos, + ulonglong fuzzydate) const { - uint32 tmp=(uint32) uint3korr(ptr); + ASSERT_COLUMN_MARKED_FOR_READ; + uint32 tmp=(uint32) uint3korr(pos); ltime->day= tmp & 31; ltime->month= (tmp >> 5) & 15; ltime->year= (tmp >> 9); ltime->time_type= MYSQL_TIMESTAMP_DATE; ltime->hour= ltime->minute= ltime->second= ltime->second_part= ltime->neg= 0; - if (!tmp) - return fuzzydate & TIME_NO_ZERO_DATE; - if (!ltime->month || !ltime->day) - return fuzzydate & TIME_NO_ZERO_IN_DATE; - return 0; + return validate_MMDD(tmp, ltime->month, ltime->day, fuzzydate); } @@ -5975,6 +6498,56 @@ void Field_newdate::sql_type(String &res) const } +Item *Field_newdate::get_equal_const_item(THD *thd, const Context &ctx, + Item *const_item) +{ + switch (ctx.subst_constraint()) { + case ANY_SUBST: + if (!is_temporal_type_with_date(const_item->field_type())) + { + MYSQL_TIME ltime; + // Get the value of const_item with conversion from TIME to DATETIME + if (const_item->get_date_with_conversion(<ime, + TIME_FUZZY_DATES | TIME_INVALID_DATES)) + return NULL; + /* + Replace the constant to a DATE or DATETIME constant. + Example: + WHERE LENGTH(date_column)=10 + AND date_column=TIME'10:20:30'; + to: + WHERE LENGTH(date_column)=10 + AND date_column=TIMESTAMP'2015-08-30 10:20:30' + + (assuming CURRENT_DATE is '2015-08-30' + */ + if (non_zero_hhmmssuu(<ime)) + return new (thd->mem_root) + Item_datetime_literal_for_invalid_dates(thd, <ime, + ltime.second_part ? + TIME_SECOND_PART_DIGITS : 0); + datetime_to_date(<ime); + return new (thd->mem_root) + Item_date_literal_for_invalid_dates(thd, <ime); + } + break; + case IDENTITY_SUBST: + if (const_item->field_type() != MYSQL_TYPE_DATE) + { + MYSQL_TIME ltime; + if (const_item->field_type() == MYSQL_TYPE_TIME ? + const_item->get_date_with_conversion(<ime, 0) : + const_item->get_date(<ime, 0)) + return NULL; + datetime_to_date(<ime); + return new (thd->mem_root) Item_date_literal(thd, <ime); + } + break; + } + return const_item; +} + + /**************************************************************************** ** datetime type ** In string context: YYYY-MM-DD HH:MM:DD @@ -6056,9 +6629,11 @@ String *Field_datetime::val_str(String *val_buffer, return val_buffer; } -bool Field_datetime::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Field_datetime::get_TIME(MYSQL_TIME *ltime, const uchar *pos, + ulonglong fuzzydate) const { - longlong tmp=Field_datetime::val_int(); + ASSERT_COLUMN_MARKED_FOR_READ; + longlong tmp= sint8korr(pos); uint32 part1,part2; part1=(uint32) (tmp/1000000LL); part2=(uint32) (tmp - (ulonglong) part1*1000000LL); @@ -6072,13 +6647,10 @@ bool Field_datetime::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) ltime->day= (int) (part1%100); ltime->month= (int) (part1/100%100); ltime->year= (int) (part1/10000); - if (!tmp) - return fuzzydate & TIME_NO_ZERO_DATE; - if (!ltime->month || !ltime->day) - return fuzzydate & TIME_NO_ZERO_IN_DATE; - return 0; + return validate_MMDD(tmp, ltime->month, ltime->day, fuzzydate); } + int Field_datetime::cmp(const uchar *a_ptr, const uchar *b_ptr) { longlong a,b; @@ -6190,17 +6762,17 @@ String *Field_datetime_with_dec::val_str(String *str, return str; } -bool Field_datetime_hires::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + +bool Field_datetime_hires::get_TIME(MYSQL_TIME *ltime, const uchar *pos, + ulonglong fuzzydate) const { - ulonglong packed= read_bigendian(ptr, Field_datetime_hires::pack_length()); + ASSERT_COLUMN_MARKED_FOR_READ; + ulonglong packed= read_bigendian(pos, Field_datetime_hires::pack_length()); unpack_time(sec_part_unshift(packed, dec), ltime); - if (!packed) - return fuzzydate & TIME_NO_ZERO_DATE; - if (!ltime->month || !ltime->day) - return fuzzydate & TIME_NO_ZERO_IN_DATE; - return 0; + return validate_MMDD(packed, ltime->month, ltime->day, fuzzydate); } + uint32 Field_datetime_hires::pack_length() const { return datetime_hires_bytes[dec]; @@ -6237,18 +6809,15 @@ void Field_datetimef::store_TIME(MYSQL_TIME *ltime) my_datetime_packed_to_binary(tmp, ptr, dec); } -bool Field_datetimef::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +bool Field_datetimef::get_TIME(MYSQL_TIME *ltime, const uchar *pos, + ulonglong fuzzydate) const { - longlong tmp= my_datetime_packed_from_binary(ptr, dec); + ASSERT_COLUMN_MARKED_FOR_READ; + longlong tmp= my_datetime_packed_from_binary(pos, dec); TIME_from_longlong_datetime_packed(ltime, tmp); - if (!tmp) - return fuzzydate & TIME_NO_ZERO_DATE; - if (!ltime->month || !ltime->day) - return fuzzydate & TIME_NO_ZERO_IN_DATE; - return false; + return validate_MMDD(tmp, ltime->month, ltime->day, fuzzydate); } - /**************************************************************************** ** string type ** A string may be varchar or binary @@ -6260,10 +6829,9 @@ bool Field_datetimef::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) SYNOPSIS check_string_copy_error() - field - Field - well_formed_error_pos - where not well formed data was first met - cannot_convert_error_pos - where a not-convertable character was first met - end - end of the string + copier - the conversion status + end - the very end of the source string + that was just copied cs - character set of the string NOTES @@ -6281,16 +6849,14 @@ bool Field_datetimef::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) */ bool -Field_longstr::check_string_copy_error(const char *well_formed_error_pos, - const char *cannot_convert_error_pos, +Field_longstr::check_string_copy_error(const String_copier *copier, const char *end, CHARSET_INFO *cs) { const char *pos; char tmp[32]; - if (!(pos= well_formed_error_pos) && - !(pos= cannot_convert_error_pos)) + if (!(pos= copier->most_important_error_pos())) return FALSE; convert_to_printable(tmp, sizeof(tmp), pos, (end - pos), cs, 6); @@ -6350,20 +6916,15 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; uint copy_length; - const char *well_formed_error_pos; - const char *cannot_convert_error_pos; - const char *from_end_pos; + String_copier copier; /* See the comment for Field_long::store(long long) */ DBUG_ASSERT(!table || table->in_use == current_thd); - copy_length= well_formed_copy_nchars(field_charset, + copy_length= copier.well_formed_copy(field_charset, (char*) ptr, field_length, cs, from, length, - field_length / field_charset->mbmaxlen, - &well_formed_error_pos, - &cannot_convert_error_pos, - &from_end_pos); + field_length / field_charset->mbmaxlen); /* Append spaces if the string was shorter than the field. */ if (copy_length < field_length) @@ -6371,11 +6932,7 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) field_length-copy_length, field_charset->pad_char); - if (check_string_copy_error(well_formed_error_pos, - cannot_convert_error_pos, from + length, cs)) - return 2; - - return report_if_important_data(from_end_pos, from + length, FALSE); + return check_conversion_status(&copier, from + length, cs, false); } @@ -6448,51 +7005,98 @@ uint32 Field_longstr::max_data_length() const } +bool +Field_longstr::cmp_to_string_with_same_collation(const Item_bool_func *cond, + const Item *item) const +{ + return item->cmp_type() == STRING_RESULT && + charset() == cond->compare_collation(); +} + + +bool +Field_longstr::cmp_to_string_with_stricter_collation(const Item_bool_func *cond, + const Item *item) const +{ + return item->cmp_type() == STRING_RESULT && + (charset() == cond->compare_collation() || + cond->compare_collation()->state & MY_CS_BINSORT); +} + + +bool Field_longstr::can_optimize_keypart_ref(const Item_bool_func *cond, + const Item *item) const +{ + DBUG_ASSERT(cmp_type() == STRING_RESULT); + return cmp_to_string_with_stricter_collation(cond, item); +} + + +bool Field_longstr::can_optimize_hash_join(const Item_bool_func *cond, + const Item *item) const +{ + DBUG_ASSERT(cmp_type() == STRING_RESULT); + return cmp_to_string_with_same_collation(cond, item); +} + + +bool Field_longstr::can_optimize_group_min_max(const Item_bool_func *cond, + const Item *const_item) const +{ + /* + Can't use indexes when comparing a string to a number or a date + Don't use an index when comparing strings of different collations. + */ + DBUG_ASSERT(cmp_type() == STRING_RESULT); + return cmp_to_string_with_same_collation(cond, const_item); +} + + +bool Field_longstr::can_optimize_range(const Item_bool_func *cond, + const Item *item, + bool is_eq_func) const +{ + return is_eq_func ? + cmp_to_string_with_stricter_collation(cond, item) : + cmp_to_string_with_same_collation(cond, item); +} + + +/** + This overrides the default behavior of the parent constructor + Warn_filter(thd) to suppress notes about trailing spaces in case of CHAR(N), + as they are truncated during val_str(). + We still do want truncation notes in case of BINARY(N), + as trailing spaces are not truncated in val_str(). +*/ +Field_string::Warn_filter_string::Warn_filter_string(const THD *thd, + const Field_string *field) + :Warn_filter(!thd->no_errors, + !thd->no_errors && + field->Field_string::charset() == &my_charset_bin) +{ } + + double Field_string::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; - int error; - char *end; - CHARSET_INFO *cs= charset(); - double result; - - result= my_strntod(cs,(char*) ptr,field_length,&end,&error); - if (!get_thd()->no_errors && - (error || (field_length != (uint32)(end - (char*) ptr) && - !check_if_only_end_space(cs, end, - (char*) ptr + field_length)))) - { - ErrConvString err((char*) ptr, field_length, cs); - push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, - ER_TRUNCATED_WRONG_VALUE, - ER(ER_TRUNCATED_WRONG_VALUE), "DOUBLE", - err.ptr()); - } - return result; + THD *thd= get_thd(); + return Converter_strntod_with_warn(get_thd(), + Warn_filter_string(thd, this), + Field_string::charset(), + (const char *) ptr, + field_length).result(); } longlong Field_string::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; - int error; - char *end; - CHARSET_INFO *cs= charset(); - longlong result; - - result= my_strntoll(cs, (char*) ptr,field_length,10,&end,&error); - if (!get_thd()->no_errors && - (error || (field_length != (uint32)(end - (char*) ptr) && - !check_if_only_end_space(cs, end, - (char*) ptr + field_length)))) - { - ErrConvString err((char*) ptr, field_length, cs); - push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, - ER_TRUNCATED_WRONG_VALUE, - ER(ER_TRUNCATED_WRONG_VALUE), - "INTEGER", err.ptr()); - } - return result; + THD *thd= get_thd(); + return Converter_strntoll_with_warn(thd, Warn_filter_string(thd, this), + Field_string::charset(), + (const char *) ptr, + field_length).result(); } @@ -6515,29 +7119,17 @@ String *Field_string::val_str(String *val_buffer __attribute__((unused)), } -my_decimal *Field_longstr::val_decimal_from_str(const char *str, - uint length, - CHARSET_INFO *cs, - my_decimal *decimal_value) -{ - int err= str2my_decimal(E_DEC_FATAL_ERROR, str, length, cs, decimal_value); - if (!get_thd()->no_errors && err) - { - ErrConvString errmsg(str, length, cs); - push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, - ER_TRUNCATED_WRONG_VALUE, - ER(ER_TRUNCATED_WRONG_VALUE), - "DECIMAL", errmsg.ptr()); - } - return decimal_value; -} - - my_decimal *Field_string::val_decimal(my_decimal *decimal_value) { ASSERT_COLUMN_MARKED_FOR_READ; - return val_decimal_from_str((const char *) ptr, field_length, - Field_string::charset(), decimal_value); + THD *thd= get_thd(); + Converter_str2my_decimal_with_warn(thd, + Warn_filter_string(thd, this), + E_DEC_FATAL_ERROR, + Field_string::charset(), + (const char *) ptr, + field_length, decimal_value); + return decimal_value; } @@ -6800,14 +7392,15 @@ uint Field_string::get_key_image(uchar *buff, uint length, imagetype type_arg) } -Field *Field_string::new_field(MEM_ROOT *root, TABLE *new_table, - bool keep_type) +Field *Field_string::make_new_field(MEM_ROOT *root, TABLE *new_table, + bool keep_type) { Field *field; if (type() != MYSQL_TYPE_VAR_STRING || keep_type) - field= Field::new_field(root, new_table, keep_type); - else if ((field= new Field_varstring(field_length, maybe_null(), field_name, - new_table->s, charset()))) + field= Field::make_new_field(root, new_table, keep_type); + else if ((field= new (root) Field_varstring(field_length, maybe_null(), + field_name, + new_table->s, charset()))) { /* Old VARCHAR field which should be modified to a VARCHAR on copy @@ -6816,10 +7409,11 @@ Field *Field_string::new_field(MEM_ROOT *root, TABLE *new_table, */ field->init(new_table); /* - Normally orig_table is different from table only if field was created - via ::new_field. Here we alter the type of field, so ::new_field is - not applicable. But we still need to preserve the original field - metadata for the client-server protocol. + Normally orig_table is different from table only if field was + created via ::make_new_field. Here we alter the type of field, + so ::make_new_field is not applicable. But we still need to + preserve the original field metadata for the client-server + protocol. */ field->orig_table= orig_table; } @@ -6867,29 +7461,19 @@ int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; uint copy_length; - const char *well_formed_error_pos; - const char *cannot_convert_error_pos; - const char *from_end_pos; + String_copier copier; - copy_length= well_formed_copy_nchars(field_charset, + copy_length= copier.well_formed_copy(field_charset, (char*) ptr + length_bytes, field_length, cs, from, length, - field_length / field_charset->mbmaxlen, - &well_formed_error_pos, - &cannot_convert_error_pos, - &from_end_pos); - + field_length / field_charset->mbmaxlen); if (length_bytes == 1) *ptr= (uchar) copy_length; else int2store(ptr, copy_length); - if (check_string_copy_error(well_formed_error_pos, - cannot_convert_error_pos, from + length, cs)) - return 2; - - return report_if_important_data(from_end_pos, from + length, TRUE); + return check_conversion_status(&copier, from + length, cs, true); } @@ -6910,54 +7494,30 @@ int Field_varstring::store(longlong nr, bool unsigned_val) double Field_varstring::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; - int error; - char *end; - double result; - CHARSET_INFO* cs= charset(); - - uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); - result= my_strntod(cs, (char*)ptr+length_bytes, length, &end, &error); - - if (!get_thd()->no_errors && - (error || (length != (uint)(end - (char*)ptr+length_bytes) && - !check_if_only_end_space(cs, end, (char*)ptr+length_bytes+length)))) - { - push_numerical_conversion_warning(current_thd, (char*)ptr+length_bytes, - length, cs,"DOUBLE", - ER_TRUNCATED_WRONG_VALUE); - } - return result; + THD *thd= get_thd(); + return Converter_strntod_with_warn(thd, Warn_filter(thd), + Field_varstring::charset(), + (const char *) get_data(), + get_length()).result(); } longlong Field_varstring::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; - int error; - char *end; - CHARSET_INFO *cs= charset(); - - uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); - longlong result= my_strntoll(cs, (char*) ptr+length_bytes, length, 10, - &end, &error); - - if (!get_thd()->no_errors && - (error || (length != (uint)(end - (char*)ptr+length_bytes) && - !check_if_only_end_space(cs, end, (char*)ptr+length_bytes+length)))) - { - push_numerical_conversion_warning(current_thd, (char*)ptr+length_bytes, - length, cs, "INTEGER", - ER_TRUNCATED_WRONG_VALUE); - } - return result; + THD *thd= get_thd(); + return Converter_strntoll_with_warn(thd, Warn_filter(thd), + Field_varstring::charset(), + (const char *) get_data(), + get_length()).result(); } + String *Field_varstring::val_str(String *val_buffer __attribute__((unused)), String *val_ptr) { ASSERT_COLUMN_MARKED_FOR_READ; - uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); - val_ptr->set((const char*) ptr+length_bytes, length, field_charset); + val_ptr->set((const char*) get_data(), get_length(), field_charset); return val_ptr; } @@ -6965,9 +7525,14 @@ String *Field_varstring::val_str(String *val_buffer __attribute__((unused)), my_decimal *Field_varstring::val_decimal(my_decimal *decimal_value) { ASSERT_COLUMN_MARKED_FOR_READ; - uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); - return val_decimal_from_str((const char *) ptr + length_bytes, length, - Field_varstring::charset(), decimal_value); + THD *thd= get_thd(); + Converter_str2my_decimal_with_warn(thd, Warn_filter(thd), + E_DEC_FATAL_ERROR, + Field_varstring::charset(), + (const char *) get_data(), + get_length(), decimal_value); + return decimal_value; + } @@ -7186,7 +7751,8 @@ uint Field_varstring::max_packed_col_length(uint max_length) return (max_length > 255 ? 2 : 1)+max_length; } -uint Field_varstring::get_key_image(uchar *buff, uint length, imagetype type) +uint Field_varstring::get_key_image(uchar *buff, uint length, + imagetype type_arg) { uint f_length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); uint local_char_length= length / field_charset->mbmaxlen; @@ -7240,11 +7806,12 @@ int Field_varstring::cmp_binary(const uchar *a_ptr, const uchar *b_ptr, } -Field *Field_varstring::new_field(MEM_ROOT *root, TABLE *new_table, - bool keep_type) +Field *Field_varstring::make_new_field(MEM_ROOT *root, TABLE *new_table, + bool keep_type) { - Field_varstring *res= (Field_varstring*) Field::new_field(root, new_table, - keep_type); + Field_varstring *res= (Field_varstring*) Field::make_new_field(root, + new_table, + keep_type); if (res) res->length_bytes= length_bytes; return res; @@ -7364,9 +7931,8 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; uint copy_length, new_length; - const char *well_formed_error_pos; - const char *cannot_convert_error_pos; - const char *from_end_pos, *tmp; + String_copier copier; + char *tmp; char buff[STRING_BUFFER_USUAL_SIZE]; String tmpstr(buff,sizeof(buff), &my_charset_bin); @@ -7377,6 +7943,35 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) } /* + For min/max fields of statistical data 'table' is set to NULL. + It could not be otherwise as this data is shared by many instances + of the same base table. + */ + + if (table && table->blob_storage) // GROUP_CONCAT with ORDER BY | DISTINCT + { + DBUG_ASSERT(!f_is_hex_escape(flags)); + DBUG_ASSERT(field_charset == cs); + DBUG_ASSERT(length <= max_data_length()); + + new_length= length; + copy_length= table->in_use->variables.group_concat_max_len; + if (new_length > copy_length) + { + int well_formed_error; + new_length= cs->cset->well_formed_len(cs, from, from + copy_length, + new_length, &well_formed_error); + table->blob_storage->set_truncated_value(true); + } + if (!(tmp= table->blob_storage->store(from, new_length))) + goto oom_error; + + Field_blob::store_length(new_length); + bmove(ptr + packlength, (uchar*) &tmp, sizeof(char*)); + return 0; + } + + /* If the 'from' address is in the range of the temporary 'value'- object we need to copy the content to a different location or it will be invalidated when the 'value'-object is reallocated to make room for @@ -7402,40 +7997,24 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) new_length= MY_MIN(max_data_length(), field_charset->mbmaxlen * length); if (value.alloc(new_length)) goto oom_error; - + tmp= const_cast<char*>(value.ptr()); if (f_is_hex_escape(flags)) { copy_length= my_copy_with_hex_escaping(field_charset, - (char*) value.ptr(), new_length, - from, length); + tmp, new_length, + from, length); Field_blob::store_length(copy_length); - tmp= value.ptr(); bmove(ptr + packlength, (uchar*) &tmp, sizeof(char*)); return 0; } - /* - "length" is OK as "nchars" argument to well_formed_copy_nchars as this - is never used to limit the length of the data. The cut of long data - is done with the new_length value. - */ - copy_length= well_formed_copy_nchars(field_charset, + copy_length= copier.well_formed_copy(field_charset, (char*) value.ptr(), new_length, - cs, from, length, - length, - &well_formed_error_pos, - &cannot_convert_error_pos, - &from_end_pos); - + cs, from, length); Field_blob::store_length(copy_length); - tmp= value.ptr(); bmove(ptr+packlength,(uchar*) &tmp,sizeof(char*)); - if (check_string_copy_error(well_formed_error_pos, - cannot_convert_error_pos, from + length, cs)) - return 2; - - return report_if_important_data(from_end_pos, from + length, TRUE); + return check_conversion_status(&copier, from + length, cs, true); oom_error: /* Fatal OOM error */ @@ -7463,32 +8042,31 @@ int Field_blob::store(longlong nr, bool unsigned_val) double Field_blob::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; - int not_used; - char *end_not_used, *blob; - uint32 length; - CHARSET_INFO *cs; - + char *blob; memcpy(&blob, ptr+packlength, sizeof(char*)); if (!blob) return 0.0; - length= get_length(ptr); - cs= charset(); - return my_strntod(cs, blob, length, &end_not_used, ¬_used); + THD *thd= get_thd(); + return Converter_strntod_with_warn(thd, Warn_filter(thd), + Field_blob::charset(), + blob, get_length(ptr)).result(); } longlong Field_blob::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; - int not_used; char *blob; memcpy(&blob, ptr+packlength, sizeof(char*)); if (!blob) return 0; - uint32 length=get_length(ptr); - return my_strntoll(charset(),blob,length,10,NULL,¬_used); + THD *thd= get_thd(); + return Converter_strntoll_with_warn(thd, Warn_filter(thd), + Field_blob::charset(), + blob, get_length(ptr)).result(); } + String *Field_blob::val_str(String *val_buffer __attribute__((unused)), String *val_ptr) { @@ -7517,8 +8095,12 @@ my_decimal *Field_blob::val_decimal(my_decimal *decimal_value) else length= get_length(ptr); - return val_decimal_from_str(blob, length, - Field_blob::charset(), decimal_value); + THD *thd= get_thd(); + Converter_str2my_decimal_with_warn(thd, Warn_filter(thd), + E_DEC_FATAL_ERROR, + Field_blob::charset(), + blob, length, decimal_value); + return decimal_value; } @@ -7683,7 +8265,7 @@ int Field_blob::do_save_field_metadata(uchar *metadata_ptr) uint32 Field_blob::sort_length() const { - return (uint32) (current_thd->variables.max_sort_length + + return (uint32) (get_thd()->variables.max_sort_length + (field_charset == &my_charset_bin ? 0 : packlength)); } @@ -7828,6 +8410,104 @@ uint Field_blob::is_equal(Create_field *new_field) #ifdef HAVE_SPATIAL +/* Values 1-40 reserved for 1-byte options, + 41-80 for 2-byte options, + 81-120 for 4-byte options, + 121-160 for 8-byte options, + other - varied length in next 1-3 bytes. +*/ +enum extra2_gis_field_options { + FIELDGEOM_END=0, + FIELDGEOM_STORAGE_MODEL=1, + FIELDGEOM_PRECISION=2, + FIELDGEOM_SCALE=3, + FIELDGEOM_SRID=81, +}; + + +uint gis_field_options_image(uchar *buff, List<Create_field> &create_fields) +{ + uint image_size= 0; + List_iterator<Create_field> it(create_fields); + Create_field *field; + while ((field= it++)) + { + if (field->sql_type != MYSQL_TYPE_GEOMETRY) + continue; + if (buff) + { + uchar *cbuf= buff + image_size; + + cbuf[0]= FIELDGEOM_STORAGE_MODEL; + cbuf[1]= (uchar) Field_geom::GEOM_STORAGE_WKB; + + cbuf[2]= FIELDGEOM_PRECISION; + cbuf[3]= (uchar) field->length; + + cbuf[4]= FIELDGEOM_SCALE; + cbuf[5]= (uchar) field->decimals; + + cbuf[6]= FIELDGEOM_SRID; + int4store(cbuf + 7, ((uint32) field->srid)); + + cbuf[11]= FIELDGEOM_END; + } + image_size+= 12; + } + + return image_size; +} + + +uint gis_field_options_read(const uchar *buf, uint buf_len, + Field_geom::storage_type *st_type,uint *precision, uint *scale, uint *srid) +{ + const uchar *buf_end= buf + buf_len; + const uchar *cbuf= buf; + int option_id; + + *precision= *scale= *srid= 0; + *st_type= Field_geom::GEOM_STORAGE_WKB; + + if (!buf) /* can only happen with the old FRM file */ + goto end_of_record; + + while (cbuf < buf_end) + { + switch ((option_id= *(cbuf++))) + { + case FIELDGEOM_STORAGE_MODEL: + *st_type= (Field_geom::storage_type) cbuf[0]; + break; + case FIELDGEOM_PRECISION: + *precision= cbuf[0]; + break; + case FIELDGEOM_SCALE: + *scale= cbuf[0]; + break; + case FIELDGEOM_SRID: + *srid= uint4korr(cbuf); + break; + case FIELDGEOM_END: + goto end_of_record; + } + if (option_id > 0 && option_id <= 40) + cbuf+= 1; + else if (option_id > 40 && option_id <= 80) + cbuf+= 2; + else if (option_id > 80 && option_id <= 120) + cbuf+= 4; + else if (option_id > 120 && option_id <= 160) + cbuf+= 8; + else /* > 160 and <=255 */ + cbuf+= cbuf[0] ? 1 + cbuf[0] : 3 + uint2korr(cbuf+1); + } + +end_of_record: + return cbuf - buf; +} + + void Field_geom::sql_type(String &res) const { @@ -7864,7 +8544,7 @@ void Field_geom::sql_type(String &res) const int Field_geom::store(double nr) { my_message(ER_CANT_CREATE_GEOMETRY_OBJECT, - ER(ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0)); + ER_THD(get_thd(), ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0)); return -1; } @@ -7872,7 +8552,7 @@ int Field_geom::store(double nr) int Field_geom::store(longlong nr, bool unsigned_val) { my_message(ER_CANT_CREATE_GEOMETRY_OBJECT, - ER(ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0)); + ER_THD(get_thd(), ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0)); return -1; } @@ -7880,7 +8560,7 @@ int Field_geom::store(longlong nr, bool unsigned_val) int Field_geom::store_decimal(const my_decimal *) { my_message(ER_CANT_CREATE_GEOMETRY_OBJECT, - ER(ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0)); + ER_THD(get_thd(), ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0)); return -1; } @@ -7907,10 +8587,13 @@ int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs) (uint32) geom_type != wkb_type) { my_printf_error(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, - ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), MYF(0), - Geometry::ci_collection[geom_type]->m_name.str, - Geometry::ci_collection[wkb_type]->m_name.str, field_name, - (ulong) table->in_use->get_stmt_da()->current_row_for_warning()); + ER_THD(get_thd(), ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), + MYF(0), + Geometry::ci_collection[geom_type]->m_name.str, + Geometry::ci_collection[wkb_type]->m_name.str, + field_name, + (ulong) table->in_use->get_stmt_da()-> + current_row_for_warning()); goto err_exit; } @@ -7927,7 +8610,7 @@ int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs) err: my_message(ER_CANT_CREATE_GEOMETRY_OBJECT, - ER(ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0)); + ER_THD(get_thd(), ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0)); err_exit: bzero(ptr, Field_blob::pack_length()); return -1; @@ -7953,6 +8636,15 @@ uint Field_geom::is_equal(Create_field *new_field) (new_field->geom_type == geom_type || new_field->geom_type == GEOM_GEOMETRY); } + + +bool Field_geom::can_optimize_range(const Item_bool_func *cond, + const Item *item, + bool is_eq_func) const +{ + return item->cmp_type() == STRING_RESULT; +} + #endif /*HAVE_SPATIAL*/ /**************************************************************************** @@ -8142,10 +8834,11 @@ void Field_enum::sql_type(String &res) const } -Field *Field_enum::new_field(MEM_ROOT *root, TABLE *new_table, - bool keep_type) +Field *Field_enum::make_new_field(MEM_ROOT *root, TABLE *new_table, + bool keep_type) { - Field_enum *res= (Field_enum*) Field::new_field(root, new_table, keep_type); + Field_enum *res= (Field_enum*) Field::make_new_field(root, new_table, + keep_type); if (res) res->typelib= copy_typelib(root, typelib); return res; @@ -8430,6 +9123,30 @@ uint Field_num::is_equal(Create_field *new_field) } +bool Field_enum::can_optimize_keypart_ref(const Item_bool_func *cond, + const Item *item) const +{ + DBUG_ASSERT(cmp_type() == INT_RESULT); + DBUG_ASSERT(result_type() == STRING_RESULT); + + switch (item->cmp_type()) + { + case TIME_RESULT: + return false; + case INT_RESULT: + case DECIMAL_RESULT: + case REAL_RESULT: + return true; + case STRING_RESULT: + return charset() == cond->compare_collation(); + case ROW_RESULT: + DBUG_ASSERT(0); + break; + } + return false; +} + + /* Bit field. @@ -8744,9 +9461,9 @@ uint Field_bit::get_key_image(uchar *buff, uint length, imagetype type_arg) *buff++= bits; length--; } - uint data_length = MY_MIN(length, bytes_in_rec); - memcpy(buff, ptr, data_length); - return data_length + 1; + uint tmp_data_length = MY_MIN(length, bytes_in_rec); + memcpy(buff, ptr, tmp_data_length); + return tmp_data_length + 1; } @@ -8962,8 +9679,8 @@ void Field_bit::set_default() { if (bit_len > 0) { - my_ptrdiff_t const offset= table->s->default_values - table->record[0]; - uchar bits= get_rec_bits(bit_ptr + offset, bit_ofs, bit_len); + my_ptrdiff_t const col_offset= table->s->default_values - table->record[0]; + uchar bits= get_rec_bits(bit_ptr + col_offset, bit_ofs, bit_len); set_rec_bits(bits, bit_ptr, bit_ofs, bit_len); } Field::set_default(); @@ -9196,96 +9913,100 @@ void Create_field::init_for_tmp_table(enum_field_types sql_type_arg, } -/** - Initialize field definition for create. - - @param thd Thread handle - @param fld_name Field name - @param fld_type Field type - @param fld_length Field length - @param fld_decimals Decimal (if any) - @param fld_type_modifier Additional type information - @param fld_default_value Field default value (if any) - @param fld_on_update_value The value of ON UPDATE clause - @param fld_comment Field comment - @param fld_change Field change - @param fld_interval_list Interval list (if any) - @param fld_charset Field charset - @param fld_geom_type Field geometry type (if any) - @param fld_vcol_info Virtual column data +static inline bool is_item_func(Item* x) +{ + return x != NULL && x->type() == Item::FUNC_ITEM; +} - @retval - FALSE on success - @retval - TRUE on error -*/ -bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, - char *fld_length, char *fld_decimals, - uint fld_type_modifier, Item *fld_default_value, - Item *fld_on_update_value, LEX_STRING *fld_comment, - char *fld_change, List<String> *fld_interval_list, - CHARSET_INFO *fld_charset, uint fld_geom_type, - Virtual_column_info *fld_vcol_info, - engine_option_value *create_opt, bool check_exists) +bool Create_field::check(THD *thd) { + const uint conditional_type_modifiers= AUTO_INCREMENT_FLAG; uint sign_len, allowed_type_modifier= 0; ulong max_field_charlength= MAX_FIELD_CHARLENGTH; - const bool on_update_is_function= - (fld_on_update_value != NULL && - fld_on_update_value->type() == Item::FUNC_ITEM); + DBUG_ENTER("Create_field::check"); + + if (vcol_info) + { + vcol_info->set_field_type(sql_type); + sql_type= (enum enum_field_types)MYSQL_TYPE_VIRTUAL; + } - DBUG_ENTER("Create_field::init()"); + if (length > MAX_FIELD_BLOBLENGTH) + { + my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), field_name, MAX_FIELD_BLOBLENGTH); + DBUG_RETURN(1); + } - field= 0; - field_name= fld_name; - flags= fld_type_modifier; - option_list= create_opt; + if (decimals >= NOT_FIXED_DEC) + { + my_error(ER_TOO_BIG_SCALE, MYF(0), static_cast<ulonglong>(decimals), + field_name, static_cast<ulong>(NOT_FIXED_DEC - 1)); + DBUG_RETURN(TRUE); + } - if (fld_default_value != NULL && fld_default_value->type() == Item::FUNC_ITEM) + if (def) + { + /* + Default value should be literal => basic constants => + no need fix_fields() + + We allow only one function as part of default value - + NOW() as default for TIMESTAMP and DATETIME type. + */ + if (def->type() == Item::FUNC_ITEM && + (static_cast<Item_func*>(def)->functype() != Item_func::NOW_FUNC || + (mysql_type_to_time_type(sql_type) != MYSQL_TIMESTAMP_DATETIME) || + def->decimals < length)) + { + my_error(ER_INVALID_DEFAULT, MYF(0), field_name); + DBUG_RETURN(1); + } + else if (def->type() == Item::NULL_ITEM) + { + def= 0; + if ((flags & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) == NOT_NULL_FLAG) + { + my_error(ER_INVALID_DEFAULT, MYF(0), field_name); + DBUG_RETURN(1); + } + } + else if (flags & AUTO_INCREMENT_FLAG) + { + my_error(ER_INVALID_DEFAULT, MYF(0), field_name); + DBUG_RETURN(1); + } + } + + if (is_item_func(def)) { /* There is a function default for insertions. */ def= NULL; - unireg_check= (on_update_is_function ? + unireg_check= (is_item_func(on_update) ? Field::TIMESTAMP_DNUN_FIELD : // for insertions and for updates. Field::TIMESTAMP_DN_FIELD); // only for insertions. } else { /* No function default for insertions. Either NULL or a constant. */ - def= fld_default_value; - if (on_update_is_function) + if (is_item_func(on_update)) unireg_check= Field::TIMESTAMP_UN_FIELD; // function default for updates else - unireg_check= ((fld_type_modifier & AUTO_INCREMENT_FLAG) != 0 ? + unireg_check= ((flags & AUTO_INCREMENT_FLAG) ? Field::NEXT_NUMBER : // Automatic increment. Field::NONE); } - decimals= fld_decimals ? (uint)atoi(fld_decimals) : 0; - if (decimals >= NOT_FIXED_DEC) + if (on_update && + (mysql_type_to_time_type(sql_type) != MYSQL_TIMESTAMP_DATETIME || + on_update->decimals < length)) { - my_error(ER_TOO_BIG_SCALE, MYF(0), decimals, fld_name, - static_cast<ulong>(NOT_FIXED_DEC - 1)); - DBUG_RETURN(TRUE); + my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name); + DBUG_RETURN(1); } - sql_type= fld_type; - length= 0; - change= fld_change; - interval= 0; - pack_length= key_length= 0; - charset= fld_charset; - geom_type= (Field::geometry_type) fld_geom_type; - interval_list.empty(); - - comment= *fld_comment; - vcol_info= fld_vcol_info; - create_if_not_exists= check_exists; - stored_in_db= TRUE; - /* Initialize data for a computed field */ - if ((uchar)fld_type == (uchar)MYSQL_TYPE_VIRTUAL) + if (sql_type == MYSQL_TYPE_VIRTUAL) { DBUG_ASSERT(vcol_info && vcol_info->expr_item); stored_in_db= vcol_info->is_stored(); @@ -9305,56 +10026,34 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, Field::vcol_info. It is is always NULL for a column that is not computed. */ - sql_type= fld_type= vcol_info->get_real_type(); - } - - /* - Set NO_DEFAULT_VALUE_FLAG if this field doesn't have a default value and - it is NOT NULL, not an AUTO_INCREMENT field and not a TIMESTAMP. - */ - if (!fld_default_value && !(fld_type_modifier & AUTO_INCREMENT_FLAG) && - (fld_type_modifier & NOT_NULL_FLAG) && !is_timestamp_type(fld_type)) - flags|= NO_DEFAULT_VALUE_FLAG; - - if (fld_length != NULL) - { - errno= 0; - length= strtoul(fld_length, NULL, 10); - if ((errno != 0) || (length > MAX_FIELD_BLOBLENGTH)) - { - my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), fld_name, MAX_FIELD_BLOBLENGTH); - DBUG_RETURN(TRUE); - } - - if (length == 0) - fld_length= NULL; /* purecov: inspected */ + sql_type= vcol_info->get_real_type(); } - sign_len= fld_type_modifier & UNSIGNED_FLAG ? 0 : 1; + sign_len= flags & UNSIGNED_FLAG ? 0 : 1; - switch (fld_type) { + switch (sql_type) { case MYSQL_TYPE_TINY: - if (!fld_length) + if (!length) length= MAX_TINYINT_WIDTH+sign_len; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; case MYSQL_TYPE_SHORT: - if (!fld_length) + if (!length) length= MAX_SMALLINT_WIDTH+sign_len; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; case MYSQL_TYPE_INT24: - if (!fld_length) + if (!length) length= MAX_MEDIUMINT_WIDTH+sign_len; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; case MYSQL_TYPE_LONG: - if (!fld_length) + if (!length) length= MAX_INT_WIDTH+sign_len; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; case MYSQL_TYPE_LONGLONG: - if (!fld_length) + if (!length) length= MAX_BIGINT_WIDTH; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; @@ -9364,18 +10063,17 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, my_decimal_trim(&length, &decimals); if (length > DECIMAL_MAX_PRECISION) { - my_error(ER_TOO_BIG_PRECISION, MYF(0), static_cast<int>(length), - fld_name, static_cast<ulong>(DECIMAL_MAX_PRECISION)); + my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name, + DECIMAL_MAX_PRECISION); DBUG_RETURN(TRUE); } if (length < decimals) { - my_error(ER_M_BIGGER_THAN_D, MYF(0), fld_name); + my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name); DBUG_RETURN(TRUE); } length= - my_decimal_precision_to_length(length, decimals, - fld_type_modifier & UNSIGNED_FLAG); + my_decimal_precision_to_length(length, decimals, flags & UNSIGNED_FLAG); pack_length= my_decimal_get_binary_size(length, decimals); break; @@ -9393,11 +10091,11 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_GEOMETRY: - if (fld_default_value) + if (def) { /* Allow empty as default value. */ String str,*res; - res= fld_default_value->val_str(&str); + res= def->val_str(&str); /* A default other than '' is always an error, and any non-NULL specified default is an error in strict mode. @@ -9405,7 +10103,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, if (res->length() || thd->is_strict_mode()) { my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), - fld_name); /* purecov: inspected */ + field_name); /* purecov: inspected */ DBUG_RETURN(TRUE); } else @@ -9415,40 +10113,22 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, */ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_BLOB_CANT_HAVE_DEFAULT, - ER(ER_BLOB_CANT_HAVE_DEFAULT), - fld_name); + ER_THD(thd, ER_BLOB_CANT_HAVE_DEFAULT), + field_name); } def= 0; } flags|= BLOB_FLAG; break; case MYSQL_TYPE_YEAR: - if (!fld_length || length != 2) + if (!length || length != 2) length= 4; /* Default length */ flags|= ZEROFILL_FLAG | UNSIGNED_FLAG; break; case MYSQL_TYPE_FLOAT: /* change FLOAT(precision) to FLOAT or DOUBLE */ allowed_type_modifier= AUTO_INCREMENT_FLAG; - if (fld_length && !fld_decimals) - { - uint tmp_length= length; - if (tmp_length > PRECISION_FOR_DOUBLE) - { - my_error(ER_WRONG_FIELD_SPEC, MYF(0), fld_name); - DBUG_RETURN(TRUE); - } - else if (tmp_length > PRECISION_FOR_FLOAT) - { - sql_type= MYSQL_TYPE_DOUBLE; - length= MAX_DOUBLE_STR_LENGTH; - } - else - length= MAX_FLOAT_STR_LENGTH; - decimals= NOT_FIXED_DEC; - break; - } - if (!fld_length && !fld_decimals) + if (!length && !decimals) { length= MAX_FLOAT_STR_LENGTH; decimals= NOT_FIXED_DEC; @@ -9456,13 +10136,13 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, if (length < decimals && decimals != NOT_FIXED_DEC) { - my_error(ER_M_BIGGER_THAN_D, MYF(0), fld_name); + my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name); DBUG_RETURN(TRUE); } break; case MYSQL_TYPE_DOUBLE: allowed_type_modifier= AUTO_INCREMENT_FLAG; - if (!fld_length && !fld_decimals) + if (!length && !decimals) { length= DBL_DIG+7; decimals= NOT_FIXED_DEC; @@ -9470,7 +10150,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, if (length < decimals && decimals != NOT_FIXED_DEC) { - my_error(ER_M_BIGGER_THAN_D, MYF(0), fld_name); + my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name); DBUG_RETURN(TRUE); } break; @@ -9478,7 +10158,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, case MYSQL_TYPE_TIMESTAMP2: if (length > MAX_DATETIME_PRECISION) { - my_error(ER_TOO_BIG_PRECISION, MYF(0), length, fld_name, + my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name, MAX_DATETIME_PRECISION); DBUG_RETURN(TRUE); } @@ -9496,7 +10176,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, case MYSQL_TYPE_TIME2: if (length > MAX_DATETIME_PRECISION) { - my_error(ER_TOO_BIG_PRECISION, MYF(0), length, fld_name, + my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name, MAX_DATETIME_PRECISION); DBUG_RETURN(TRUE); } @@ -9506,50 +10186,29 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, case MYSQL_TYPE_DATETIME2: if (length > MAX_DATETIME_PRECISION) { - my_error(ER_TOO_BIG_PRECISION, MYF(0), length, fld_name, + my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name, MAX_DATETIME_PRECISION); DBUG_RETURN(TRUE); } length+= MAX_DATETIME_WIDTH + (length ? 1 : 0); break; case MYSQL_TYPE_SET: - { - pack_length= get_set_pack_length(fld_interval_list->elements); - - List_iterator<String> it(*fld_interval_list); - String *tmp; - while ((tmp= it++)) - interval_list.push_back(tmp); - /* - Set fake length to 1 to pass the below conditions. - Real length will be set in mysql_prepare_table() - when we know the character set of the column - */ - length= 1; - break; - } + pack_length= get_set_pack_length(interval_list.elements); + break; case MYSQL_TYPE_ENUM: - { - /* Should be safe. */ - pack_length= get_enum_pack_length(fld_interval_list->elements); - - List_iterator<String> it(*fld_interval_list); - String *tmp; - while ((tmp= it++)) - interval_list.push_back(tmp); - length= 1; /* See comment for MYSQL_TYPE_SET above. */ - break; - } + /* Should be safe. */ + pack_length= get_enum_pack_length(interval_list.elements); + break; case MYSQL_TYPE_VAR_STRING: DBUG_ASSERT(0); /* Impossible. */ break; case MYSQL_TYPE_BIT: { - if (!fld_length) + if (!length) length= 1; if (length > MAX_BIT_FIELD_LENGTH) { - my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), fld_name, + my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), field_name, static_cast<ulong>(MAX_BIT_FIELD_LENGTH)); DBUG_RETURN(TRUE); } @@ -9558,37 +10217,54 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, } case MYSQL_TYPE_DECIMAL: DBUG_ASSERT(0); /* Was obsolete */ - } + } /* Remember the value of length */ char_length= length; + /* + Set NO_DEFAULT_VALUE_FLAG if this field doesn't have a default value and + it is NOT NULL, not an AUTO_INCREMENT field. + We need to do this check here and in mysql_create_prepare_table() as + sp_head::fill_field_definition() calls this function. + */ + if (!def && unireg_check == Field::NONE && (flags & NOT_NULL_FLAG)) + { + /* + TIMESTAMP columns get implicit DEFAULT value when + explicit_defaults_for_timestamp is not set. + */ + if (opt_explicit_defaults_for_timestamp || + !is_timestamp_type(sql_type)) + { + flags|= NO_DEFAULT_VALUE_FLAG; + } + } + if (!(flags & BLOB_FLAG) && - ((length > max_field_charlength && fld_type != MYSQL_TYPE_SET && - fld_type != MYSQL_TYPE_ENUM && - (fld_type != MYSQL_TYPE_VARCHAR || fld_default_value)) || - ((length == 0) && - fld_type != MYSQL_TYPE_STRING && - fld_type != MYSQL_TYPE_VARCHAR && fld_type != MYSQL_TYPE_GEOMETRY))) - { - my_error((fld_type == MYSQL_TYPE_VAR_STRING || - fld_type == MYSQL_TYPE_VARCHAR || - fld_type == MYSQL_TYPE_STRING) ? ER_TOO_BIG_FIELDLENGTH : + ((length > max_field_charlength && + (sql_type != MYSQL_TYPE_VARCHAR || def)) || + (length == 0 && + sql_type != MYSQL_TYPE_ENUM && sql_type != MYSQL_TYPE_SET && + sql_type != MYSQL_TYPE_STRING && sql_type != MYSQL_TYPE_VARCHAR && + sql_type != MYSQL_TYPE_GEOMETRY))) + { + my_error((sql_type == MYSQL_TYPE_VAR_STRING || + sql_type == MYSQL_TYPE_VARCHAR || + sql_type == MYSQL_TYPE_STRING) ? ER_TOO_BIG_FIELDLENGTH : ER_TOO_BIG_DISPLAYWIDTH, MYF(0), - fld_name, max_field_charlength); /* purecov: inspected */ + field_name, max_field_charlength); /* purecov: inspected */ DBUG_RETURN(TRUE); } - fld_type_modifier&= AUTO_INCREMENT_FLAG; - if ((~allowed_type_modifier) & fld_type_modifier) + if ((~allowed_type_modifier) & flags & conditional_type_modifiers) { - my_error(ER_WRONG_FIELD_SPEC, MYF(0), fld_name); + my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name); DBUG_RETURN(TRUE); } DBUG_RETURN(FALSE); /* success */ } - enum_field_types get_blob_type_from_length(ulong length) { enum_field_types type; @@ -9675,18 +10351,21 @@ uint pack_length_to_packflag(uint type) } -Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length, +Field *make_field(TABLE_SHARE *share, + MEM_ROOT *mem_root, + uchar *ptr, uint32 field_length, uchar *null_pos, uchar null_bit, uint pack_flag, enum_field_types field_type, CHARSET_INFO *field_charset, - Field::geometry_type geom_type, + Field::geometry_type geom_type, uint srid, Field::utype unireg_check, TYPELIB *interval, const char *field_name) { uchar *UNINIT_VAR(bit_ptr); uchar UNINIT_VAR(bit_offset); + if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag)) { bit_ptr= null_pos; @@ -9723,16 +10402,18 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length, if (field_type == MYSQL_TYPE_STRING || field_type == MYSQL_TYPE_DECIMAL || // 3.23 or 4.0 string field_type == MYSQL_TYPE_VAR_STRING) - return new Field_string(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - field_charset); + return new (mem_root) + Field_string(ptr,field_length,null_pos,null_bit, + unireg_check, field_name, + field_charset); if (field_type == MYSQL_TYPE_VARCHAR) - return new Field_varstring(ptr,field_length, - HA_VARCHAR_PACKLENGTH(field_length), - null_pos,null_bit, - unireg_check, field_name, - share, - field_charset); + return new (mem_root) + Field_varstring(ptr,field_length, + HA_VARCHAR_PACKLENGTH(field_length), + null_pos,null_bit, + unireg_check, field_name, + share, + field_charset); return 0; // Error } @@ -9744,138 +10425,160 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length, if (f_is_geom(pack_flag)) { status_var_increment(current_thd->status_var.feature_gis); - return new Field_geom(ptr,null_pos,null_bit, - unireg_check, field_name, share, - pack_length, geom_type); + return new (mem_root) + Field_geom(ptr,null_pos,null_bit, + unireg_check, field_name, share, + pack_length, geom_type, srid); } #endif if (f_is_blob(pack_flag)) - return new Field_blob(ptr,null_pos,null_bit, - unireg_check, field_name, share, - pack_length, field_charset); + return new (mem_root) + Field_blob(ptr,null_pos,null_bit, + unireg_check, field_name, share, + pack_length, field_charset); if (interval) { if (f_is_enum(pack_flag)) - return new Field_enum(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - pack_length, interval, field_charset); + return new (mem_root) + Field_enum(ptr,field_length,null_pos,null_bit, + unireg_check, field_name, + pack_length, interval, field_charset); else - return new Field_set(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - pack_length, interval, field_charset); + return new (mem_root) + Field_set(ptr,field_length,null_pos,null_bit, + unireg_check, field_name, + pack_length, interval, field_charset); } } switch (field_type) { case MYSQL_TYPE_DECIMAL: - return new Field_decimal(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - f_decimals(pack_flag), - f_is_zerofill(pack_flag) != 0, - f_is_dec(pack_flag) == 0); + return new (mem_root) + Field_decimal(ptr,field_length,null_pos,null_bit, + unireg_check, field_name, + f_decimals(pack_flag), + f_is_zerofill(pack_flag) != 0, + f_is_dec(pack_flag) == 0); case MYSQL_TYPE_NEWDECIMAL: - return new Field_new_decimal(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - f_decimals(pack_flag), - f_is_zerofill(pack_flag) != 0, - f_is_dec(pack_flag) == 0); + return new (mem_root) + Field_new_decimal(ptr,field_length,null_pos,null_bit, + unireg_check, field_name, + f_decimals(pack_flag), + f_is_zerofill(pack_flag) != 0, + f_is_dec(pack_flag) == 0); case MYSQL_TYPE_FLOAT: - return new Field_float(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - f_decimals(pack_flag), - f_is_zerofill(pack_flag) != 0, - f_is_dec(pack_flag)== 0); + return new (mem_root) + Field_float(ptr,field_length,null_pos,null_bit, + unireg_check, field_name, + f_decimals(pack_flag), + f_is_zerofill(pack_flag) != 0, + f_is_dec(pack_flag)== 0); case MYSQL_TYPE_DOUBLE: - return new Field_double(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - f_decimals(pack_flag), - f_is_zerofill(pack_flag) != 0, - f_is_dec(pack_flag)== 0); + return new (mem_root) + Field_double(ptr,field_length,null_pos,null_bit, + unireg_check, field_name, + f_decimals(pack_flag), + f_is_zerofill(pack_flag) != 0, + f_is_dec(pack_flag)== 0); case MYSQL_TYPE_TINY: - return new Field_tiny(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - f_is_zerofill(pack_flag) != 0, - f_is_dec(pack_flag) == 0); + return new (mem_root) + Field_tiny(ptr,field_length,null_pos,null_bit, + unireg_check, field_name, + f_is_zerofill(pack_flag) != 0, + f_is_dec(pack_flag) == 0); case MYSQL_TYPE_SHORT: - return new Field_short(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - f_is_zerofill(pack_flag) != 0, - f_is_dec(pack_flag) == 0); + return new (mem_root) + Field_short(ptr,field_length,null_pos,null_bit, + unireg_check, field_name, + f_is_zerofill(pack_flag) != 0, + f_is_dec(pack_flag) == 0); case MYSQL_TYPE_INT24: - return new Field_medium(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - f_is_zerofill(pack_flag) != 0, - f_is_dec(pack_flag) == 0); + return new (mem_root) + Field_medium(ptr,field_length,null_pos,null_bit, + unireg_check, field_name, + f_is_zerofill(pack_flag) != 0, + f_is_dec(pack_flag) == 0); case MYSQL_TYPE_LONG: - return new Field_long(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - f_is_zerofill(pack_flag) != 0, - f_is_dec(pack_flag) == 0); + return new (mem_root) + Field_long(ptr,field_length,null_pos,null_bit, + unireg_check, field_name, + f_is_zerofill(pack_flag) != 0, + f_is_dec(pack_flag) == 0); case MYSQL_TYPE_LONGLONG: - return new Field_longlong(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - f_is_zerofill(pack_flag) != 0, - f_is_dec(pack_flag) == 0); + return new (mem_root) + Field_longlong(ptr,field_length,null_pos,null_bit, + unireg_check, field_name, + f_is_zerofill(pack_flag) != 0, + f_is_dec(pack_flag) == 0); case MYSQL_TYPE_TIMESTAMP: { uint dec= field_length > MAX_DATETIME_WIDTH ? field_length - MAX_DATETIME_WIDTH - 1: 0; - return new_Field_timestamp(ptr, null_pos, null_bit, unireg_check, + return new_Field_timestamp(mem_root, ptr, null_pos, null_bit, unireg_check, field_name, share, dec); } case MYSQL_TYPE_TIMESTAMP2: { uint dec= field_length > MAX_DATETIME_WIDTH ? field_length - MAX_DATETIME_WIDTH - 1: 0; - return new Field_timestampf(ptr, null_pos, null_bit, unireg_check, - field_name, share, dec); + return new (mem_root) + Field_timestampf(ptr, null_pos, null_bit, unireg_check, + field_name, share, dec); } case MYSQL_TYPE_YEAR: - return new Field_year(ptr,field_length,null_pos,null_bit, - unireg_check, field_name); + return new (mem_root) + Field_year(ptr,field_length,null_pos,null_bit, + unireg_check, field_name); case MYSQL_TYPE_DATE: - return new Field_date(ptr,null_pos,null_bit, - unireg_check, field_name); + return new (mem_root) + Field_date(ptr,null_pos,null_bit, + unireg_check, field_name); case MYSQL_TYPE_NEWDATE: - return new Field_newdate(ptr,null_pos,null_bit, - unireg_check, field_name); + return new (mem_root) + Field_newdate(ptr,null_pos,null_bit, + unireg_check, field_name); case MYSQL_TYPE_TIME: { uint dec= field_length > MIN_TIME_WIDTH ? field_length - MIN_TIME_WIDTH - 1: 0; - return new_Field_time(ptr, null_pos, null_bit, unireg_check, + return new_Field_time(mem_root, ptr, null_pos, null_bit, unireg_check, field_name, dec); } case MYSQL_TYPE_TIME2: { uint dec= field_length > MIN_TIME_WIDTH ? field_length - MIN_TIME_WIDTH - 1: 0; - return new Field_timef(ptr, null_pos, null_bit, unireg_check, - field_name, dec); + return new (mem_root) + Field_timef(ptr, null_pos, null_bit, unireg_check, + field_name, dec); } case MYSQL_TYPE_DATETIME: { uint dec= field_length > MAX_DATETIME_WIDTH ? field_length - MAX_DATETIME_WIDTH - 1: 0; - return new_Field_datetime(ptr, null_pos, null_bit, unireg_check, + return new_Field_datetime(mem_root, ptr, null_pos, null_bit, unireg_check, field_name, dec); } case MYSQL_TYPE_DATETIME2: { uint dec= field_length > MAX_DATETIME_WIDTH ? field_length - MAX_DATETIME_WIDTH - 1: 0; - return new Field_datetimef(ptr, null_pos, null_bit, unireg_check, - field_name, dec); + return new (mem_root) + Field_datetimef(ptr, null_pos, null_bit, unireg_check, + field_name, dec); } case MYSQL_TYPE_NULL: - return new Field_null(ptr, field_length, unireg_check, field_name, - field_charset); + return new (mem_root) + Field_null(ptr, field_length, unireg_check, field_name, + field_charset); case MYSQL_TYPE_BIT: - return f_bit_as_char(pack_flag) ? - new Field_bit_as_char(ptr, field_length, null_pos, null_bit, - unireg_check, field_name) : - new Field_bit(ptr, field_length, null_pos, null_bit, bit_ptr, - bit_offset, unireg_check, field_name); + return (f_bit_as_char(pack_flag) ? + new (mem_root) + Field_bit_as_char(ptr, field_length, null_pos, null_bit, + unireg_check, field_name) : + new (mem_root) + Field_bit(ptr, field_length, null_pos, null_bit, bit_ptr, + bit_offset, unireg_check, field_name)); default: // Impossible (Wrong version) break; @@ -9886,7 +10589,7 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length, /** Create a field suitable for create of table. */ -Create_field::Create_field(Field *old_field,Field *orig_field) +Create_field::Create_field(THD *thd, Field *old_field, Field *orig_field) { field= old_field; field_name=change=old_field->field_name; @@ -9932,16 +10635,17 @@ Create_field::Create_field(Field *old_field,Field *orig_field) #ifdef HAVE_SPATIAL case MYSQL_TYPE_GEOMETRY: geom_type= ((Field_geom*)old_field)->geom_type; + srid= ((Field_geom*)old_field)->srid; break; #endif case MYSQL_TYPE_YEAR: if (length != 4) { char buff[sizeof("YEAR()") + MY_INT64_NUM_DECIMAL_DIGITS + 1]; - my_snprintf(buff, sizeof(buff), "YEAR(%lu)", length); - push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_NOTE, + my_snprintf(buff, sizeof(buff), "YEAR(%llu)", length); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_WARN_DEPRECATED_SYNTAX, - ER(ER_WARN_DEPRECATED_SYNTAX), + ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX), buff, "YEAR(4)"); } break; @@ -9987,21 +10691,15 @@ Create_field::Create_field(Field *old_field,Field *orig_field) } if (!default_now) // Give a constant default { - char buff[MAX_FIELD_WIDTH]; - String tmp(buff,sizeof(buff), charset); - /* Get the value from default_values */ - my_ptrdiff_t diff= orig_field->table->default_values_offset(); - orig_field->move_field_offset(diff); // Points now at default_values - if (!orig_field->is_real_null()) + const uchar *dv= orig_field->table->s->default_values; + if (!orig_field->is_null_in_record(dv)) { - char buff[MAX_FIELD_WIDTH], *pos; - String tmp(buff, sizeof(buff), charset), *res; - res= orig_field->val_str(&tmp); - pos= (char*) sql_strmake(res->ptr(), res->length()); - def= new Item_string(pos, res->length(), charset); + StringBuffer<MAX_FIELD_WIDTH> tmp(charset); + String *res= orig_field->val_str(&tmp, orig_field->ptr_in_record(dv)); + char *pos= (char*) sql_strmake(res->ptr(), res->length()); + def= new (thd->mem_root) Item_string(thd, pos, res->length(), charset); } - orig_field->move_field_offset(-diff); // Back to record[0] } } } @@ -10111,11 +10809,11 @@ Field::set_warning(Sql_condition::enum_warning_level level, uint code, If this field was created only for type conversion purposes it will have table == NULL. */ - THD *thd= table ? table->in_use : current_thd; + THD *thd= get_thd(); if (thd->count_cuted_fields) { thd->cuted_fields+= cut_increment; - push_warning_printf(thd, level, code, ER(code), field_name, + push_warning_printf(thd, level, code, ER_THD(thd, code), field_name, thd->get_stmt_da()->current_row_for_warning()); return 0; } @@ -10144,6 +10842,7 @@ Field::set_warning(Sql_condition::enum_warning_level level, uint code, void Field::set_datetime_warning(Sql_condition::enum_warning_level level, uint code, const ErrConv *str, timestamp_type ts_type, int cuted_increment) + const { THD *thd= get_thd(); if (thd->really_abort_on_warning() && level >= Sql_condition::WARN_LEVEL_WARN) @@ -10153,14 +10852,14 @@ void Field::set_datetime_warning(Sql_condition::enum_warning_level level, } -void Field::set_warning_truncated_wrong_value(const char *type, +void Field::set_warning_truncated_wrong_value(const char *type_arg, const char *value) { THD *thd= get_thd(); push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, - ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), - type, value, field_name, + ER_THD(thd, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), + type_arg, value, field_name, static_cast<ulong>(thd->get_stmt_da()-> current_row_for_warning())); } @@ -10214,3 +10913,22 @@ void Field::set_explicit_default(Item *value) return; set_has_explicit_value(); } + + +bool Field::validate_value_in_record_with_warn(THD *thd, const uchar *record) +{ + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); + bool rc; + if ((rc= validate_value_in_record(thd, record))) + { + // Get and report val_str() for the DEFAULT value + StringBuffer<MAX_FIELD_WIDTH> tmp; + val_str(&tmp, ptr_in_record(record)); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_INVALID_DEFAULT_VALUE_FOR_FIELD, + ER_THD(thd, ER_INVALID_DEFAULT_VALUE_FOR_FIELD), + ErrConvString(&tmp).ptr(), field_name); + } + dbug_tmp_restore_column_map(table->read_set, old_map); + return rc; +} |