summaryrefslogtreecommitdiff
path: root/sql/field.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/field.cc')
-rw-r--r--sql/field.cc2008
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, &ltime, 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, &ltime, &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(&ltime, 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(&ltime, 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(&ltime, 0) :
+ const_item->get_date(&ltime, 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, &ltime,
+ 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(&ltime,
+ TIME_FUZZY_DATES |
+ TIME_INVALID_DATES))
+ return NULL;
+ return new (thd->mem_root)
+ Item_datetime_literal_for_invalid_dates(thd, &ltime,
+ 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(&ltime, &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, &ltime,
+ 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, &ltime,
+ 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, &ltime, 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, &ltime, 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(&ltime, ptr, 0);
val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH);
uint length= (uint) my_date_to_str(&ltime,
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(&ltime,
+ 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(&ltime))
+ return new (thd->mem_root)
+ Item_datetime_literal_for_invalid_dates(thd, &ltime,
+ ltime.second_part ?
+ TIME_SECOND_PART_DIGITS : 0);
+ datetime_to_date(&ltime);
+ return new (thd->mem_root)
+ Item_date_literal_for_invalid_dates(thd, &ltime);
+ }
+ 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(&ltime, 0) :
+ const_item->get_date(&ltime, 0))
+ return NULL;
+ datetime_to_date(&ltime);
+ return new (thd->mem_root) Item_date_literal(thd, &ltime);
+ }
+ 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, &not_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,&not_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;
+}