diff options
Diffstat (limited to 'sql/field.cc')
-rw-r--r-- | sql/field.cc | 2658 |
1 files changed, 1497 insertions, 1161 deletions
diff --git a/sql/field.cc b/sql/field.cc index a23004ebd96..3a9b2257748 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2000, 2017, Oracle and/or its affiliates. - Copyright (c) 2008, 2017, MariaDB + Copyright (c) 2008, 2019, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -27,7 +27,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_select.h" #include "rpl_rli.h" // Pull in Relay_log_info @@ -51,15 +51,7 @@ *****************************************************************************/ static const char *zero_timestamp="0000-00-00 00:00:00.000000"; - -/* number of bytes to store second_part part of the TIMESTAMP(N) */ -static uint sec_part_bytes[MAX_DATETIME_PRECISION+1]= { 0, 1, 1, 2, 2, 3, 3 }; - -/* number of bytes to store DATETIME(N) */ -static uint datetime_hires_bytes[MAX_DATETIME_PRECISION+1]= { 5, 6, 6, 7, 7, 7, 8 }; - -/* number of bytes to store TIME(N) */ -static uint time_hires_bytes[MAX_DATETIME_PRECISION+1]= { 3, 4, 4, 5, 5, 5, 6 }; +LEX_CSTRING temp_lex_str= {STRING_WITH_LEN("temp")}; uchar Field_null::null[1]={1}; const char field_separator=','; @@ -95,18 +87,35 @@ const char field_separator=','; following #defines describe that gap and how to canculate number of fields and index of field in this array. */ -#define FIELDTYPE_TEAR_FROM (MYSQL_TYPE_BIT + 1) -#define FIELDTYPE_TEAR_TO (MYSQL_TYPE_NEWDECIMAL - 1) -#define FIELDTYPE_NUM (FIELDTYPE_TEAR_FROM + (255 - FIELDTYPE_TEAR_TO)) +const int FIELDTYPE_TEAR_FROM= (MYSQL_TYPE_BIT + 1); +const int FIELDTYPE_TEAR_TO= (MYSQL_TYPE_NEWDECIMAL - 1); +const int FIELDTYPE_LAST= 254; +const int FIELDTYPE_NUM= FIELDTYPE_TEAR_FROM + (FIELDTYPE_LAST - + FIELDTYPE_TEAR_TO); + static inline int field_type2index (enum_field_types field_type) { + DBUG_ASSERT(real_type_to_type(field_type) < FIELDTYPE_TEAR_FROM || + real_type_to_type(field_type) > FIELDTYPE_TEAR_TO); + DBUG_ASSERT(field_type <= FIELDTYPE_LAST); field_type= real_type_to_type(field_type); - return (field_type < FIELDTYPE_TEAR_FROM ? - field_type : - ((int)FIELDTYPE_TEAR_FROM) + (field_type - FIELDTYPE_TEAR_TO) - 1); + if (field_type < FIELDTYPE_TEAR_FROM) + return field_type; + return FIELDTYPE_TEAR_FROM + (field_type - FIELDTYPE_TEAR_TO) - 1; } +/** + Implements data type merge rules for the built-in traditional data types. + Used for operations such as: + - UNION + - CASE and its abbreviations COALESCE, IF, IFNULL + - LEAST/GREATEST + + Given Fields A and B of real_types a and b, we find the result type of + COALESCE(A, B) by querying: + field_types_merge_rules[field_type_to_index(a)][field_type_to_index(b)]. +*/ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= { /* MYSQL_TYPE_DECIMAL -> */ @@ -120,7 +129,7 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 - MYSQL_TYPE_DECIMAL, MYSQL_TYPE_DECIMAL, + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_NEWDECIMAL, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR @@ -137,8 +146,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_TINY -> */ { @@ -168,8 +177,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_SHORT -> */ { @@ -199,8 +208,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_LONG -> */ { @@ -230,8 +239,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_FLOAT -> */ { @@ -261,8 +270,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_DOUBLE -> */ { @@ -292,8 +301,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_NULL -> */ { @@ -323,8 +332,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_GEOMETRY + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_TIMESTAMP -> */ { @@ -354,8 +363,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_LONGLONG -> */ { @@ -385,8 +394,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_INT24 -> */ { @@ -416,8 +425,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_DATE -> */ { @@ -447,8 +456,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_TIME -> */ { @@ -478,8 +487,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_DATETIME -> */ { @@ -509,13 +518,13 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_YEAR -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY - MYSQL_TYPE_DECIMAL, MYSQL_TYPE_TINY, + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_TINY, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE @@ -540,8 +549,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_NEWDATE -> */ { @@ -571,8 +580,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_VARCHAR -> */ { @@ -602,8 +611,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_BIT -> */ { @@ -633,8 +642,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_NEWDECIMAL -> */ { @@ -664,8 +673,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_ENUM -> */ { @@ -695,8 +704,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_SET -> */ { @@ -726,8 +735,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_TINY_BLOB -> */ { @@ -757,8 +766,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_TINY_BLOB, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB + //MYSQL_TYPE_STRING + MYSQL_TYPE_TINY_BLOB }, /* MYSQL_TYPE_MEDIUM_BLOB -> */ { @@ -788,8 +797,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB + //MYSQL_TYPE_STRING + MYSQL_TYPE_MEDIUM_BLOB }, /* MYSQL_TYPE_LONG_BLOB -> */ { @@ -819,8 +828,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB + //MYSQL_TYPE_STRING + MYSQL_TYPE_LONG_BLOB }, /* MYSQL_TYPE_BLOB -> */ { @@ -850,8 +859,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB + //MYSQL_TYPE_STRING + MYSQL_TYPE_BLOB }, /* MYSQL_TYPE_VAR_STRING -> */ { @@ -881,8 +890,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_STRING -> */ { @@ -912,39 +921,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_STRING - }, - /* MYSQL_TYPE_GEOMETRY -> */ - { - //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP - MYSQL_TYPE_GEOMETRY, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_DATE MYSQL_TYPE_TIME - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_BIT <16>-<245> - MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, - //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB - MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, - //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING - MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_GEOMETRY + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING } }; @@ -961,46 +939,19 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= enum_field_types Field::field_type_merge(enum_field_types a, enum_field_types b) { - DBUG_ASSERT(real_type_to_type(a) < FIELDTYPE_TEAR_FROM || - real_type_to_type(a) > FIELDTYPE_TEAR_TO); - DBUG_ASSERT(real_type_to_type(b) < FIELDTYPE_TEAR_FROM || - real_type_to_type(b) > FIELDTYPE_TEAR_TO); return field_types_merge_rules[field_type2index(a)] [field_type2index(b)]; } - -static Item_result field_types_result_type [FIELDTYPE_NUM]= +const Type_handler * +Type_handler::aggregate_for_result_traditional(const Type_handler *a, + const Type_handler *b) { - //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY - DECIMAL_RESULT, INT_RESULT, - //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG - INT_RESULT, INT_RESULT, - //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE - REAL_RESULT, REAL_RESULT, - //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP - STRING_RESULT, STRING_RESULT, - //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 - INT_RESULT, INT_RESULT, - //MYSQL_TYPE_DATE MYSQL_TYPE_TIME - STRING_RESULT, STRING_RESULT, - //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR - STRING_RESULT, INT_RESULT, - //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR - STRING_RESULT, STRING_RESULT, - //MYSQL_TYPE_BIT <16>-<245> - STRING_RESULT, - //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM - DECIMAL_RESULT, STRING_RESULT, - //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB - STRING_RESULT, STRING_RESULT, - //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB - STRING_RESULT, STRING_RESULT, - //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING - STRING_RESULT, STRING_RESULT, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - STRING_RESULT, STRING_RESULT -}; + enum_field_types ta= a->real_field_type(); + enum_field_types tb= b->real_field_type(); + return + Type_handler::get_handler_by_real_type(Field::field_type_merge(ta, tb)); +} /* @@ -1047,57 +998,11 @@ int compare(unsigned int a, unsigned int b) CPP_UNNAMED_NS_END -/** - Detect Item_result by given field type of UNION merge result. - - @param field_type given field type - - @return - Item_result (type of internal MySQL expression result) -*/ - -Item_result Field::result_merge_type(enum_field_types field_type) -{ - DBUG_ASSERT(real_type_to_type(field_type) < FIELDTYPE_TEAR_FROM || - real_type_to_type(field_type) > FIELDTYPE_TEAR_TO); - return field_types_result_type[field_type2index(field_type)]; -} /***************************************************************************** Static help functions *****************************************************************************/ -/** - 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 - to check the type of a non-Field in mysql_alter_table(). - - @param type field type - - @retval - TRUE Type can have a prefixed key - @retval - FALSE Type can not have a prefixed key -*/ - -bool Field::type_can_have_key_part(enum enum_field_types type) -{ - switch (type) { - case MYSQL_TYPE_VARCHAR: - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_LONG_BLOB: - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_VAR_STRING: - case MYSQL_TYPE_STRING: - case MYSQL_TYPE_GEOMETRY: - return TRUE; - default: - return FALSE; - } -} - void Field::make_sort_key(uchar *buff,uint length) { @@ -1278,7 +1183,7 @@ bool Field::test_if_equality_guarantees_uniqueness(const Item *item) const 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(item_equal->compare_type_handler()->cmp_type() != STRING_RESULT); DBUG_ASSERT(cmp_type() != STRING_RESULT); switch (ctx.subst_constraint()) { case ANY_SUBST: @@ -1291,7 +1196,7 @@ bool Field::can_be_substituted_to_equal_item(const Context &ctx, 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(); + return ctx.compare_type_handler() == item_equal->compare_type_handler(); case IDENTITY_SUBST: return true; } @@ -1324,19 +1229,19 @@ bool Field::can_optimize_group_min_max(const Item_bool_func *cond, /* - This covers all numeric types, ENUM, SET, BIT + This covers all numeric types, 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 + DBUG_ASSERT(cmp_type() != STRING_RESULT); // Handled in Field_str descendants return item->cmp_type() != TIME_RESULT; } -int Field::store_hex_hybrid(const char *str, uint length) +int Field::store_hex_hybrid(const char *str, size_t length) { DBUG_ASSERT(result_type() != STRING_RESULT); ulonglong nr; @@ -1428,6 +1333,45 @@ void Field::load_data_set_value(const char *pos, uint length, } +bool Field::sp_prepare_and_store_item(THD *thd, Item **value) +{ + DBUG_ENTER("Field::sp_prepare_and_store_item"); + DBUG_ASSERT(value); + + Item *expr_item; + + if (!(expr_item= thd->sp_prepare_func_item(value, 1))) + goto error; + + /* + expr_item is now fixed, it's safe to call cmp_type() + */ + if (expr_item->cmp_type() == ROW_RESULT) + { + my_error(ER_OPERAND_COLUMNS, MYF(0), 1); + goto error; + } + + /* Save the value in the field. Convert the value if needed. */ + + expr_item->save_in_field(this, 0); + + if (likely(!thd->is_error())) + DBUG_RETURN(false); + +error: + /* + In case of error during evaluation, leave the result field set to NULL. + Sic: we can't do it in the beginning of the function because the + result field might be needed for its own re-evaluation, e.g. case of + set x = x + 1; + */ + set_null(); + DBUG_ASSERT(thd->is_error()); + DBUG_RETURN(true); +} + + void Field::error_generated_column_function_is_not_allowed(THD *thd, bool error) const { @@ -1439,7 +1383,7 @@ void Field::error_generated_column_function_is_not_allowed(THD *thd, my_error(ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(error ? 0 : ME_JUST_WARNING), tmp.c_ptr(), vcol_info->get_vcol_type_name(), - const_cast<const char*>(field_name)); + const_cast<const char*>(field_name.str)); } @@ -1473,7 +1417,7 @@ bool Field::check_vcol_sql_mode_dependency(THD *thd, vcol_init_mode mode) const */ Field_num::Field_num(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, - const char *field_name_arg, + const LEX_CSTRING *field_name_arg, uint8 dec_arg, bool zero_arg, bool unsigned_arg) :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg), @@ -1492,7 +1436,7 @@ void Field_num::prepend_zeros(String *value) const if ((diff= (int) (field_length - value->length())) > 0) { const bool error= value->realloc(field_length); - if (!error) + if (likely(!error)) { bmove_upp((uchar*) value->ptr()+field_length, (uchar*) value->ptr()+value->length(), @@ -1520,7 +1464,7 @@ Item *Field_num::get_equal_zerofill_const_item(THD *thd, const Context &ctx, break; } DBUG_ASSERT(const_item->const_item()); - DBUG_ASSERT(ctx.compare_type() != STRING_RESULT); + DBUG_ASSERT(ctx.compare_type_handler()->cmp_type() != STRING_RESULT); return const_item; } @@ -1635,8 +1579,7 @@ Value_source::Converter_string_to_number::check_edom_and_truncation(THD *thd, int Field_num::check_edom_and_important_data_truncation(const char *type, bool edom, CHARSET_INFO *cs, - const char *str, - uint length, + const char *str, size_t length, const char *end) { /* Test if we get an empty string or garbage */ @@ -1658,7 +1601,7 @@ int Field_num::check_edom_and_important_data_truncation(const char *type, int Field_num::check_edom_and_truncation(const char *type, bool edom, CHARSET_INFO *cs, - const char *str, uint length, + const char *str, size_t length, const char *end) { int rc= check_edom_and_important_data_truncation(type, edom, @@ -1692,7 +1635,7 @@ int Field_num::check_edom_and_truncation(const char *type, bool edom, 1 error */ -bool Field_num::get_int(CHARSET_INFO *cs, const char *from, uint len, +bool Field_num::get_int(CHARSET_INFO *cs, const char *from, size_t len, longlong *rnd, ulonglong unsigned_max, longlong signed_min, longlong signed_max) { @@ -1725,7 +1668,7 @@ bool Field_num::get_int(CHARSET_INFO *cs, const char *from, uint len, goto out_of_range; } } - if (get_thd()->count_cuted_fields && + if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && check_int(cs, from, len, end, error)) return 1; return 0; @@ -1736,17 +1679,17 @@ out_of_range: } -double Field_real::get_double(const char *str, uint length, CHARSET_INFO *cs, +double Field_real::get_double(const char *str, size_t length, CHARSET_INFO *cs, int *error) { char *end; double nr= my_strntod(cs,(char*) str, length, &end, error); - if (*error) + if (unlikely(*error)) { set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); *error= 1; } - else if (get_thd()->count_cuted_fields && + else if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && check_edom_and_truncation("double", str == end, cs, str, length, end)) *error= 1; @@ -1809,9 +1752,10 @@ String *Field::val_int_as_str(String *val_buffer, bool unsigned_val) /// This is used as a table name when the table structure is not set up Field::Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, uchar null_bit_arg, - utype unireg_check_arg, const char *field_name_arg) - :ptr(ptr_arg), null_ptr(null_ptr_arg), table(0), orig_table(0), - table_name(0), field_name(field_name_arg), option_list(0), + utype unireg_check_arg, const LEX_CSTRING *field_name_arg) + :ptr(ptr_arg), invisible(VISIBLE), + null_ptr(null_ptr_arg), table(0), orig_table(0), + table_name(0), field_name(*field_name_arg), option_list(0), option_struct(0), key_start(0), part_of_key(0), part_of_key_not_clustered(0), part_of_sortkey(0), unireg_check(unireg_check_arg), field_length(length_arg), @@ -1920,7 +1864,7 @@ bool Field::compatible_field_size(uint field_metadata, } -int Field::store(const char *to, uint length, CHARSET_INFO *cs, +int Field::store(const char *to, size_t length, CHARSET_INFO *cs, enum_check_fields check_level) { int res; @@ -1933,30 +1877,11 @@ int Field::store(const char *to, uint length, CHARSET_INFO *cs, } -static int timestamp_to_TIME(THD *thd, MYSQL_TIME *ltime, my_time_t ts, - ulong sec_part, ulonglong fuzzydate) -{ - thd->time_zone_used= 1; - if (ts == 0 && sec_part == 0) - { - if (fuzzydate & TIME_NO_ZERO_DATE) - return 1; - set_zero_time(ltime, MYSQL_TIMESTAMP_DATETIME); - } - else - { - thd->variables.time_zone->gmt_sec_to_TIME(ltime, ts); - ltime->second_part= sec_part; - } - return 0; -} - - int Field::store_timestamp(my_time_t ts, ulong sec_part) { MYSQL_TIME ltime; THD *thd= get_thd(); - timestamp_to_TIME(thd, <ime, ts, sec_part, 0); + thd->timestamp_to_TIME(<ime, ts, sec_part, 0); return store_time_dec(<ime, decimals()); } @@ -2074,12 +1999,12 @@ void Field_num::add_zerofill_and_unsigned(String &res) const } -void Field::make_field(Send_field *field) +void Field::make_send_field(Send_field *field) { if (orig_table && orig_table->s->db.str && *orig_table->s->db.str) { field->db_name= orig_table->s->db.str; - if (orig_table->pos_in_table_list && + if (orig_table->pos_in_table_list && orig_table->pos_in_table_list->schema_table) field->org_table_name= (orig_table->pos_in_table_list-> schema_table->table_name); @@ -2096,10 +2021,9 @@ void Field::make_field(Send_field *field) else { field->table_name= ""; - field->org_col_name= ""; + field->org_col_name= empty_clex_str; } field->col_name= field_name; - field->charsetnr= charset()->number; field->length=field_length; field->type=type(); field->flags=table->maybe_null ? (flags & ~NOT_NULL_FLAG) : flags; @@ -2165,7 +2089,7 @@ longlong Field::convert_decimal2longlong(const my_decimal *val, !=0 error */ -int Field_num::store_decimal(const my_decimal *val) +int Field_int::store_decimal(const my_decimal *val) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int err= 0; @@ -2188,37 +2112,79 @@ int Field_num::store_decimal(const my_decimal *val) pointer to decimal buffer with value of field */ -my_decimal* Field_num::val_decimal(my_decimal *decimal_value) +my_decimal* Field_int::val_decimal(my_decimal *decimal_value) { ASSERT_COLUMN_MARKED_FOR_READ; - DBUG_ASSERT(result_type() == INT_RESULT); longlong nr= val_int(); int2my_decimal(E_DEC_FATAL_ERROR, nr, unsigned_flag, decimal_value); return decimal_value; } -bool Field_num::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) +bool Field_int::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, - table->s, field_name); + table->s, field_name.str); +} + + +bool Field_vers_trx_id::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate, ulonglong trx_id) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + DBUG_ASSERT(ltime); + if (!table || !table->s) + return true; + DBUG_ASSERT(table->versioned(VERS_TRX_ID) || + (table->versioned() && table->s->table_category == TABLE_CATEGORY_TEMPORARY)); + if (!trx_id) + return true; + + THD *thd= get_thd(); + DBUG_ASSERT(thd); + if (trx_id == ULONGLONG_MAX) + { + thd->variables.time_zone->gmt_sec_to_TIME(ltime, TIMESTAMP_MAX_VALUE); + ltime->second_part= TIME_MAX_SECOND_PART; + return false; + } + if (cached == trx_id) + { + *ltime= cache; + return false; + } + + TR_table trt(thd); + bool found= trt.query(trx_id); + if (found) + { + trt[TR_table::FLD_COMMIT_TS]->get_date(&cache, fuzzydate); + *ltime= cache; + cached= trx_id; + return false; + } + + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_VERS_NO_TRX_ID, ER_THD(thd, ER_VERS_NO_TRX_ID), + (longlong) trx_id); + return true; } 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) + const LEX_CSTRING *field_name_arg, + const DTCollation &collation) :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg) { - field_charset= charset_arg; - if (charset_arg->state & MY_CS_BINSORT) + field_charset= collation.collation; + if (collation.collation->state & MY_CS_BINSORT) flags|=BINARY_FLAG; - field_derivation= DERIVATION_IMPLICIT; - field_repertoire= my_charset_repertoire(charset_arg); + field_derivation= collation.derivation; + field_repertoire= collation.repertoire; } @@ -2251,11 +2217,11 @@ bool Field_str::test_if_equality_guarantees_uniqueness(const Item *item) const 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); + DBUG_ASSERT(item_equal->compare_type_handler()->cmp_type() == STRING_RESULT); switch (ctx.subst_constraint()) { case ANY_SUBST: - return ctx.compare_type() == item_equal->compare_type() && - (ctx.compare_type() != STRING_RESULT || + return ctx.compare_type_handler() == item_equal->compare_type_handler() && + (ctx.compare_type_handler()->cmp_type() != STRING_RESULT || ctx.compare_collation() == item_equal->compare_collation()); case IDENTITY_SUBST: return ((charset()->state & MY_CS_BINSORT) && @@ -2265,9 +2231,9 @@ bool Field_str::can_be_substituted_to_equal_item(const Context &ctx, } -void Field_num::make_field(Send_field *field) +void Field_num::make_send_field(Send_field *field) { - Field::make_field(field); + Field::make_send_field(field); field->decimals= dec; } @@ -2363,7 +2329,7 @@ bool Field::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) Needs to be changed if/when we want to support different time formats. */ -int Field::store_time_dec(MYSQL_TIME *ltime, uint dec) +int Field::store_time_dec(const MYSQL_TIME *ltime, uint dec) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; char buff[MAX_DATE_STRING_REP_LENGTH]; @@ -2374,7 +2340,7 @@ int Field::store_time_dec(MYSQL_TIME *ltime, uint dec) } -bool Field::optimize_range(uint idx, uint part) +bool Field::optimize_range(uint idx, uint part) const { return MY_TEST(table->file->index_flags(idx, part, 1) & HA_READ_RANGE); } @@ -2399,8 +2365,11 @@ Field *Field::make_new_field(MEM_ROOT *root, TABLE *new_table, */ tmp->unireg_check= Field::NONE; tmp->flags&= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG | - ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG); + ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG | + VERS_SYS_START_FLAG | VERS_SYS_END_FLAG | + VERS_UPDATE_UNVERSIONED_FLAG); tmp->reset_fields(); + tmp->invisible= VISIBLE; return tmp; } @@ -2480,6 +2449,51 @@ void Field_null::sql_type(String &res) const /**************************************************************************** + Field_row, e.g. for ROW-type SP variables +****************************************************************************/ + +Field_row::~Field_row() +{ + delete m_table; +} + + +bool Field_row::sp_prepare_and_store_item(THD *thd, Item **value) +{ + DBUG_ENTER("Field_row::sp_prepare_and_store_item"); + + if (value[0]->type() == Item::NULL_ITEM) + { + /* + We're in a auto-generated sp_inst_set, to assign + the explicit default NULL value to a ROW variable. + */ + m_table->set_all_fields_to_null(); + DBUG_RETURN(false); + } + + /** + - In case if we're assigning a ROW variable from another ROW variable, + value[0] points to Item_splocal. sp_fix_func_item() will return the + fixed underlying Item_field pointing to Field_row. + - In case if we're assigning from a ROW() value, src and value[0] will + point to the same Item_row. + */ + Item *src; + if (!(src= thd->sp_fix_func_item(value)) || + src->cmp_type() != ROW_RESULT || + src->cols() != m_table->s->fields) + { + my_error(ER_OPERAND_COLUMNS, MYF(0), m_table->s->fields); + m_table->set_all_fields_to_null(); + DBUG_RETURN(true); + } + + DBUG_RETURN(m_table->sp_set_all_fields_from_item(thd, src)); +} + + +/**************************************************************************** Functions for the Field_decimal class This is an number stored as a pre-space (or pre-zero) string ****************************************************************************/ @@ -2530,7 +2544,7 @@ void Field_decimal::overflow(bool negative) } -int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) +int Field_decimal::store(const char *from_arg, size_t len, CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; char buff[STRING_BUFFER_USUAL_SIZE]; @@ -2674,7 +2688,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) it makes the code easer to read. */ - if (get_thd()->count_cuted_fields) + if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION) { // Skip end spaces for (;from != end && my_isspace(&my_charset_bin, *from); from++) ; @@ -2844,7 +2858,8 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) { if (pos == right_wall) { - if (get_thd()->count_cuted_fields && !is_cuted_fields_incr) + if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && + !is_cuted_fields_incr) break; // Go on below to see if we lose non zero digits return 0; } @@ -2904,13 +2919,12 @@ int Field_decimal::store(double nr) return 1; } - if (!isfinite(nr)) // Handle infinity as special case + if (!std::isfinite(nr)) // Handle infinity as special case { overflow(nr < 0.0); return 1; } - uint i; size_t length; uchar fyllchar,*to; char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE]; @@ -2926,7 +2940,7 @@ int Field_decimal::store(double nr) else { to=ptr; - for (i=field_length-length ; i-- > 0 ;) + for (size_t i=field_length-length ; i-- > 0 ;) *to++ = fyllchar; memcpy(to,buff,length); return 0; @@ -3094,7 +3108,7 @@ Field *Field_decimal::make_new_field(MEM_ROOT *root, TABLE *new_table, Field *field= new (root) Field_new_decimal(NULL, field_length, maybe_null() ? (uchar*) "" : 0, 0, - NONE, field_name, + NONE, &field_name, dec, flags & ZEROFILL_FLAG, unsigned_flag); if (field) @@ -3107,86 +3121,29 @@ Field *Field_decimal::make_new_field(MEM_ROOT *root, TABLE *new_table, ** Field_new_decimal ****************************************************************************/ +static uint get_decimal_precision(uint len, uint8 dec, bool unsigned_val) +{ + uint precision= my_decimal_length_to_precision(len, dec, unsigned_val); + return MY_MIN(precision, DECIMAL_MAX_PRECISION); +} + Field_new_decimal::Field_new_decimal(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, - const char *field_name_arg, + const LEX_CSTRING *field_name_arg, uint8 dec_arg,bool zero_arg, bool unsigned_arg) :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, dec_arg, zero_arg, unsigned_arg) { - precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg); - set_if_smaller(precision, DECIMAL_MAX_PRECISION); - DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION) && - (dec <= DECIMAL_MAX_SCALE)); - bin_size= my_decimal_get_binary_size(precision, dec); -} - - -Field_new_decimal::Field_new_decimal(uint32 len_arg, - bool maybe_null_arg, - const char *name, - uint8 dec_arg, - bool unsigned_arg) - :Field_num((uchar*) 0, len_arg, - maybe_null_arg ? (uchar*) "": 0, 0, - NONE, name, dec_arg, 0, unsigned_arg) -{ - precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg); - set_if_smaller(precision, DECIMAL_MAX_PRECISION); + precision= get_decimal_precision(len_arg, dec_arg, unsigned_arg); DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION) && (dec <= DECIMAL_MAX_SCALE)); bin_size= my_decimal_get_binary_size(precision, dec); } -Field *Field_new_decimal::create_from_item(MEM_ROOT *mem_root, Item *item) -{ - uint8 dec= item->decimals; - uint8 intg= item->decimal_precision() - dec; - uint32 len= item->max_char_length(); - - DBUG_ASSERT (item->result_type() == DECIMAL_RESULT); - - /* - Trying to put too many digits overall in a DECIMAL(prec,dec) - will always throw a warning. We must limit dec to - DECIMAL_MAX_SCALE however to prevent an assert() later. - */ - - if (dec > 0) - { - signed int overflow; - - dec= MY_MIN(dec, DECIMAL_MAX_SCALE); - - /* - If the value still overflows the field with the corrected dec, - we'll throw out decimals rather than integers. This is still - bad and of course throws a truncation warning. - +1: for decimal point - */ - - const int required_length= - my_decimal_precision_to_length(intg + dec, dec, - item->unsigned_flag); - - overflow= required_length - len; - - if (overflow > 0) - dec= MY_MAX(0, dec - overflow); // too long, discard fract - else - /* Corrected value fits. */ - len= required_length; - } - return new (mem_root) - Field_new_decimal(len, item->maybe_null, item->name, - dec, item->unsigned_flag); -} - - int Field_new_decimal::reset(void) { store_value(&decimal_zero); @@ -3266,7 +3223,7 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value, *native_error= my_decimal2binary(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW, decimal_value, ptr, precision, dec); - if (*native_error == E_DEC_OVERFLOW) + if (unlikely(*native_error == E_DEC_OVERFLOW)) { my_decimal buff; DBUG_PRINT("info", ("overflow")); @@ -3285,13 +3242,13 @@ 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) + if (unlikely(!rc && native_error == E_DEC_TRUNCATED)) set_note(WARN_DATA_TRUNCATED, 1); return rc; } -int Field_new_decimal::store(const char *from, uint length, +int Field_new_decimal::store(const char *from, size_t length, CHARSET_INFO *charset_arg) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; @@ -3316,7 +3273,7 @@ int Field_new_decimal::store(const char *from, uint length, DBUG_RETURN(1); } - if (thd->count_cuted_fields) + if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION) { if (check_edom_and_important_data_truncation("decimal", err && err != E_DEC_TRUNCATED, @@ -3361,7 +3318,7 @@ int Field_new_decimal::store(const char *from, uint length, - 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 && + if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION && (err == E_DEC_TRUNCATED || err2 == E_DEC_TRUNCATED || end < from + length)) @@ -3430,7 +3387,7 @@ int Field_new_decimal::store_decimal(const my_decimal *decimal_value) } -int Field_new_decimal::store_time_dec(MYSQL_TIME *ltime, uint dec_arg) +int Field_new_decimal::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg) { my_decimal decimal_value; return store_value(date2my_decimal(ltime, &decimal_value)); @@ -3497,7 +3454,8 @@ 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, table->s, field_name); + ltime, fuzzydate, table->s, + field_name.str); } @@ -3508,7 +3466,7 @@ int Field_new_decimal::cmp(const uchar *a,const uchar*b) void Field_new_decimal::sort_string(uchar *buff, - uint length __attribute__((unused))) + uint) { memcpy(buff, ptr, bin_size); } @@ -3534,7 +3492,7 @@ void Field_new_decimal::sql_type(String &str) const @returns number of bytes written to metadata_ptr */ -int Field_new_decimal::do_save_field_metadata(uchar *metadata_ptr) +int Field_new_decimal::save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= precision; *(metadata_ptr + 1)= decimals(); @@ -3579,7 +3537,7 @@ bool Field_new_decimal::compatible_field_size(uint field_metadata, uint Field_new_decimal::is_equal(Create_field *new_field) { - return ((new_field->sql_type == real_type()) && + return ((new_field->type_handler() == type_handler()) && ((new_field->flags & UNSIGNED_FLAG) == (uint) (flags & UNSIGNED_FLAG)) && ((new_field->flags & AUTO_INCREMENT_FLAG) == @@ -3667,7 +3625,8 @@ Item *Field_new_decimal::get_equal_const_item(THD *thd, const Context &ctx, 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, + return new (thd->mem_root) Item_decimal(thd, field_name.str, + &val_buffer2, decimals(), field_length); } break; @@ -3678,7 +3637,7 @@ Item *Field_new_decimal::get_equal_const_item(THD *thd, const Context &ctx, } -int Field_num::store_time_dec(MYSQL_TIME *ltime, uint dec_arg) +int Field_int::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg) { longlong v= TIME_to_ulonglong(ltime); if (ltime->neg == 0) @@ -3691,7 +3650,7 @@ int Field_num::store_time_dec(MYSQL_TIME *ltime, uint dec_arg) ** tiny int ****************************************************************************/ -int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs) +int Field_tiny::store(const char *from,size_t len,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error; @@ -3813,24 +3772,8 @@ String *Field_tiny::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { ASSERT_COLUMN_MARKED_FOR_READ; - CHARSET_INFO *cs= &my_charset_numeric; - uint length; - uint mlength=MY_MAX(field_length+1,5*cs->mbmaxlen); - val_buffer->alloc(mlength); - char *to=(char*) val_buffer->ptr(); - - if (unsigned_flag) - length= (uint) cs->cset->long10_to_str(cs,to,mlength, 10, - (long) *ptr); - else - length= (uint) cs->cset->long10_to_str(cs,to,mlength,-10, - (long) *((signed char*) ptr)); - - val_buffer->length(length); - if (zerofill) - prepend_zeros(val_buffer); - val_buffer->set_charset(cs); - return val_buffer; + long nr= unsigned_flag ? (long) ptr[0] : (long) ((signed char*) ptr)[0]; + return val_str_from_long(val_buffer, 5, -10, nr); } bool Field_tiny::send_binary(Protocol *protocol) @@ -3867,7 +3810,7 @@ void Field_tiny::sql_type(String &res) const Field type short int (2 byte) ****************************************************************************/ -int Field_short::store(const char *from,uint len,CHARSET_INFO *cs) +int Field_short::store(const char *from,size_t len,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int store_tmp; @@ -3995,24 +3938,9 @@ String *Field_short::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { ASSERT_COLUMN_MARKED_FOR_READ; - CHARSET_INFO *cs= &my_charset_numeric; - uint length; - uint mlength=MY_MAX(field_length+1,7*cs->mbmaxlen); - val_buffer->alloc(mlength); - char *to=(char*) val_buffer->ptr(); - short j; - j=sint2korr(ptr); - - if (unsigned_flag) - length=(uint) cs->cset->long10_to_str(cs, to, mlength, 10, - (long) (uint16) j); - else - length=(uint) cs->cset->long10_to_str(cs, to, mlength,-10, (long) j); - val_buffer->length(length); - if (zerofill) - prepend_zeros(val_buffer); - val_buffer->set_charset(cs); - return val_buffer; + short j= sint2korr(ptr); + long nr= unsigned_flag ? (long) (unsigned short) j : (long) j; + return val_str_from_long(val_buffer, 7, -10, nr); } @@ -4056,7 +3984,7 @@ void Field_short::sql_type(String &res) const Field type medium int (3 byte) ****************************************************************************/ -int Field_medium::store(const char *from,uint len,CHARSET_INFO *cs) +int Field_medium::store(const char *from,size_t len,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int store_tmp; @@ -4185,14 +4113,21 @@ String *Field_medium::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { ASSERT_COLUMN_MARKED_FOR_READ; + long nr= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr); + return val_str_from_long(val_buffer, 10, -10, nr); +} + + +String *Field_int::val_str_from_long(String *val_buffer, + uint max_char_length, + int radix, long nr) +{ CHARSET_INFO *cs= &my_charset_numeric; uint length; - uint mlength=MY_MAX(field_length+1,10*cs->mbmaxlen); + uint mlength= MY_MAX(field_length + 1, max_char_length * cs->mbmaxlen); val_buffer->alloc(mlength); char *to=(char*) val_buffer->ptr(); - long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr); - - length=(uint) cs->cset->long10_to_str(cs,to,mlength,-10,j); + length= (uint) cs->cset->long10_to_str(cs, to, mlength, radix, nr); val_buffer->length(length); if (zerofill) prepend_zeros(val_buffer); /* purecov: inspected */ @@ -4247,7 +4182,7 @@ void Field_medium::sql_type(String &res) const ** long int ****************************************************************************/ -int Field_long::store(const char *from,uint len,CHARSET_INFO *cs) +int Field_long::store(const char *from,size_t len,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; long store_tmp; @@ -4298,7 +4233,7 @@ int Field_long::store(double nr) else res=(int32) (longlong) nr; } - if (error) + if (unlikely(error)) set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); int4store(ptr,res); @@ -4344,7 +4279,7 @@ int Field_long::store(longlong nr, bool unsigned_val) else res=(int32) nr; } - if (error) + if (unlikely(error)) set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); int4store(ptr,res); @@ -4370,27 +4305,13 @@ longlong Field_long::val_int(void) return unsigned_flag ? (longlong) (uint32) j : (longlong) j; } + String *Field_long::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { ASSERT_COLUMN_MARKED_FOR_READ; - CHARSET_INFO *cs= &my_charset_numeric; - uint length; - uint mlength=MY_MAX(field_length+1,12*cs->mbmaxlen); - val_buffer->alloc(mlength); - char *to=(char*) val_buffer->ptr(); - int32 j; - j=sint4korr(ptr); - - if (unsigned_flag) - length=cs->cset->long10_to_str(cs,to,mlength, 10,(long) (uint32)j); - else - length=cs->cset->long10_to_str(cs,to,mlength,-10,(long) j); - val_buffer->length(length); - if (zerofill) - prepend_zeros(val_buffer); - val_buffer->set_charset(cs); - return val_buffer; + long nr= unsigned_flag ? (long) uint4korr(ptr) : sint4korr(ptr); + return val_str_from_long(val_buffer, 12, unsigned_flag ? 10 : -10, nr); } @@ -4434,7 +4355,7 @@ void Field_long::sql_type(String &res) const Field type longlong int (8 bytes) ****************************************************************************/ -int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs) +int Field_longlong::store(const char *from,size_t len,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; @@ -4442,12 +4363,12 @@ int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs) ulonglong tmp; tmp= cs->cset->strntoull10rnd(cs,from,len,unsigned_flag,&end,&error); - if (error == MY_ERRNO_ERANGE) + if (unlikely(error == MY_ERRNO_ERANGE)) { set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } - else if (get_thd()->count_cuted_fields && + else if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && check_int(cs, from, len, end, error)) error= 1; else @@ -4462,7 +4383,7 @@ int Field_longlong::store(double nr) ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; Converter_double_to_longlong conv(nr, unsigned_flag); - if (conv.error()) + if (unlikely(conv.error())) set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); int8store(ptr, conv.result()); @@ -4475,7 +4396,7 @@ int Field_longlong::store(longlong nr, bool unsigned_val) ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; - if (nr < 0) // Only possible error + if (unlikely(nr < 0)) // Only possible error { /* if field is unsigned and value is signed (< 0) or @@ -4581,6 +4502,26 @@ void Field_longlong::sql_type(String &res) const add_zerofill_and_unsigned(res); } +void Field_longlong::set_max() +{ + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + set_notnull(); + int8store(ptr, unsigned_flag ? ULONGLONG_MAX : LONGLONG_MAX); +} + +bool Field_longlong::is_max() +{ + ASSERT_COLUMN_MARKED_FOR_READ; + if (unsigned_flag) + { + ulonglong j; + j= uint8korr(ptr); + return j == ULONGLONG_MAX; + } + longlong j; + j= sint8korr(ptr); + return j == LONGLONG_MAX; +} /* Floating-point numbers @@ -4590,7 +4531,7 @@ void Field_longlong::sql_type(String &res) const single precision float ****************************************************************************/ -int Field_float::store(const char *from,uint len,CHARSET_INFO *cs) +int Field_float::store(const char *from,size_t len,CHARSET_INFO *cs) { int error; Field_float::store(get_double(from, len, cs, &error)); @@ -4604,7 +4545,7 @@ int Field_float::store(double nr) int error= truncate_double(&nr, field_length, not_fixed ? NOT_FIXED_DEC : dec, unsigned_flag, FLT_MAX); - if (error) + if (unlikely(error)) { set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); if (error < 0) // Wrong double value @@ -4648,34 +4589,15 @@ String *Field_float::val_str(String *val_buffer, { ASSERT_COLUMN_MARKED_FOR_READ; DBUG_ASSERT(!zerofill || field_length <= MAX_FIELD_CHARLENGTH); - float nr; - float4get(nr,ptr); - uint to_length= 70; - if (val_buffer->alloc(to_length)) + if (Float(ptr).to_string(val_buffer, dec)) { my_error(ER_OUT_OF_RESOURCES, MYF(0)); return val_buffer; } - char *to=(char*) val_buffer->ptr(); - size_t len; - - if (dec >= FLOATING_POINT_DECIMALS) - len= my_gcvt(nr, MY_GCVT_ARG_FLOAT, to_length - 1, to, NULL); - else - { - /* - We are safe here because the buffer length is 70, and - fabs(float) < 10^39, dec < FLOATING_POINT_DECIMALS. So the resulting string - will be not longer than 69 chars + terminating '\0'. - */ - len= my_fcvt(nr, dec, to, NULL); - } - val_buffer->length((uint) len); if (zerofill) prepend_zeros(val_buffer); - val_buffer->set_charset(&my_charset_numeric); return val_buffer; } @@ -4742,7 +4664,7 @@ bool Field_float::send_binary(Protocol *protocol) @returns number of bytes written to metadata_ptr */ -int Field_float::do_save_field_metadata(uchar *metadata_ptr) +int Field_float::save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= pack_length(); return 1; @@ -4769,7 +4691,7 @@ void Field_float::sql_type(String &res) const double precision floating point numbers ****************************************************************************/ -int Field_double::store(const char *from,uint len,CHARSET_INFO *cs) +int Field_double::store(const char *from,size_t len,CHARSET_INFO *cs) { int error; Field_double::store(get_double(from, len, cs, &error)); @@ -4783,7 +4705,7 @@ int Field_double::store(double nr) int error= truncate_double(&nr, field_length, not_fixed ? NOT_FIXED_DEC : dec, unsigned_flag, DBL_MAX); - if (error) + if (unlikely(error)) { set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); if (error < 0) // Wrong double value @@ -4821,7 +4743,7 @@ int truncate_double(double *nr, uint field_length, uint dec, int error= 0; double res= *nr; - if (isnan(res)) + if (std::isnan(res)) { *nr= 0; return -1; @@ -4843,7 +4765,7 @@ int truncate_double(double *nr, uint field_length, uint dec, max_value-= 1.0 / log_10[dec]; /* Check for infinity so we don't get NaN in calculations */ - if (!my_isinf(res)) + if (!std::isinf(res)) { double tmp= rint((res - floor(res)) * log_10[dec]) / log_10[dec]; res= floor(res) + tmp; @@ -4932,7 +4854,7 @@ int Field_real::store_decimal(const my_decimal *dm) return store(dbl); } -int Field_real::store_time_dec(MYSQL_TIME *ltime, uint dec_arg) +int Field_real::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg) { return store(TIME_to_double(ltime)); } @@ -4950,7 +4872,7 @@ double Field_double::val_real(void) longlong Field_double::val_int_from_real(bool want_unsigned_result) { Converter_double_to_longlong conv(val_real(), want_unsigned_result); - if (!want_unsigned_result && conv.error()) + if (unlikely(!want_unsigned_result && conv.error())) conv.push_warning(get_thd(), Field_double::val_real(), false); return conv.result(); } @@ -4969,7 +4891,7 @@ bool Field_real::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) ASSERT_COLUMN_MARKED_FOR_READ; double nr= val_real(); return double_to_datetime_with_warn(nr, ltime, fuzzydate, - table->s, field_name); + table->s, field_name.str); } @@ -5060,7 +4982,7 @@ void Field_double::sort_string(uchar *to,uint length __attribute__((unused))) @returns number of bytes written to metadata_ptr */ -int Field_double::do_save_field_metadata(uchar *metadata_ptr) +int Field_double::save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= pack_length(); return 1; @@ -5124,7 +5046,7 @@ void Field_double::sql_type(String &res) const Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, - const char *field_name_arg, + const LEX_CSTRING *field_name_arg, TABLE_SHARE *share) :Field_temporal(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg) @@ -5188,7 +5110,7 @@ int Field_timestamp::store_TIME_with_warning(THD *thd, MYSQL_TIME *l_time, timestamp= TIME_to_timestamp(thd, l_time, &conversion_error); if (timestamp == 0 && l_time->second_part == 0) conversion_error= ER_WARN_DATA_OUT_OF_RANGE; - if (conversion_error) + if (unlikely(conversion_error)) { set_datetime_warning(conversion_error, str, MYSQL_TIMESTAMP_DATETIME, !error); @@ -5215,7 +5137,14 @@ copy_or_convert_to_datetime(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to) } -int Field_timestamp::store_time_dec(MYSQL_TIME *ltime, uint dec) +sql_mode_t Field_timestamp::sql_mode_for_timestamp(THD *thd) const +{ + // We don't want to store invalid or fuzzy datetime values in TIMESTAMP + return (thd->variables.sql_mode & MODE_NO_ZERO_DATE) | MODE_NO_ZERO_IN_DATE; +} + + +int Field_timestamp::store_time_dec(const MYSQL_TIME *ltime, uint dec) { int unused; ErrConvTime str(ltime); @@ -5223,14 +5152,12 @@ int Field_timestamp::store_time_dec(MYSQL_TIME *ltime, uint dec) MYSQL_TIME l_time; bool valid= !copy_or_convert_to_datetime(thd, ltime, &l_time) && !check_date(&l_time, pack_time(&l_time) != 0, - (thd->variables.sql_mode & MODE_NO_ZERO_DATE) | - MODE_NO_ZERO_IN_DATE, &unused); - + sql_mode_for_timestamp(thd), &unused); return store_TIME_with_warning(thd, &l_time, &str, false, valid); } -int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs) +int Field_timestamp::store(const char *from,size_t len,CHARSET_INFO *cs) { MYSQL_TIME l_time; MYSQL_TIME_STATUS status; @@ -5238,11 +5165,8 @@ int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs) ErrConvString str(from, len, cs); THD *thd= get_thd(); - /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ have_smth_to_conv= !str_to_datetime(cs, from, len, &l_time, - (thd->variables.sql_mode & - MODE_NO_ZERO_DATE) | - MODE_NO_ZERO_IN_DATE, &status); + sql_mode_for_timestamp(thd), &status); return store_TIME_with_warning(thd, &l_time, &str, status.warnings, have_smth_to_conv); } @@ -5255,9 +5179,8 @@ int Field_timestamp::store(double nr) ErrConvDouble str(nr); THD *thd= get_thd(); - longlong tmp= double_to_datetime(nr, &l_time, (thd->variables.sql_mode & - MODE_NO_ZERO_DATE) | - MODE_NO_ZERO_IN_DATE, &error); + longlong tmp= double_to_datetime(nr, &l_time, sql_mode_for_timestamp(thd), + &error); return store_TIME_with_warning(thd, &l_time, &str, error, tmp != -1); } @@ -5269,10 +5192,8 @@ int Field_timestamp::store(longlong nr, bool unsigned_val) ErrConvInteger str(nr, unsigned_val); THD *thd= get_thd(); - /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ - longlong tmp= number_to_datetime(nr, 0, &l_time, (thd->variables.sql_mode & - MODE_NO_ZERO_DATE) | - MODE_NO_ZERO_IN_DATE, &error); + longlong tmp= number_to_datetime(nr, 0, &l_time, sql_mode_for_timestamp(thd), + &error); return store_TIME_with_warning(thd, &l_time, &str, error, tmp != -1); } @@ -5398,7 +5319,7 @@ bool Field_timestamp::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) { ulong sec_part; my_time_t ts= get_timestamp(&sec_part); - return timestamp_to_TIME(get_thd(), ltime, ts, sec_part, fuzzydate); + return get_thd()->timestamp_to_TIME(ltime, ts, sec_part, fuzzydate); } @@ -5535,47 +5456,17 @@ static longlong read_lowendian(const uchar *from, uint bytes) } } -static void store_bigendian(ulonglong num, uchar *to, uint bytes) -{ - switch(bytes) { - case 1: mi_int1store(to, num); break; - case 2: mi_int2store(to, num); break; - case 3: mi_int3store(to, num); break; - case 4: mi_int4store(to, num); break; - case 5: mi_int5store(to, num); break; - case 6: mi_int6store(to, num); break; - case 7: mi_int7store(to, num); break; - case 8: mi_int8store(to, num); break; - default: DBUG_ASSERT(0); - } -} - -static longlong read_bigendian(const uchar *from, uint bytes) -{ - switch(bytes) { - case 1: return mi_uint1korr(from); - case 2: return mi_uint2korr(from); - case 3: return mi_uint3korr(from); - case 4: return mi_uint4korr(from); - case 5: return mi_uint5korr(from); - case 6: return mi_uint6korr(from); - case 7: return mi_uint7korr(from); - case 8: return mi_sint8korr(from); - default: DBUG_ASSERT(0); return 0; - } -} - void Field_timestamp_hires::store_TIME(my_time_t timestamp, ulong sec_part) { mi_int4store(ptr, timestamp); - store_bigendian(sec_part_shift(sec_part, dec), ptr+4, sec_part_bytes[dec]); + store_bigendian(sec_part_shift(sec_part, dec), ptr+4, sec_part_bytes(dec)); } 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(pos+4, sec_part_bytes[dec]), dec); + *sec_part= (long)sec_part_unshift(read_bigendian(pos+4, sec_part_bytes(dec)), dec); return mi_uint4korr(pos); } @@ -5613,10 +5504,8 @@ int Field_timestamp::store_decimal(const my_decimal *d) error= 2; } else - tmp= number_to_datetime(nr, sec_part, <ime, TIME_NO_ZERO_IN_DATE | - (thd->variables.sql_mode & - MODE_NO_ZERO_DATE), &error); - + tmp= number_to_datetime(nr, sec_part, <ime, sql_mode_for_timestamp(thd), + &error); return store_TIME_with_warning(thd, <ime, &str, error, tmp != -1); } @@ -5642,22 +5531,17 @@ int Field_timestamp_hires::cmp(const uchar *a_ptr, const uchar *b_ptr) int32 a,b; ulong a_sec_part, b_sec_part; a= mi_uint4korr(a_ptr); - a_sec_part= (ulong)read_bigendian(a_ptr+4, sec_part_bytes[dec]); + a_sec_part= (ulong)read_bigendian(a_ptr+4, sec_part_bytes(dec)); b= mi_uint4korr(b_ptr); - b_sec_part= (ulong)read_bigendian(b_ptr+4, sec_part_bytes[dec]); + b_sec_part= (ulong)read_bigendian(b_ptr+4, sec_part_bytes(dec)); return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : a_sec_part < b_sec_part ? -1 : a_sec_part > b_sec_part ? 1 : 0; } -uint32 Field_timestamp_hires::pack_length() const -{ - return 4 + sec_part_bytes[dec]; -} - -void Field_timestamp_with_dec::make_field(Send_field *field) +void Field_timestamp_with_dec::make_send_field(Send_field *field) { - Field::make_field(field); + Field::make_send_field(field); field->decimals= dec; } @@ -5675,6 +5559,27 @@ void Field_timestampf::store_TIME(my_time_t timestamp, ulong sec_part) my_timestamp_to_binary(&tm, ptr, dec); } +void Field_timestampf::set_max() +{ + DBUG_ENTER("Field_timestampf::set_max"); + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + DBUG_ASSERT(dec == TIME_SECOND_PART_DIGITS); + + set_notnull(); + mi_int4store(ptr, TIMESTAMP_MAX_VALUE); + mi_int3store(ptr + 4, TIME_MAX_SECOND_PART); + + DBUG_VOID_RETURN; +} + +bool Field_timestampf::is_max() +{ + DBUG_ENTER("Field_timestampf::is_max"); + ASSERT_COLUMN_MARKED_FOR_READ; + + DBUG_RETURN(mi_sint4korr(ptr) == TIMESTAMP_MAX_VALUE && + mi_sint3korr(ptr + 4) == TIME_MAX_SECOND_PART); +} my_time_t Field_timestampf::get_timestamp(const uchar *pos, ulong *sec_part) const @@ -5695,7 +5600,7 @@ sql_mode_t Field_temporal::can_handle_sql_mode_dependency_on_store() const uint Field_temporal::is_equal(Create_field *new_field) { - return new_field->sql_type == real_type() && + return new_field->type_handler() == type_handler() && new_field->length == max_display_length(); } @@ -5715,11 +5620,9 @@ void Field_temporal::set_warnings(Sql_condition::enum_warning_level trunc_level, a DATE field and non-zero time part is thrown away. */ if (was_cut & MYSQL_TIME_WARN_TRUNCATED) - set_datetime_warning(trunc_level, WARN_DATA_TRUNCATED, - str, mysql_type_to_time_type(type()), 1); + set_datetime_warning(trunc_level, WARN_DATA_TRUNCATED, str, ts_type, 1); if (was_cut & MYSQL_TIME_WARN_OUT_OF_RANGE) - set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE, - str, mysql_type_to_time_type(type()), 1); + set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE, str, ts_type, 1); } @@ -5755,20 +5658,21 @@ int Field_temporal_with_date::store_TIME_with_warning(MYSQL_TIME *ltime, } else if (!MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) && (MYSQL_TIME_WARN_HAVE_NOTES(was_cut) || - (mysql_type_to_time_type(type()) == MYSQL_TIMESTAMP_DATE && + (type_handler()->mysql_timestamp_type() == MYSQL_TIMESTAMP_DATE && (ltime->hour || ltime->minute || ltime->second || ltime->second_part)))) { trunc_level= Sql_condition::WARN_LEVEL_NOTE; was_cut|= MYSQL_TIME_WARN_TRUNCATED; ret= 3; } - set_warnings(trunc_level, str, was_cut, mysql_type_to_time_type(type())); + set_warnings(trunc_level, str, was_cut, + type_handler()->mysql_timestamp_type()); store_TIME(ltime); return was_cut ? ret : 0; } -int Field_temporal_with_date::store(const char *from, uint len, CHARSET_INFO *cs) +int Field_temporal_with_date::store(const char *from, size_t len, CHARSET_INFO *cs) { MYSQL_TIME ltime; MYSQL_TIME_STATUS status; @@ -5808,7 +5712,7 @@ int Field_temporal_with_date::store(longlong nr, bool unsigned_val) } -int Field_temporal_with_date::store_time_dec(MYSQL_TIME *ltime, uint dec) +int Field_temporal_with_date::store_time_dec(const MYSQL_TIME *ltime, uint dec) { int error= 0, have_smth_to_conv= 1; ErrConvTime str(ltime); @@ -5852,7 +5756,7 @@ my_decimal *Field_temporal::val_decimal(my_decimal *d) if (get_date(<ime, 0)) { bzero(<ime, sizeof(ltime)); - ltime.time_type= mysql_type_to_time_type(type()); + ltime.time_type= type_handler()->mysql_timestamp_type(); } return TIME_to_my_decimal(<ime, d); } @@ -5882,30 +5786,28 @@ Item *Field_temporal::get_equal_const_item_datetime(THD *thd, 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)) + Datetime dt(thd, const_item, 0); + if (!dt.is_valid_datetime()) 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, + return new (thd->mem_root) Item_datetime_literal(thd, + dt.get_mysql_time(), 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)) + Datetime dt(thd, const_item, TIME_FUZZY_DATES | TIME_INVALID_DATES); + if (!dt.is_valid_datetime()) return NULL; return new (thd->mem_root) - Item_datetime_literal_for_invalid_dates(thd, <ime, - ltime.second_part ? + Item_datetime_literal_for_invalid_dates(thd, dt.get_mysql_time(), + dt.get_mysql_time()-> + second_part ? TIME_SECOND_PART_DIGITS : 0); } break; @@ -5925,34 +5827,38 @@ int Field_time::store_TIME_with_warning(MYSQL_TIME *ltime, int was_cut, int have_smth_to_conv) { - Sql_condition::enum_warning_level trunc_level= Sql_condition::WARN_LEVEL_WARN; - int ret= 2; ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; if (!have_smth_to_conv) { bzero(ltime, sizeof(*ltime)); - was_cut= MYSQL_TIME_WARN_TRUNCATED; - ret= 1; + store_TIME(ltime); + set_warnings(Sql_condition::WARN_LEVEL_WARN, str, MYSQL_TIME_WARN_TRUNCATED); + return 1; } - else if (!MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) && - ((ltime->year || ltime->month) || - MYSQL_TIME_WARN_HAVE_NOTES(was_cut))) + if (ltime->year != 0 || ltime->month != 0) { - if (ltime->year || ltime->month) - ltime->year= ltime->month= ltime->day= 0; - trunc_level= Sql_condition::WARN_LEVEL_NOTE; - was_cut|= MYSQL_TIME_WARN_TRUNCATED; - ret= 3; + ltime->year= ltime->month= ltime->day= 0; + was_cut|= MYSQL_TIME_NOTE_TRUNCATED; } - set_warnings(trunc_level, str, was_cut, MYSQL_TIMESTAMP_TIME); + my_time_trunc(ltime, decimals()); store_TIME(ltime); - return was_cut ? ret : 0; + if (!MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) && + MYSQL_TIME_WARN_HAVE_NOTES(was_cut)) + { + set_warnings(Sql_condition::WARN_LEVEL_NOTE, str, + was_cut | MYSQL_TIME_WARN_TRUNCATED); + return 3; + } + set_warnings(Sql_condition::WARN_LEVEL_WARN, str, was_cut); + return was_cut ? 2 : 0; } -void Field_time::store_TIME(MYSQL_TIME *ltime) +void Field_time::store_TIME(const MYSQL_TIME *ltime) { + DBUG_ASSERT(ltime->year == 0); + DBUG_ASSERT(ltime->month == 0); long tmp= (ltime->day*24L+ltime->hour)*10000L + (ltime->minute*100+ltime->second); if (ltime->neg) @@ -5960,7 +5866,7 @@ void Field_time::store_TIME(MYSQL_TIME *ltime) int3store(ptr,tmp); } -int Field_time::store(const char *from,uint len,CHARSET_INFO *cs) +int Field_time::store(const char *from,size_t len,CHARSET_INFO *cs) { MYSQL_TIME ltime; MYSQL_TIME_STATUS status; @@ -5986,7 +5892,10 @@ static void calc_datetime_days_diff(MYSQL_TIME *ltime, long days) long daydiff= calc_daynr(ltime->year, ltime->month, ltime->day) - days; ltime->year= ltime->month= 0; if (daydiff >=0 ) + { ltime->day= daydiff; + ltime->time_type= MYSQL_TIMESTAMP_TIME; + } else { longlong timediff= ((((daydiff * 24LL + @@ -5994,20 +5903,12 @@ static void calc_datetime_days_diff(MYSQL_TIME *ltime, long days) ltime->minute) * 60LL + ltime->second) * 1000000LL + ltime->second_part); - unpack_time(timediff, ltime); - /* - unpack_time() broke down hours into ltime members hour,day,month. - Mix them back to ltime->hour using the same factors - that pack_time()/unpack_time() use (i.e. 32 for month). - */ - ltime->hour+= (ltime->month * 32 + ltime->day) * 24; - ltime->month= ltime->day= 0; + unpack_time(timediff, ltime, MYSQL_TIMESTAMP_TIME); } - ltime->time_type= MYSQL_TIMESTAMP_TIME; } -int Field_time::store_time_dec(MYSQL_TIME *ltime, uint dec) +int Field_time::store_time_dec(const MYSQL_TIME *ltime, uint dec) { MYSQL_TIME l_time= *ltime; ErrConvTime str(ltime); @@ -6114,7 +6015,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_THD(thd, ER_WARN_DATA_OUT_OF_RANGE), field_name, + ER_THD(thd, ER_WARN_DATA_OUT_OF_RANGE), field_name.str, thd->get_stmt_da()->current_row_for_warning()); return true; } @@ -6193,8 +6094,10 @@ int Field_time_hires::reset() } -void Field_time_hires::store_TIME(MYSQL_TIME *ltime) +void Field_time_hires::store_TIME(const MYSQL_TIME *ltime) { + DBUG_ASSERT(ltime->year == 0); + DBUG_ASSERT(ltime->month == 0); ulonglong packed= sec_part_shift(pack_time(ltime), dec) + zero_point; store_bigendian(packed, ptr, Field_time_hires::pack_length()); } @@ -6214,6 +6117,39 @@ int Field_time::store_decimal(const my_decimal *d) } +bool Field_time::can_be_substituted_to_equal_item(const Context &ctx, + const Item_equal *item_equal) +{ + DBUG_ASSERT(item_equal->compare_type_handler()->cmp_type() != STRING_RESULT); + switch (ctx.subst_constraint()) { + case ANY_SUBST: + /* + A TIME field in a DATETIME comparison can be substituted to + Item_equal with TIME comparison. + + SET timestamp=UNIX_TIMESTAMP('2015-08-30 10:20:30'); + CREATE OR REPLACE TABLE t1 (a TIME); + INSERT INTO t1 VALUES ('00:00:00'),('00:00:01'); + SELECT * FROM t1 WHERE a>=TIMESTAMP'2015-08-30 00:00:00' + AND a='00:00:00'; + + The above query can be simplified to: + SELECT * FROM t1 WHERE TIME'00:00:00'>=TIMESTAMP'2015-08-30 00:00:00' + AND a='00:00:00'; + And further to: + SELECT * FROM t1 WHERE a=TIME'00:00:00'; + */ + if (ctx.compare_type_handler() == &type_handler_datetime && + item_equal->compare_type_handler() == &type_handler_time) + return true; + return ctx.compare_type_handler() == item_equal->compare_type_handler(); + case IDENTITY_SUBST: + return true; + } + return false; +} + + Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx, Item *const_item) { @@ -6223,10 +6159,8 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx, { 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)) + ulonglong fuzzydate= Time::comparison_flags_for_get_date(); + if (const_item->get_time_with_conversion(thd, <ime, fuzzydate)) return NULL; /* Replace a DATE/DATETIME constant to a TIME constant: @@ -6273,11 +6207,6 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx, } -uint32 Field_time_hires::pack_length() const -{ - return time_hires_bytes[dec]; -} - longlong Field_time_with_dec::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; @@ -6304,14 +6233,7 @@ bool Field_time_hires::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) packed= sec_part_unshift(packed - zero_point, dec); - unpack_time(packed, ltime); - /* - unpack_time() returns MYSQL_TIMESTAMP_DATETIME. - To get MYSQL_TIMESTAMP_TIME we need few adjustments - */ - ltime->time_type= MYSQL_TIMESTAMP_TIME; - ltime->hour+= (ltime->month*32+ltime->day)*24; - ltime->month= ltime->day= 0; + unpack_time(packed, ltime, MYSQL_TIMESTAMP_TIME); return false; } @@ -6330,9 +6252,9 @@ void Field_time_hires::sort_string(uchar *to,uint length __attribute__((unused)) to[0]^= 128; } -void Field_time_with_dec::make_field(Send_field *field) +void Field_time_with_dec::make_send_field(Send_field *field) { - Field::make_field(field); + Field::make_send_field(field); field->decimals= dec; } @@ -6348,9 +6270,8 @@ int Field_timef::reset() return 0; } -void Field_timef::store_TIME(MYSQL_TIME *ltime) +void Field_timef::store_TIME(const MYSQL_TIME *ltime) { - my_time_trunc(ltime, decimals()); longlong tmp= TIME_to_longlong_time_packed(ltime); my_time_packed_to_binary(tmp, ptr, dec); } @@ -6370,7 +6291,7 @@ bool Field_timef::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) ** Can handle 2 byte or 4 byte years! ****************************************************************************/ -int Field_year::store(const char *from, uint len,CHARSET_INFO *cs) +int Field_year::store(const char *from, size_t len,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; char *end; @@ -6384,10 +6305,10 @@ int Field_year::store(const char *from, uint len,CHARSET_INFO *cs) set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); return 1; } - if (get_thd()->count_cuted_fields && + if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && (error= check_int(cs, from, len, end, error))) { - if (error == 1) /* empty or incorrect string */ + if (unlikely(error == 1) /* empty or incorrect string */) { *ptr= 0; return 1; @@ -6439,7 +6360,7 @@ int Field_year::store(longlong nr, bool unsigned_val) } -int Field_year::store_time_dec(MYSQL_TIME *ltime, uint dec_arg) +int Field_year::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg) { ErrConvTime str(ltime); if (Field_year::store(ltime->year, 0)) @@ -6495,7 +6416,7 @@ bool Field_year::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) if (tmp || field_length != 4) tmp+= 1900; return int_to_datetime_with_warn(false, tmp * 10000, - ltime, fuzzydate, table->s, field_name); + ltime, fuzzydate, table->s, field_name.str); } @@ -6712,10 +6633,9 @@ Item *Field_newdate::get_equal_const_item(THD *thd, const Context &ctx, 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)) + Datetime dt(thd, const_item, TIME_FUZZY_DATES | TIME_INVALID_DATES); + if (!dt.is_valid_datetime()) return NULL; /* Replace the constant to a DATE or DATETIME constant. @@ -6728,26 +6648,23 @@ Item *Field_newdate::get_equal_const_item(THD *thd, const Context &ctx, (assuming CURRENT_DATE is '2015-08-30' */ - if (non_zero_hhmmssuu(<ime)) + if (!dt.hhmmssff_is_zero()) return new (thd->mem_root) - Item_datetime_literal_for_invalid_dates(thd, <ime, - ltime.second_part ? + Item_datetime_literal_for_invalid_dates(thd, dt.get_mysql_time(), + dt.get_mysql_time()-> + second_part ? TIME_SECOND_PART_DIGITS : 0); - datetime_to_date(<ime); return new (thd->mem_root) - Item_date_literal_for_invalid_dates(thd, <ime); + Item_date_literal_for_invalid_dates(thd, Date(&dt).get_mysql_time()); } 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)) + Date d(thd, const_item, 0); + if (!d.is_valid_date()) return NULL; - datetime_to_date(<ime); - return new (thd->mem_root) Item_date_literal(thd, <ime); + return new (thd->mem_root) Item_date_literal(thd, d.get_mysql_time()); } break; } @@ -6975,16 +6892,11 @@ bool Field_datetime_hires::get_TIME(MYSQL_TIME *ltime, const uchar *pos, { ASSERT_COLUMN_MARKED_FOR_READ; ulonglong packed= read_bigendian(pos, Field_datetime_hires::pack_length()); - unpack_time(sec_part_unshift(packed, dec), ltime); + unpack_time(sec_part_unshift(packed, dec), ltime, MYSQL_TIMESTAMP_DATETIME); return validate_MMDD(packed, ltime->month, ltime->day, fuzzydate); } -uint32 Field_datetime_hires::pack_length() const -{ - return datetime_hires_bytes[dec]; -} - int Field_datetime_hires::cmp(const uchar *a_ptr, const uchar *b_ptr) { ulonglong a=read_bigendian(a_ptr, Field_datetime_hires::pack_length()); @@ -6992,9 +6904,9 @@ int Field_datetime_hires::cmp(const uchar *a_ptr, const uchar *b_ptr) return a < b ? -1 : a > b ? 1 : 0; } -void Field_datetime_with_dec::make_field(Send_field *field) +void Field_datetime_with_dec::make_send_field(Send_field *field) { - Field::make_field(field); + Field::make_send_field(field); field->decimals= dec; } @@ -7063,10 +6975,11 @@ Field_longstr::check_string_copy_error(const String_copier *copier, const char *pos; char tmp[32]; - if (!(pos= copier->most_important_error_pos())) + if (likely(!(pos= copier->most_important_error_pos()))) return FALSE; - if (get_thd()->count_cuted_fields) + /* Ignore errors from internal expressions */ + if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION) { convert_to_printable(tmp, sizeof(tmp), pos, (end - pos), cs, 6); set_warning_truncated_wrong_value("string", tmp); @@ -7101,7 +7014,7 @@ Field_longstr::report_if_important_data(const char *pstr, const char *end, { THD *thd; if ((pstr < end) && - (thd=get_thd())->count_cuted_fields) + (thd= get_thd())->count_cuted_fields > CHECK_FIELD_EXPRESSION) { if (test_if_important_data(field_charset, pstr, end)) { @@ -7124,19 +7037,19 @@ Field_longstr::report_if_important_data(const char *pstr, const char *end, /* Copy a string and fill with space */ -int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) +int Field_string::store(const char *from, size_t length,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; uint copy_length; - String_copier copier; + int rc; /* See the comment for Field_long::store(long long) */ DBUG_ASSERT(!table || table->in_use == current_thd); - copy_length= copier.well_formed_copy(field_charset, - (char*) ptr, field_length, - cs, from, length, - field_length / field_charset->mbmaxlen); + rc= well_formed_copy_with_check((char*) ptr, field_length, + cs, from, length, + field_length / field_charset->mbmaxlen, + false, ©_length); /* Append spaces if the string was shorter than the field. */ if (copy_length < field_length) @@ -7144,7 +7057,21 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) field_length-copy_length, field_charset->pad_char); - return check_conversion_status(&copier, from + length, cs, false); + return rc; +} + + +int Field_str::store(longlong nr, bool unsigned_val) +{ + char buff[64]; + uint length; + length= (uint) (field_charset->cset->longlong10_to_str)(field_charset, + buff, + sizeof(buff), + (unsigned_val ? 10: + -10), + nr); + return store(buff, length, field_charset); } @@ -7160,46 +7087,36 @@ int Field_str::store(double nr) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE]; - uint local_char_length= field_length / charset()->mbmaxlen; + uint local_char_length= MY_MIN(sizeof(buff), + field_length / field_charset->mbmaxlen); size_t length= 0; my_bool error= (local_char_length == 0); // my_gcvt() requires width > 0, and we may have a CHAR(0) column. - if (!error) + if (likely(!error)) length= my_gcvt(nr, MY_GCVT_ARG_DOUBLE, local_char_length, buff, &error); - if (error) + if (unlikely(error)) { if (get_thd()->abort_on_warning) set_warning(ER_DATA_TOO_LONG, 1); else set_warning(WARN_DATA_TRUNCATED, 1); } - return store(buff, length, &my_charset_numeric); + return store(buff, (uint)length, &my_charset_numeric); } uint Field::is_equal(Create_field *new_field) { - return (new_field->sql_type == real_type()); + return new_field->type_handler() == type_handler(); } uint Field_str::is_equal(Create_field *new_field) { - return ((new_field->sql_type == real_type()) && - new_field->charset == field_charset && - new_field->length == max_display_length()); -} - - -int Field_string::store(longlong nr, bool unsigned_val) -{ - char buff[64]; - int l; - CHARSET_INFO *cs=charset(); - l= (cs->cset->longlong10_to_str)(cs,buff,sizeof(buff), - unsigned_val ? 10 : -10, nr); - return Field_string::store(buff,(uint)l,cs); + return new_field->type_handler() == type_handler() && + new_field->charset == field_charset && + new_field->length == max_display_length(); } @@ -7330,7 +7247,7 @@ String *Field_string::val_str(String *val_buffer __attribute__((unused)), ASSERT_COLUMN_MARKED_FOR_READ; /* See the comment for Field_long::store(long long) */ DBUG_ASSERT(!table || table->in_use == current_thd); - uint length; + size_t length; if (get_thd()->variables.sql_mode & MODE_PAD_CHAR_TO_FULL_LENGTH) length= my_charpos(field_charset, ptr, ptr + field_length, @@ -7368,7 +7285,7 @@ check_field_for_37426(const void *param_arg) Check_field_param *param= (Check_field_param*) param_arg; DBUG_ASSERT(param->field->real_type() == MYSQL_TYPE_STRING); DBUG_PRINT("debug", ("Field %s - type: %d, size: %d", - param->field->field_name, + param->field->field_name.str, param->field->real_type(), param->field->row_pack_length())); return param->field->row_pack_length() > 255; @@ -7393,11 +7310,11 @@ Field_string::compatible_field_size(uint field_metadata, int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr) { - uint a_len, b_len; + size_t a_len, b_len; if (field_charset->mbmaxlen != 1) { - uint char_len= field_length/field_charset->mbmaxlen; + size_t char_len= field_length/field_charset->mbmaxlen; a_len= my_charpos(field_charset, a_ptr, a_ptr + field_length, char_len); b_len= my_charpos(field_charset, b_ptr, b_ptr + field_length, char_len); } @@ -7415,7 +7332,9 @@ int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr) void Field_string::sort_string(uchar *to,uint length) { - uint tmp __attribute__((unused))= +#ifdef DBUG_ASSERT_EXISTS + size_t tmp= +#endif field_charset->coll->strnxfrm(field_charset, to, length, char_length() * @@ -7431,7 +7350,7 @@ void Field_string::sql_type(String &res) const { THD *thd= table->in_use; CHARSET_INFO *cs=res.charset(); - ulong length; + size_t length; length= cs->cset->snprintf(cs,(char*) res.ptr(), res.alloced_length(), "%s(%d)", @@ -7470,9 +7389,10 @@ void Field_string::sql_rpl_type(String *res) const uchar *Field_string::pack(uchar *to, const uchar *from, uint max_length) { - uint length= MY_MIN(field_length,max_length); - uint local_char_length= max_length/field_charset->mbmaxlen; - DBUG_PRINT("debug", ("Packing field '%s' - length: %u ", field_name, length)); + size_t length= MY_MIN(field_length,max_length); + size_t local_char_length= max_length/field_charset->mbmaxlen; + DBUG_PRINT("debug", ("Packing field '%s' - length: %zu ", field_name.str, + length)); if (length > local_char_length) local_char_length= my_charpos(field_charset, from, from+length, @@ -7515,7 +7435,7 @@ uchar *Field_string::pack(uchar *to, const uchar *from, uint max_length) the master. @note For information about how the length is packed, see @c - Field_string::do_save_field_metadata + Field_string::save_field_metadata @param to Destination of the data @param from Source of the data @@ -7598,7 +7518,7 @@ Field_string::unpack(uchar *to, const uchar *from, const uchar *from_end, @returns number of bytes written to metadata_ptr */ -int Field_string::do_save_field_metadata(uchar *metadata_ptr) +int Field_string::save_field_metadata(uchar *metadata_ptr) { DBUG_ASSERT(field_length < 1024); DBUG_ASSERT((real_type() & 0xF0) == 0xF0); @@ -7626,14 +7546,14 @@ uint Field_string::max_packed_col_length(uint max_length) uint Field_string::get_key_image(uchar *buff, uint length, imagetype type_arg) { - uint bytes = my_charpos(field_charset, (char*) ptr, + size_t bytes = my_charpos(field_charset, (char*) ptr, (char*) ptr + field_length, length / field_charset->mbmaxlen); memcpy(buff, ptr, bytes); if (bytes < length) field_charset->cset->fill(field_charset, (char*) buff + bytes, length - bytes, field_charset->pad_char); - return bytes; + return (uint)bytes; } @@ -7644,7 +7564,7 @@ Field *Field_string::make_new_field(MEM_ROOT *root, TABLE *new_table, if (type() != MYSQL_TYPE_VAR_STRING || keep_type) field= Field::make_new_field(root, new_table, keep_type); else if ((field= new (root) Field_varstring(field_length, maybe_null(), - field_name, + &field_name, new_table->s, charset()))) { /* @@ -7687,44 +7607,27 @@ const uint Field_varstring::MAX_SIZE= UINT_MAX16; @returns number of bytes written to metadata_ptr */ -int Field_varstring::do_save_field_metadata(uchar *metadata_ptr) +int Field_varstring::save_field_metadata(uchar *metadata_ptr) { DBUG_ASSERT(field_length <= 65535); int2store((char*)metadata_ptr, field_length); return 2; } -int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs) +int Field_varstring::store(const char *from,size_t length,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; uint copy_length; - String_copier copier; - - copy_length= copier.well_formed_copy(field_charset, - (char*) ptr + length_bytes, - field_length, - cs, from, length, - field_length / field_charset->mbmaxlen); - if (length_bytes == 1) - *ptr= (uchar) copy_length; - else - int2store(ptr, copy_length); + int rc; - return check_conversion_status(&copier, from + length, cs, true); -} + rc= well_formed_copy_with_check((char*) get_data(), field_length, + cs, from, length, + field_length / field_charset->mbmaxlen, + true, ©_length); + store_length(copy_length); -int Field_varstring::store(longlong nr, bool unsigned_val) -{ - char buff[64]; - uint length; - length= (uint) (field_charset->cset->longlong10_to_str)(field_charset, - buff, - sizeof(buff), - (unsigned_val ? 10: - -10), - nr); - return Field_varstring::store(buff, length, field_charset); + return rc; } @@ -7809,8 +7712,8 @@ int Field_varstring::cmp_max(const uchar *a_ptr, const uchar *b_ptr, int Field_varstring::key_cmp(const uchar *key_ptr, uint max_key_length) { - uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); - uint local_char_length= max_key_length / field_charset->mbmaxlen; + size_t length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); + size_t local_char_length= max_key_length / field_charset->mbmaxlen; local_char_length= my_charpos(field_charset, ptr + length_bytes, ptr + length_bytes + length, local_char_length); @@ -7844,26 +7747,29 @@ int Field_varstring::key_cmp(const uchar *a,const uchar *b) void Field_varstring::sort_string(uchar *to,uint length) { - uint tot_length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); + String buf; + + val_str(&buf, &buf); if (field_charset == &my_charset_bin) { /* Store length last in high-byte order to sort longer strings first */ if (length_bytes == 1) - to[length-1]= tot_length; + to[length - 1]= buf.length(); else - mi_int2store(to+length-2, tot_length); + mi_int2store(to + length - 2, buf.length()); length-= length_bytes; } - - tot_length= field_charset->coll->strnxfrm(field_charset, - to, length, - char_length() * - field_charset->strxfrm_multiply, - ptr + length_bytes, tot_length, - MY_STRXFRM_PAD_WITH_SPACE | - MY_STRXFRM_PAD_TO_MAXLEN); - DBUG_ASSERT(tot_length == length); + +#ifdef DBUG_ASSERT_EXISTS + size_t rc= +#endif + field_charset->coll->strnxfrm(field_charset, to, length, + char_length() * field_charset->strxfrm_multiply, + (const uchar*) buf.ptr(), buf.length(), + MY_STRXFRM_PAD_WITH_SPACE | + MY_STRXFRM_PAD_TO_MAXLEN); + DBUG_ASSERT(rc == length); } @@ -7879,16 +7785,21 @@ enum ha_base_keytype Field_varstring::key_type() const } +/* + Compressed columns need one extra byte to store the compression method. + This byte is invisible to the end user, but not for the storage engine. +*/ + void Field_varstring::sql_type(String &res) const { THD *thd= table->in_use; CHARSET_INFO *cs=res.charset(); - ulong length; + size_t length; length= cs->cset->snprintf(cs,(char*) res.ptr(), - res.alloced_length(), "%s(%d)", + res.alloced_length(), "%s(%u)", (has_charset() ? "varchar" : "varbinary"), - (int) field_length / charset()->mbmaxlen); + (uint) char_length()); res.length(length); if ((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) && has_charset() && (charset()->state & MY_CS_BINSORT)) @@ -8013,32 +7924,36 @@ uint Field_varstring::max_packed_col_length(uint max_length) 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; - uchar *pos= ptr+length_bytes; - local_char_length= my_charpos(field_charset, pos, pos + f_length, - local_char_length); - set_if_smaller(f_length, local_char_length); + String val; + uint local_char_length; + my_bitmap_map *old_map; + + old_map= dbug_tmp_use_all_columns(table, table->read_set); + val_str(&val, &val); + dbug_tmp_restore_column_map(table->read_set, old_map); + + local_char_length= val.charpos(length / field_charset->mbmaxlen); + if (local_char_length < val.length()) + val.length(local_char_length); /* Key is always stored with 2 bytes */ - int2store(buff,f_length); - memcpy(buff+HA_KEY_BLOB_LENGTH, pos, f_length); - if (f_length < length) + int2store(buff, val.length()); + memcpy(buff + HA_KEY_BLOB_LENGTH, val.ptr(), val.length()); + if (val.length() < length) { /* Must clear this as we do a memcmp in opt_range.cc to detect identical keys */ - bzero(buff+HA_KEY_BLOB_LENGTH+f_length, (length-f_length)); + memset(buff + HA_KEY_BLOB_LENGTH + val.length(), 0, length - val.length()); } - return HA_KEY_BLOB_LENGTH+f_length; + return HA_KEY_BLOB_LENGTH + val.length(); } void Field_varstring::set_key_image(const uchar *buff,uint length) { length= uint2korr(buff); // Real length is here - (void) Field_varstring::store((const char*) buff+HA_KEY_BLOB_LENGTH, length, - field_charset); + (void) store((const char*) buff + HA_KEY_BLOB_LENGTH, length, field_charset); } @@ -8094,14 +8009,15 @@ Field *Field_varstring::new_key_field(MEM_ROOT *root, TABLE *new_table, uint Field_varstring::is_equal(Create_field *new_field) { - if (new_field->sql_type == real_type() && - new_field->charset == field_charset) + if (new_field->type_handler() == type_handler() && + new_field->charset == field_charset && + !new_field->compression_method() == !compression_method()) { - if (new_field->length == max_display_length()) + if (new_field->length == field_length) return IS_EQUAL_YES; - if (new_field->length > max_display_length() && - ((new_field->length <= 255 && max_display_length() <= 255) || - (new_field->length > 255 && max_display_length() > 255))) + if (new_field->length > field_length && + ((new_field->length <= 255 && field_length <= 255) || + (new_field->length > 255 && field_length > 255))) return IS_EQUAL_PACK_LENGTH; // VARCHAR, longer variable length } return IS_EQUAL_NO; @@ -8123,6 +8039,212 @@ void Field_varstring::hash(ulong *nr, ulong *nr2) } +/** + Compress field + + @param[out] to destination buffer for compressed data + @param[in] to_length size of to + @param[in] from data to compress + @param[in] length from length + @param[in] max_length truncate `from' to this length + @param[out] out_length compessed data length + @param[in] cs from character set + @param[in] nchars copy no more than "nchars" characters + + In worst case (no compression performed) storage requirement is increased by + 1 byte to store header. If it exceeds field length, normal data truncation is + performed. + + Generic compressed header format (1 byte): + + Bits 1-4: method specific bits + Bits 5-8: compression method + + If compression method is 0 then header is immediately followed by + uncompressed data. + + If compression method is zlib: + + Bits 1-3: number of bytes occupied by original data length + Bits 4: true if zlib wrapper not present + Bits 5-8: store 8 (zlib) + + Header is immediately followed by original data length, + followed by compressed data. +*/ + +int Field_longstr::compress(char *to, uint to_length, + const char *from, uint length, + uint max_length, + uint *out_length, + CHARSET_INFO *cs, size_t nchars) +{ + THD *thd= get_thd(); + char *buf; + uint buf_length; + int rc= 0; + + if (String::needs_conversion_on_storage(length, cs, field_charset) || + max_length < length) + { + set_if_smaller(max_length, static_cast<ulonglong>(field_charset->mbmaxlen) * length + 1); + if (!(buf= (char*) my_malloc(max_length, MYF(MY_WME)))) + { + *out_length= 0; + return -1; + } + + rc= well_formed_copy_with_check(buf, max_length, cs, from, length, + nchars, true, &buf_length); + } + else + { + buf= const_cast<char*>(from); + buf_length= length; + } + + if (buf_length == 0) + *out_length= 0; + else if (buf_length >= thd->variables.column_compression_threshold && + (*out_length= compression_method()->compress(thd, to, buf, buf_length))) + status_var_increment(thd->status_var.column_compressions); + else + { + /* Store uncompressed */ + to[0]= 0; + if (buf_length < to_length) + memcpy(to + 1, buf, buf_length); + else + { + /* Storing string at blob capacity, e.g. 255 bytes string to TINYBLOB. */ + rc= well_formed_copy_with_check(to + 1, to_length - 1, cs, from, length, + nchars, true, &buf_length); + } + *out_length= buf_length + 1; + } + + if (buf != from) + my_free(buf); + return rc; +} + + +/* + Memory is allocated only when original data was actually compressed. + Otherwise val_ptr points at data located immediately after header. + + Data can be stored uncompressed if data was shorter than threshold + or compressed data was longer than original data. +*/ + +String *Field_longstr::uncompress(String *val_buffer, String *val_ptr, + const uchar *from, uint from_length) +{ + if (from_length) + { + uchar method= (*from & 0xF0) >> 4; + + /* Uncompressed data */ + if (!method) + { + val_ptr->set((const char*) from + 1, from_length - 1, field_charset); + return val_ptr; + } + + if (compression_methods[method].uncompress) + { + if (!compression_methods[method].uncompress(val_buffer, from, from_length, + field_length)) + { + val_buffer->set_charset(field_charset); + status_var_increment(get_thd()->status_var.column_decompressions); + return val_buffer; + } + } + } + + /* + It would be better to return 0 in case of errors, but to take the + safer route, let's return a zero string and let the general + handler catch the error. + */ + val_ptr->set("", 0, field_charset); + return val_ptr; +} + + +int Field_varstring_compressed::store(const char *from, size_t length, + CHARSET_INFO *cs) +{ + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + uint compressed_length; + int rc= compress((char*) get_data(), field_length, from, (uint) length, + Field_varstring_compressed::max_display_length(), + &compressed_length, cs, + Field_varstring_compressed::char_length()); + store_length(compressed_length); + return rc; +} + + +String *Field_varstring_compressed::val_str(String *val_buffer, String *val_ptr) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + return uncompress(val_buffer, val_ptr, get_data(), get_length()); +} + + +double Field_varstring_compressed::val_real(void) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + THD *thd= get_thd(); + String buf; + val_str(&buf, &buf); + return Converter_strntod_with_warn(thd, Warn_filter(thd), field_charset, + buf.ptr(), buf.length()).result(); +} + + +longlong Field_varstring_compressed::val_int(void) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + THD *thd= get_thd(); + String buf; + val_str(&buf, &buf); + return Converter_strntoll_with_warn(thd, Warn_filter(thd), field_charset, + buf.ptr(), buf.length()).result(); +} + + +int Field_varstring_compressed::cmp_max(const uchar *a_ptr, const uchar *b_ptr, + uint max_len) +{ + String a, b; + uint a_length, b_length; + + if (length_bytes == 1) + { + a_length= (uint) *a_ptr; + b_length= (uint) *b_ptr; + } + else + { + a_length= uint2korr(a_ptr); + b_length= uint2korr(b_ptr); + } + + uncompress(&a, &a, a_ptr + length_bytes, a_length); + uncompress(&b, &b, b_ptr + length_bytes, b_length); + + if (a.length() > max_len) + a.length(max_len); + if (b.length() > max_len) + b.length(max_len); + + return sortcmp(&a, &b, field_charset); +} + + /**************************************************************************** ** blob type ** A blob is saved as a length and a pointer. The length is stored in the @@ -8130,12 +8252,13 @@ void Field_varstring::hash(ulong *nr, ulong *nr2) ****************************************************************************/ Field_blob::Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, + enum utype unireg_check_arg, + const LEX_CSTRING *field_name_arg, TABLE_SHARE *share, uint blob_pack_length, - CHARSET_INFO *cs) + const DTCollation &collation) :Field_longstr(ptr_arg, BLOB_PACK_LENGTH_TO_MAX_LENGH(blob_pack_length), null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, - cs), + collation), packlength(blob_pack_length) { DBUG_ASSERT(blob_pack_length <= 4); // Only pack lengths 1-4 supported currently @@ -8164,6 +8287,7 @@ uint32 Field_blob::get_length(const uchar *pos, uint packlength_arg) const int Field_blob::copy_value(Field_blob *from) { DBUG_ASSERT(field_charset == from->charset()); + DBUG_ASSERT(!compression_method() == !from->compression_method()); int rc= 0; uint32 length= from->get_length(); uchar *data= from->get_ptr(); @@ -8182,14 +8306,15 @@ int Field_blob::copy_value(Field_blob *from) } -int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) +int Field_blob::store(const char *from,size_t length,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; - uint copy_length, new_length; - String_copier copier; + size_t copy_length, new_length; + uint copy_len; char *tmp; char buff[STRING_BUFFER_USUAL_SIZE]; String tmpstr(buff,sizeof(buff), &my_charset_bin); + int rc; if (!length) { @@ -8210,7 +8335,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) DBUG_ASSERT(length <= max_data_length()); new_length= length; - copy_length= (uint)MY_MIN(UINT_MAX,table->in_use->variables.group_concat_max_len); + copy_length= (size_t)MY_MIN(UINT_MAX,table->in_use->variables.group_concat_max_len); if (new_length > copy_length) { new_length= Well_formed_prefix(cs, @@ -8262,13 +8387,13 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) bmove(ptr + packlength, (uchar*) &tmp, sizeof(char*)); return 0; } - copy_length= copier.well_formed_copy(field_charset, - (char*) value.ptr(), new_length, - cs, from, length); - Field_blob::store_length(copy_length); + rc= well_formed_copy_with_check((char*) value.ptr(), (uint) new_length, + cs, from, length, + length, true, ©_len); + Field_blob::store_length(copy_len); bmove(ptr+packlength,(uchar*) &tmp,sizeof(char*)); - return check_conversion_status(&copier, from + length, cs, true); + return rc; oom_error: /* Fatal OOM error */ @@ -8277,22 +8402,6 @@ oom_error: } -int Field_blob::store(double nr) -{ - CHARSET_INFO *cs=charset(); - value.set_real(nr, NOT_FIXED_DEC, cs); - return Field_blob::store(value.ptr(),(uint) value.length(), cs); -} - - -int Field_blob::store(longlong nr, bool unsigned_val) -{ - CHARSET_INFO *cs=charset(); - value.set_int(nr, unsigned_val, cs); - return Field_blob::store(value.ptr(), (uint) value.length(), cs); -} - - double Field_blob::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; @@ -8402,7 +8511,7 @@ int Field_blob::cmp_binary(const uchar *a_ptr, const uchar *b_ptr, uint Field_blob::get_key_image(uchar *buff,uint length, imagetype type_arg) { - uint32 blob_length= get_length(ptr); + size_t blob_length= get_length(ptr); uchar *blob; #ifdef HAVE_SPATIAL @@ -8420,7 +8529,7 @@ uint Field_blob::get_key_image(uchar *buff,uint length, imagetype type_arg) return image_length; } blob= get_ptr(); - gobj= Geometry::construct(&buffer, (char*) blob, blob_length); + gobj= Geometry::construct(&buffer, (char*) blob, (uint32)blob_length); if (!gobj || gobj->get_mbr(&mbr, &dummy)) bzero(buff, image_length); else @@ -8435,12 +8544,12 @@ uint Field_blob::get_key_image(uchar *buff,uint length, imagetype type_arg) #endif /*HAVE_SPATIAL*/ blob= get_ptr(); - uint local_char_length= length / field_charset->mbmaxlen; + size_t local_char_length= length / field_charset->mbmaxlen; local_char_length= my_charpos(field_charset, blob, blob + blob_length, local_char_length); set_if_smaller(blob_length, local_char_length); - if ((uint32) length > blob_length) + if (length > blob_length) { /* Must clear this as we do a memcmp in opt_range.cc to detect @@ -8466,14 +8575,14 @@ void Field_blob::set_key_image(const uchar *buff,uint length) int Field_blob::key_cmp(const uchar *key_ptr, uint max_key_length) { uchar *blob1; - uint blob_length=get_length(ptr); + size_t blob_length=get_length(ptr); memcpy(&blob1, ptr+packlength, sizeof(char*)); CHARSET_INFO *cs= charset(); - uint local_char_length= max_key_length / cs->mbmaxlen; + size_t local_char_length= max_key_length / cs->mbmaxlen; local_char_length= my_charpos(cs, blob1, blob1+blob_length, local_char_length); set_if_smaller(blob_length, local_char_length); - return Field_blob::cmp(blob1, blob_length, + return Field_blob::cmp(blob1, (uint32)blob_length, key_ptr+HA_KEY_BLOB_LENGTH, uint2korr(key_ptr)); } @@ -8490,8 +8599,10 @@ Field *Field_blob::new_key_field(MEM_ROOT *root, TABLE *new_table, uchar *new_null_ptr, uint new_null_bit) { Field_varstring *res= new (root) Field_varstring(new_ptr, length, 2, - new_null_ptr, new_null_bit, Field::NONE, - field_name, table->s, charset()); + new_null_ptr, + new_null_bit, Field::NONE, + &field_name, + table->s, charset()); res->init(new_table); return res; } @@ -8507,9 +8618,9 @@ Field *Field_blob::new_key_field(MEM_ROOT *root, TABLE *new_table, @returns number of bytes written to metadata_ptr */ -int Field_blob::do_save_field_metadata(uchar *metadata_ptr) +int Field_blob::save_field_metadata(uchar *metadata_ptr) { - DBUG_ENTER("Field_blob::do_save_field_metadata"); + DBUG_ENTER("Field_blob::save_field_metadata"); *metadata_ptr= pack_length_no_ptr(); DBUG_PRINT("debug", ("metadata: %u (pack_length_no_ptr)", *metadata_ptr)); DBUG_RETURN(1); @@ -8525,34 +8636,47 @@ uint32 Field_blob::sort_length() const void Field_blob::sort_string(uchar *to,uint length) { - uchar *blob; - uint blob_length=get_length(); + String buf; - if (!blob_length && field_charset->pad_char == 0) + val_str(&buf, &buf); + if (!buf.length() && field_charset->pad_char == 0) bzero(to,length); else { if (field_charset == &my_charset_bin) { - uchar *pos; - /* Store length of blob last in blob to shorter blobs before longer blobs */ length-= packlength; - pos= to+length; - - store_bigendian(blob_length, pos, packlength); + store_bigendian(buf.length(), to + length, packlength); } - memcpy(&blob, ptr+packlength, sizeof(char*)); - - blob_length= field_charset->coll->strnxfrm(field_charset, - to, length, length, - blob, blob_length, - MY_STRXFRM_PAD_WITH_SPACE | - MY_STRXFRM_PAD_TO_MAXLEN); - DBUG_ASSERT(blob_length == length); + +#ifdef DBUG_ASSERT_EXISTS + size_t rc= +#endif + field_charset->coll->strnxfrm(field_charset, to, length, length, + (const uchar*) buf.ptr(), buf.length(), + MY_STRXFRM_PAD_WITH_SPACE | + MY_STRXFRM_PAD_TO_MAXLEN); + DBUG_ASSERT(rc == length); + } +} + + +/* + Return the data type handler, according to packlength. + Implemented in field.cc rather than in field.h + to avoid exporting type_handler_xxx with MYSQL_PLUGIN_IMPORT. +*/ +const Type_handler *Field_blob::type_handler() const +{ + switch (packlength) { + case 1: return &type_handler_tiny_blob; + case 2: return &type_handler_blob; + case 3: return &type_handler_medium_blob; } + return &type_handler_long_blob; } @@ -8568,7 +8692,11 @@ void Field_blob::sql_type(String &res) const } res.set_ascii(str,length); if (charset() == &my_charset_bin) + { res.append(STRING_WITH_LEN("blob")); + if (packlength == 2 && (get_thd()->variables.sql_mode & MODE_ORACLE)) + res.append(STRING_WITH_LEN("(65535)")); + } else { res.append(STRING_WITH_LEN("text")); @@ -8618,6 +8746,7 @@ uchar *Field_blob::pack(uchar *to, const uchar *from, uint max_length) @return New pointer into memory based on from + length of the data */ + const uchar *Field_blob::unpack(uchar *to, const uchar *from, const uchar *from_end, uint param_data) { @@ -8630,12 +8759,9 @@ const uchar *Field_blob::unpack(uchar *to, const uchar *from, DBUG_RETURN(0); // Error in data uint32 const length= get_length(from, master_packlength); DBUG_DUMP("packed", from, length + master_packlength); - bitmap_set_bit(table->write_set, field_index); if (from + master_packlength + length > from_end) DBUG_RETURN(0); - store(reinterpret_cast<const char*>(from) + master_packlength, - length, field_charset); - DBUG_DUMP("record", to, table->s->reclength); + set_ptr(length, const_cast<uchar*> (from) + master_packlength); DBUG_RETURN(from + master_packlength + length); } @@ -8654,11 +8780,77 @@ uint Field_blob::max_packed_col_length(uint max_length) } +/* + Blob fields are regarded equal if they have same character set, + same blob store length and if either both are compressed or both are + uncompressed. + The logic for compression is that we don't have to uncompress and compress + again an already compressed field just because compression method changes. +*/ + uint Field_blob::is_equal(Create_field *new_field) { - return ((new_field->sql_type == get_blob_type_from_length(max_data_length())) - && new_field->charset == field_charset && - new_field->pack_length == pack_length()); + return new_field->type_handler() == type_handler() && + new_field->charset == field_charset && + new_field->pack_length == pack_length() && + !new_field->compression_method() == !compression_method(); +} + + +int Field_blob_compressed::store(const char *from, size_t length, + CHARSET_INFO *cs) +{ + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + uint compressed_length; + uint max_length= max_data_length(); + uint to_length= (uint) MY_MIN(max_length, + field_charset->mbmaxlen * length + 1); + String tmp(from, length, cs); + int rc; + + if (from >= value.ptr() && from <= value.end() && tmp.copy(from, length, cs)) + goto oom; + + if (value.alloc(to_length)) + goto oom; + + rc= compress((char*) value.ptr(), to_length, tmp.ptr(), (uint) length, + max_length, &compressed_length, cs, (uint) length); + set_ptr(compressed_length, (uchar*) value.ptr()); + return rc; + +oom: + set_ptr((uint32) 0, NULL); + return -1; +} + + +String *Field_blob_compressed::val_str(String *val_buffer, String *val_ptr) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + return uncompress(val_buffer, val_ptr, get_ptr(), get_length()); +} + + +double Field_blob_compressed::val_real(void) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + THD *thd= get_thd(); + String buf; + val_str(&buf, &buf); + return Converter_strntod_with_warn(thd, Warn_filter(thd), field_charset, + buf.ptr(), buf.length()).result(); +} + + +longlong Field_blob_compressed::val_int(void) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + THD *thd= get_thd(); + String buf; + val_str(&buf, &buf); + return Converter_strntoll_with_warn(thd, Warn_filter(thd), field_charset, + buf.ptr(), buf.length()).result(); } @@ -8685,7 +8877,7 @@ uint gis_field_options_image(uchar *buff, List<Create_field> &create_fields) Create_field *field; while ((field= it++)) { - if (field->sql_type != MYSQL_TYPE_GEOMETRY) + if (field->real_field_type() != MYSQL_TYPE_GEOMETRY) continue; if (buff) { @@ -8712,7 +8904,7 @@ uint gis_field_options_image(uchar *buff, List<Create_field> &create_fields) } -uint gis_field_options_read(const uchar *buf, uint buf_len, +uint gis_field_options_read(const uchar *buf, size_t buf_len, Field_geom::storage_type *st_type,uint *precision, uint *scale, uint *srid) { const uchar *buf_end= buf + buf_len; @@ -8818,7 +9010,7 @@ int Field_geom::store_decimal(const my_decimal *) } -int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs) +int Field_geom::store(const char *from, size_t length, CHARSET_INFO *cs) { if (!length) bzero(ptr, Field_blob::pack_length()); @@ -8835,7 +9027,7 @@ int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs) wkb_type > (uint32) Geometry::wkb_last) goto err; - if (geom_type != Field::GEOM_GEOMETRY && + if (geom_type != Field::GEOM_GEOMETRY && geom_type != Field::GEOM_GEOMETRYCOLLECTION && (uint32) geom_type != wkb_type) { @@ -8850,7 +9042,7 @@ int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs) my_error(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, MYF(0), Geometry::ci_collection[geom_type]->m_name.str, Geometry::ci_collection[wkb_type]->m_name.str, - db, tab_name, field_name, + db, tab_name, field_name.str, (ulong) table->in_use->get_stmt_da()-> current_row_for_warning()); goto err_exit; @@ -8886,7 +9078,7 @@ Field::geometry_type Field_geom::geometry_type_merge(geometry_type a, uint Field_geom::is_equal(Create_field *new_field) { - return new_field->sql_type == MYSQL_TYPE_GEOMETRY && + return new_field->type_handler() == type_handler() && /* - Allow ALTER..INPLACE to supertype (GEOMETRY), e.g. POINT to GEOMETRY or POLYGON to GEOMETRY. @@ -8916,7 +9108,7 @@ bool Field_geom::load_data_set_null(THD *thd) Field_blob::reset(); if (!maybe_null()) { - my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0), field_name, + my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0), field_name.str, thd->get_stmt_da()->current_row_for_warning()); return true; } @@ -8963,7 +9155,7 @@ void Field_enum::store_type(ulonglong value) (if there isn't a empty value in the enum) */ -int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs) +int Field_enum::store(const char *from,size_t length,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int err= 0; @@ -8980,7 +9172,7 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs) } /* Remove end space */ - length= field_charset->cset->lengthsp(field_charset, from, length); + length= (uint)field_charset->cset->lengthsp(field_charset, from, length); uint tmp=find_type2(typelib, from, length, field_charset); if (!tmp) { @@ -8993,12 +9185,16 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs) { tmp=0; set_warning(WARN_DATA_TRUNCATED, 1); + err= 1; } - if (!get_thd()->count_cuted_fields) + if ((get_thd()->count_cuted_fields <= CHECK_FIELD_EXPRESSION) && !length) err= 0; } else + { set_warning(WARN_DATA_TRUNCATED, 1); + err= 1; + } } store_type((ulonglong) tmp); return err; @@ -9018,7 +9214,7 @@ int Field_enum::store(longlong nr, bool unsigned_val) if ((ulonglong) nr > typelib->count || nr == 0) { set_warning(WARN_DATA_TRUNCATED, 1); - if (nr != 0 || get_thd()->count_cuted_fields) + if (nr != 0 || get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION) { nr= 0; error= 1; @@ -9053,7 +9249,7 @@ longlong Field_enum::val_int(void) @returns number of bytes written to metadata_ptr */ -int Field_enum::do_save_field_metadata(uchar *metadata_ptr) +int Field_enum::save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= real_type(); *(metadata_ptr + 1)= pack_length(); @@ -9142,7 +9338,7 @@ Field *Field_enum::make_new_field(MEM_ROOT *root, TABLE *new_table, */ -int Field_set::store(const char *from,uint length,CHARSET_INFO *cs) +int Field_set::store(const char *from,size_t length,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; bool got_warning= 0; @@ -9172,6 +9368,7 @@ int Field_set::store(const char *from,uint length,CHARSET_INFO *cs) { tmp=0; set_warning(WARN_DATA_TRUNCATED, 1); + err= 1; } } else if (got_warning) @@ -9336,7 +9533,7 @@ uint Field_enum::is_equal(Create_field *new_field) The fields are compatible if they have the same flags, type, charset and have the same underlying length. */ - if (new_field->sql_type != real_type() || + if (new_field->type_handler() != type_handler() || new_field->charset != field_charset || new_field->pack_length != pack_length()) return IS_EQUAL_NO; @@ -9401,7 +9598,7 @@ bool Field_num::eq_def(const Field *field) const uint Field_num::is_equal(Create_field *new_field) { - return ((new_field->sql_type == real_type()) && + return ((new_field->type_handler() == type_handler()) && ((new_field->flags & UNSIGNED_FLAG) == (uint) (flags & UNSIGNED_FLAG)) && ((new_field->flags & AUTO_INCREMENT_FLAG) == @@ -9410,12 +9607,17 @@ uint Field_num::is_equal(Create_field *new_field) } +bool Field_enum::can_optimize_range(const Item_bool_func *cond, + const Item *item, + bool is_eq_func) const +{ + return item->cmp_type() != TIME_RESULT; +} + + 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: @@ -9465,7 +9667,8 @@ bool Field_enum::can_optimize_keypart_ref(const Item_bool_func *cond, Field_bit::Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, uchar *bit_ptr_arg, uchar bit_ofs_arg, - enum utype unireg_check_arg, const char *field_name_arg) + enum utype unireg_check_arg, + const LEX_CSTRING *field_name_arg) : Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg), bit_ptr(bit_ptr_arg), bit_ofs(bit_ofs_arg), bit_len(len_arg & 7), @@ -9549,19 +9752,19 @@ Field *Field_bit::new_key_field(MEM_ROOT *root, TABLE *new_table, uint Field_bit::is_equal(Create_field *new_field) { - return (new_field->sql_type == real_type() && - new_field->length == max_display_length()); + return new_field->type_handler() == type_handler() && + new_field->length == max_display_length(); } -int Field_bit::store(const char *from, uint length, CHARSET_INFO *cs) +int Field_bit::store(const char *from, size_t length, CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int delta; for (; length && !*from; from++, length--) // skip left 0's ; - delta= bytes_in_rec - length; + delta= (int)(bytes_in_rec - length); if (delta < -1 || (delta == -1 && (uchar) *from > ((1 << bit_len) - 1)) || @@ -9765,9 +9968,9 @@ uint Field_bit::get_key_image(uchar *buff, uint length, imagetype type_arg) @returns number of bytes written to metadata_ptr */ -int Field_bit::do_save_field_metadata(uchar *metadata_ptr) +int Field_bit::save_field_metadata(uchar *metadata_ptr) { - DBUG_ENTER("Field_bit::do_save_field_metadata"); + DBUG_ENTER("Field_bit::save_field_metadata"); DBUG_PRINT("debug", ("bit_len: %d, bytes_in_rec: %d", bit_len, bytes_in_rec)); /* @@ -9837,9 +10040,9 @@ Field_bit::compatible_field_size(uint field_metadata, void Field_bit::sql_type(String &res) const { CHARSET_INFO *cs= res.charset(); - ulong length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(), + size_t length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(), "bit(%d)", (int) field_length); - res.length((uint) length); + res.length(length); } @@ -9980,7 +10183,7 @@ int Field_bit::set_default() Field_bit_as_char::Field_bit_as_char(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, - const char *field_name_arg) + const LEX_CSTRING *field_name_arg) :Field_bit(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, 0, 0, unireg_check_arg, field_name_arg) { @@ -9990,7 +10193,7 @@ Field_bit_as_char::Field_bit_as_char(uchar *ptr_arg, uint32 len_arg, } -int Field_bit_as_char::store(const char *from, uint length, CHARSET_INFO *cs) +int Field_bit_as_char::store(const char *from, size_t length, CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int delta; @@ -9998,7 +10201,7 @@ int Field_bit_as_char::store(const char *from, uint length, CHARSET_INFO *cs) for (; length && !*from; from++, length--) // skip left 0's ; - delta= bytes_in_rec - length; + delta= (int)(bytes_in_rec - length); if (delta < 0 || (delta == 0 && bits && (uint) (uchar) *from >= (uint) (1 << bits))) @@ -10021,9 +10224,9 @@ int Field_bit_as_char::store(const char *from, uint length, CHARSET_INFO *cs) void Field_bit_as_char::sql_type(String &res) const { CHARSET_INFO *cs= res.charset(); - ulong length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(), + size_t length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(), "bit(%d)", (int) field_length); - res.length((uint) length); + res.length(length); } @@ -10031,65 +10234,189 @@ void Field_bit_as_char::sql_type(String &res) const Handling of field and Create_field *****************************************************************************/ -/** - Convert create_field::length from number of characters to number of bytes. -*/ - -void Column_definition::create_length_to_internal_length(void) -{ - switch (sql_type) { - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_LONG_BLOB: - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_GEOMETRY: - case MYSQL_TYPE_VAR_STRING: - case MYSQL_TYPE_STRING: - case MYSQL_TYPE_VARCHAR: - length*= charset->mbmaxlen; - set_if_smaller(length, UINT_MAX32); - key_length= (uint32)length; - pack_length= calc_pack_length(sql_type, key_length); - break; - case MYSQL_TYPE_ENUM: - case MYSQL_TYPE_SET: - /* Pack_length already calculated in sql_parse.cc */ - length*= charset->mbmaxlen; - key_length= pack_length; - break; - case MYSQL_TYPE_BIT: - if (f_bit_as_char(pack_flag)) +bool Column_definition::create_interval_from_interval_list(MEM_ROOT *mem_root, + bool reuse_interval_list_values) +{ + DBUG_ENTER("Column_definition::create_interval_from_interval_list"); + DBUG_ASSERT(!interval); + if (!(interval= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB)))) + DBUG_RETURN(true); // EOM + + List_iterator<String> it(interval_list); + StringBuffer<64> conv; + char comma_buf[5]; /* 5 bytes for 'filename' charset */ + DBUG_ASSERT(sizeof(comma_buf) >= charset->mbmaxlen); + int comma_length= charset->cset->wc_mb(charset, ',', + (uchar*) comma_buf, + (uchar*) comma_buf + + sizeof(comma_buf)); + DBUG_ASSERT(comma_length >= 0 && comma_length <= (int) sizeof(comma_buf)); + + if (!multi_alloc_root(mem_root, + &interval->type_names, + sizeof(char*) * (interval_list.elements + 1), + &interval->type_lengths, + sizeof(uint) * (interval_list.elements + 1), + NullS)) + goto err; // EOM + + interval->name= ""; + interval->count= interval_list.elements; + + for (uint i= 0; i < interval->count; i++) + { + uint32 dummy; + String *tmp= it++; + LEX_CSTRING value; + if (String::needs_conversion(tmp->length(), tmp->charset(), + charset, &dummy)) { - key_length= pack_length= ((length + 7) & ~7) / 8; + uint cnv_errs; + conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), charset, &cnv_errs); + value.str= strmake_root(mem_root, conv.ptr(), conv.length()); + value.length= conv.length(); } else { - pack_length= (uint)(length / 8); - /* We need one extra byte to store the bits we save among the null bits */ - key_length= pack_length + MY_TEST(length & 7); + value.str= reuse_interval_list_values ? tmp->ptr() : + strmake_root(mem_root, + tmp->ptr(), + tmp->length()); + value.length= tmp->length(); } - break; - case MYSQL_TYPE_NEWDECIMAL: + if (!value.str) + goto err; // EOM + + // Strip trailing spaces. + value.length= charset->cset->lengthsp(charset, value.str, value.length); + ((char*) value.str)[value.length]= '\0'; + + if (real_field_type() == MYSQL_TYPE_SET) + { + if (charset->coll->instr(charset, value.str, value.length, + comma_buf, comma_length, NULL, 0)) + { + ErrConvString err(tmp); + my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", err.ptr()); + goto err; + } + } + interval->type_names[i]= value.str; + interval->type_lengths[i]= (uint)value.length; + } + interval->type_names[interval->count]= 0; // End marker + interval->type_lengths[interval->count]= 0; + interval_list.empty(); // Don't need interval_list anymore + DBUG_RETURN(false); +err: + interval= NULL; // Avoid having both non-empty interval_list and interval + DBUG_RETURN(true); +} + + +bool Column_definition::prepare_interval_field(MEM_ROOT *mem_root, + bool reuse_interval_list_values) +{ + DBUG_ENTER("Column_definition::prepare_interval_field"); + DBUG_ASSERT(real_field_type() == MYSQL_TYPE_ENUM || + real_field_type() == MYSQL_TYPE_SET); + /* + Interval values are either in "interval" or in "interval_list", + but not in both at the same time, and are not empty at the same time. + - Values are in "interval_list" when we're coming from the parser + in CREATE TABLE or in CREATE {FUNCTION|PROCEDURE}. + - Values are in "interval" when we're in ALTER TABLE. + + In a corner case with an empty set like SET(''): + - after the parser we have interval_list.elements==1 + - in ALTER TABLE we have a non-NULL interval with interval->count==1, + with interval->type_names[0]=="" and interval->type_lengths[0]==0. + So the assert is still valid for this corner case. + + ENUM and SET with no values at all (e.g. ENUM(), SET()) are not possible, + as the parser requires at least one element, so for a ENUM or SET field it + should never happen that both internal_list.elements and interval are 0. + */ + DBUG_ASSERT((interval == NULL) == (interval_list.elements > 0)); + + /* + Create typelib from interval_list, and if necessary + convert strings from client character set to the + column character set. + */ + if (interval_list.elements && + create_interval_from_interval_list(mem_root, + reuse_interval_list_values)) + DBUG_RETURN(true); + + if (!reuse_interval_list_values) { /* - This code must be identical to code in - Field_new_decimal::Field_new_decimal as otherwise the record layout - gets out of sync. + We're initializing from an existing table or view Field_enum + (e.g. for a %TYPE variable) rather than from the parser. + The constructor Column_definition(THD*,Field*,Field*) has already + copied the TYPELIB pointer from the original Field_enum. + Now we need to make a permanent copy of that TYPELIB, + as the original field can be freed before the end of the life + cycle of "this". */ - uint precision= my_decimal_length_to_precision((uint)length, decimals, - flags & UNSIGNED_FLAG); - set_if_smaller(precision, DECIMAL_MAX_PRECISION); - key_length= pack_length= my_decimal_get_binary_size(precision, decimals); - break; + DBUG_ASSERT(interval); + if (!(interval= copy_typelib(mem_root, interval))) + DBUG_RETURN(true); } - default: - key_length= pack_length= calc_pack_length(sql_type, (uint)length); - break; + prepare_interval_field_calc_length(); + DBUG_RETURN(false); +} + + +void Column_definition::set_attributes(const Lex_field_type_st &type, + CHARSET_INFO *cs) +{ + DBUG_ASSERT(type_handler() == &type_handler_null); + DBUG_ASSERT(charset == &my_charset_bin || charset == NULL); + DBUG_ASSERT(length == 0); + DBUG_ASSERT(decimals == 0); + + set_handler(type.type_handler()); + charset= cs; + + if (type.length()) + { + int err; + length= my_strtoll10(type.length(), NULL, &err); + if (err) + length= ~0ULL; // safety + } + + if (type.dec()) + decimals= (uint) atoi(type.dec()); +} + + +void Column_definition::create_length_to_internal_length_bit() +{ + if (f_bit_as_char(pack_flag)) + { + key_length= pack_length= ((length + 7) & ~7) / 8; + } + else + { + pack_length= (uint) length / 8; + /* We need one extra byte to store the bits we save among the null bits */ + key_length= pack_length + MY_TEST(length & 7); } } -bool check_expression(Virtual_column_info *vcol, const char *name, +void Column_definition::create_length_to_internal_length_newdecimal() +{ + DBUG_ASSERT(length < UINT_MAX32); + uint prec= get_decimal_precision((uint)length, decimals, flags & UNSIGNED_FLAG); + key_length= pack_length= my_decimal_get_binary_size(prec, decimals); +} + + +bool check_expression(Virtual_column_info *vcol, LEX_CSTRING *name, enum_vcol_info_type type) { @@ -10097,7 +10424,7 @@ bool check_expression(Virtual_column_info *vcol, const char *name, Item::vcol_func_processor_result res; if (!vcol->name.length) - vcol->name.str= const_cast<char*>(name); + vcol->name= *name; /* Walk through the Item tree checking if all items are valid @@ -10110,11 +10437,13 @@ bool check_expression(Virtual_column_info *vcol, const char *name, uint filter= VCOL_IMPOSSIBLE; if (type != VCOL_GENERATED_VIRTUAL && type != VCOL_DEFAULT) filter|= VCOL_NOT_STRICTLY_DETERMINISTIC; + if (type == VCOL_GENERATED_VIRTUAL) + filter|= VCOL_NOT_VIRTUAL; - if (ret || (res.errors & filter)) + if (unlikely(ret || (res.errors & filter))) { my_error(ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), res.name, - vcol_type_name(type), name); + vcol_type_name(type), name->str); return TRUE; } /* @@ -10127,31 +10456,118 @@ bool check_expression(Virtual_column_info *vcol, const char *name, } +bool Column_definition::check_length(uint mysql_errno, uint limit) const +{ + if (length <= limit) + return false; + my_error(mysql_errno, MYF(0), field_name.str, static_cast<ulong>(limit)); + return true; +} + + +bool Column_definition::fix_attributes_int(uint default_length) +{ + if (length) + return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_FIELD_CHARLENGTH); + length= default_length; + return false; +} + + +bool Column_definition::fix_attributes_real(uint default_length) +{ + /* change FLOAT(precision) to FLOAT or DOUBLE */ + if (!length && !decimals) + { + length= default_length; + decimals= NOT_FIXED_DEC; + } + if (length < decimals && decimals != NOT_FIXED_DEC) + { + my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name.str); + return true; + } + if (decimals != NOT_FIXED_DEC && decimals >= FLOATING_POINT_DECIMALS) + { + my_error(ER_TOO_BIG_SCALE, MYF(0), static_cast<ulonglong>(decimals), + field_name.str, static_cast<uint>(FLOATING_POINT_DECIMALS-1)); + return true; + } + return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_FIELD_CHARLENGTH); +} + + +bool Column_definition::fix_attributes_decimal() +{ + if (decimals >= NOT_FIXED_DEC) + { + my_error(ER_TOO_BIG_SCALE, MYF(0), static_cast<ulonglong>(decimals), + field_name.str, static_cast<uint>(NOT_FIXED_DEC - 1)); + return true; + } + my_decimal_trim(&length, &decimals); + if (length > DECIMAL_MAX_PRECISION) + { + my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name.str, + DECIMAL_MAX_PRECISION); + return true; + } + if (length < decimals) + { + my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name.str); + return true; + } + length= my_decimal_precision_to_length((uint) length, decimals, + flags & UNSIGNED_FLAG); + pack_length= my_decimal_get_binary_size((uint) length, decimals); + return false; +} + + +bool Column_definition::fix_attributes_bit() +{ + if (!length) + length= 1; + pack_length= ((uint) length + 7) / 8; + return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_BIT_FIELD_LENGTH); +} + + +bool Column_definition::fix_attributes_temporal_with_time(uint int_part_length) +{ + if (length > MAX_DATETIME_PRECISION) + { + my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name.str, + MAX_DATETIME_PRECISION); + return true; + } + length+= int_part_length + (length ? 1 : 0); + return false; +} + + bool Column_definition::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; DBUG_ENTER("Column_definition::check"); /* Initialize data for a computed field */ if (vcol_info) { DBUG_ASSERT(vcol_info->expr); - vcol_info->set_field_type(sql_type); - if (check_expression(vcol_info, field_name, vcol_info->stored_in_db + vcol_info->set_field_type(real_field_type()); + if (check_expression(vcol_info, &field_name, vcol_info->stored_in_db ? VCOL_GENERATED_STORED : VCOL_GENERATED_VIRTUAL)) DBUG_RETURN(TRUE); } if (check_constraint && - check_expression(check_constraint, field_name, VCOL_CHECK_FIELD)) + check_expression(check_constraint, &field_name, VCOL_CHECK_FIELD)) DBUG_RETURN(1); if (default_value) { Item *def_expr= default_value->expr; - if (check_expression(default_value, field_name, VCOL_DEFAULT)) + if (check_expression(default_value, &field_name, VCOL_DEFAULT)) DBUG_RETURN(TRUE); /* Constant's are stored in the 'empty_record', except for blobs */ @@ -10162,7 +10578,7 @@ bool Column_definition::check(THD *thd) default_value= 0; if ((flags & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) == NOT_NULL_FLAG) { - my_error(ER_INVALID_DEFAULT, MYF(0), field_name); + my_error(ER_INVALID_DEFAULT, MYF(0), field_name.str); DBUG_RETURN(1); } } @@ -10171,12 +10587,12 @@ bool Column_definition::check(THD *thd) if (default_value && (flags & AUTO_INCREMENT_FLAG)) { - my_error(ER_INVALID_DEFAULT, MYF(0), field_name); + my_error(ER_INVALID_DEFAULT, MYF(0), field_name.str); DBUG_RETURN(1); } if (default_value && !default_value->expr->basic_const_item() && - mysql_type_to_time_type(sql_type) == MYSQL_TIMESTAMP_DATETIME && + mysql_timestamp_type() == MYSQL_TIMESTAMP_DATETIME && default_value->expr->type() == Item::FUNC_ITEM) { /* @@ -10194,10 +10610,10 @@ bool Column_definition::check(THD *thd) if (on_update) { - if (mysql_type_to_time_type(sql_type) != MYSQL_TIMESTAMP_DATETIME || + if (mysql_timestamp_type() != MYSQL_TIMESTAMP_DATETIME || on_update->decimals < length) { - my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name); + my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name.str); DBUG_RETURN(TRUE); } unireg_check= unireg_check == Field::NONE ? Field::TIMESTAMP_UN_FIELD @@ -10206,186 +10622,9 @@ bool Column_definition::check(THD *thd) else if (flags & AUTO_INCREMENT_FLAG) unireg_check= Field::NEXT_NUMBER; - sign_len= flags & UNSIGNED_FLAG ? 0 : 1; + if (type_handler()->Column_definition_fix_attributes(this)) + DBUG_RETURN(true); - switch (sql_type) { - case MYSQL_TYPE_TINY: - if (!length) - length= MAX_TINYINT_WIDTH+sign_len; - allowed_type_modifier= AUTO_INCREMENT_FLAG; - break; - case MYSQL_TYPE_SHORT: - if (!length) - length= MAX_SMALLINT_WIDTH+sign_len; - allowed_type_modifier= AUTO_INCREMENT_FLAG; - break; - case MYSQL_TYPE_INT24: - if (!length) - length= MAX_MEDIUMINT_WIDTH+sign_len; - allowed_type_modifier= AUTO_INCREMENT_FLAG; - break; - case MYSQL_TYPE_LONG: - if (!length) - length= MAX_INT_WIDTH+sign_len; - allowed_type_modifier= AUTO_INCREMENT_FLAG; - break; - case MYSQL_TYPE_LONGLONG: - if (!length) - length= MAX_BIGINT_WIDTH; - allowed_type_modifier= AUTO_INCREMENT_FLAG; - break; - case MYSQL_TYPE_NULL: - break; - case MYSQL_TYPE_NEWDECIMAL: - if (decimals >= NOT_FIXED_DEC) - { - my_error(ER_TOO_BIG_SCALE, MYF(0), static_cast<ulonglong>(decimals), - field_name, static_cast<uint>(NOT_FIXED_DEC - 1)); - DBUG_RETURN(TRUE); - } - my_decimal_trim(&length, &decimals); - if (length > 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), field_name); - DBUG_RETURN(TRUE); - } - length= - my_decimal_precision_to_length((uint)length, decimals, flags & UNSIGNED_FLAG); - pack_length= - my_decimal_get_binary_size((uint)length, decimals); - break; - case MYSQL_TYPE_VARCHAR: - /* - Long VARCHAR's are automaticly converted to blobs in mysql_prepare_table - if they don't have a default value - */ - max_field_charlength= MAX_FIELD_VARCHARLENGTH; - break; - case MYSQL_TYPE_STRING: - break; - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_LONG_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_GEOMETRY: - flags|= BLOB_FLAG; - break; - case MYSQL_TYPE_YEAR: - 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 (!length && !decimals) - { - length= MAX_FLOAT_STR_LENGTH; - decimals= NOT_FIXED_DEC; - } - if (length < decimals && - decimals != NOT_FIXED_DEC) - { - my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name); - DBUG_RETURN(TRUE); - } - if (decimals != NOT_FIXED_DEC && decimals >= FLOATING_POINT_DECIMALS) - { - my_error(ER_TOO_BIG_SCALE, MYF(0), static_cast<ulonglong>(decimals), - field_name, static_cast<uint>(FLOATING_POINT_DECIMALS-1)); - DBUG_RETURN(TRUE); - } - break; - case MYSQL_TYPE_DOUBLE: - allowed_type_modifier= AUTO_INCREMENT_FLAG; - if (!length && !decimals) - { - length= DBL_DIG+7; - decimals= NOT_FIXED_DEC; - } - if (length < decimals && - decimals != NOT_FIXED_DEC) - { - my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name); - DBUG_RETURN(TRUE); - } - if (decimals != NOT_FIXED_DEC && decimals >= FLOATING_POINT_DECIMALS) - { - my_error(ER_TOO_BIG_SCALE, MYF(0), static_cast<ulonglong>(decimals), - field_name, static_cast<uint>(FLOATING_POINT_DECIMALS-1)); - DBUG_RETURN(TRUE); - } - break; - case MYSQL_TYPE_TIMESTAMP: - case MYSQL_TYPE_TIMESTAMP2: - if (length > MAX_DATETIME_PRECISION) - { - my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name, - MAX_DATETIME_PRECISION); - DBUG_RETURN(TRUE); - } - length+= MAX_DATETIME_WIDTH + (length ? 1 : 0); - flags|= UNSIGNED_FLAG; - break; - case MYSQL_TYPE_DATE: - /* We don't support creation of MYSQL_TYPE_DATE anymore */ - sql_type= MYSQL_TYPE_NEWDATE; - /* fall through */ - case MYSQL_TYPE_NEWDATE: - length= MAX_DATE_WIDTH; - break; - case MYSQL_TYPE_TIME: - case MYSQL_TYPE_TIME2: - if (length > MAX_DATETIME_PRECISION) - { - my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name, - MAX_DATETIME_PRECISION); - DBUG_RETURN(TRUE); - } - length+= MIN_TIME_WIDTH + (length ? 1 : 0); - break; - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_DATETIME2: - if (length > MAX_DATETIME_PRECISION) - { - 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(interval_list.elements); - break; - case MYSQL_TYPE_ENUM: - /* 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 (!length) - length= 1; - if (length > MAX_BIT_FIELD_LENGTH) - { - my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), field_name, - static_cast<ulong>(MAX_BIT_FIELD_LENGTH)); - DBUG_RETURN(TRUE); - } - pack_length= ((uint)length + 7) / 8; - break; - } - case MYSQL_TYPE_DECIMAL: - DBUG_ASSERT(0); /* Was obsolete */ - } /* Remember the value of length */ char_length= (uint)length; @@ -10401,38 +10640,18 @@ bool Column_definition::check(THD *thd) 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)) + if ((opt_explicit_defaults_for_timestamp || + !is_timestamp_type()) && !vers_sys_field()) { flags|= NO_DEFAULT_VALUE_FLAG; } } - if (!(flags & BLOB_FLAG) && - ((length > max_field_charlength && - sql_type != MYSQL_TYPE_VARCHAR) || - (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), - field_name, max_field_charlength); /* purecov: inspected */ - DBUG_RETURN(TRUE); - } - else if (length > MAX_FIELD_BLOBLENGTH) - { - my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), field_name, MAX_FIELD_BLOBLENGTH); - DBUG_RETURN(1); - } - if ((~allowed_type_modifier) & flags & conditional_type_modifiers) + if ((flags & AUTO_INCREMENT_FLAG) && + !type_handler()->type_can_have_auto_increment_attribute()) { - my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name); + my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name.str); DBUG_RETURN(TRUE); } @@ -10454,64 +10673,6 @@ enum_field_types get_blob_type_from_length(ulong length) } -/* - Make a field from the .frm file info -*/ - -uint32 calc_pack_length(enum_field_types type,uint32 length) -{ - switch (type) { - case MYSQL_TYPE_VAR_STRING: - case MYSQL_TYPE_STRING: - case MYSQL_TYPE_DECIMAL: return (length); - case MYSQL_TYPE_VARCHAR: return (length + (length < 256 ? 1: 2)); - case MYSQL_TYPE_YEAR: - case MYSQL_TYPE_TINY : return 1; - case MYSQL_TYPE_SHORT : return 2; - case MYSQL_TYPE_INT24: - case MYSQL_TYPE_NEWDATE: return 3; - case MYSQL_TYPE_TIME: return length > MIN_TIME_WIDTH - ? time_hires_bytes[length - 1 - MIN_TIME_WIDTH] - : 3; - case MYSQL_TYPE_TIME2: - return length > MIN_TIME_WIDTH ? - my_time_binary_length(length - MIN_TIME_WIDTH - 1) : 3; - case MYSQL_TYPE_TIMESTAMP: - return length > MAX_DATETIME_WIDTH - ? 4 + sec_part_bytes[length - 1 - MAX_DATETIME_WIDTH] - : 4; - case MYSQL_TYPE_TIMESTAMP2: - return length > MAX_DATETIME_WIDTH ? - my_timestamp_binary_length(length - MAX_DATETIME_WIDTH - 1) : 4; - case MYSQL_TYPE_DATE: - case MYSQL_TYPE_LONG : return 4; - case MYSQL_TYPE_FLOAT : return sizeof(float); - case MYSQL_TYPE_DOUBLE: return sizeof(double); - case MYSQL_TYPE_DATETIME: - return length > MAX_DATETIME_WIDTH - ? datetime_hires_bytes[length - 1 - MAX_DATETIME_WIDTH] - : 8; - case MYSQL_TYPE_DATETIME2: - return length > MAX_DATETIME_WIDTH ? - my_datetime_binary_length(length - MAX_DATETIME_WIDTH - 1) : 5; - case MYSQL_TYPE_LONGLONG: return 8; /* Don't crash if no longlong */ - case MYSQL_TYPE_NULL : return 0; - case MYSQL_TYPE_TINY_BLOB: return 1+portable_sizeof_char_ptr; - case MYSQL_TYPE_BLOB: return 2+portable_sizeof_char_ptr; - case MYSQL_TYPE_MEDIUM_BLOB: return 3+portable_sizeof_char_ptr; - case MYSQL_TYPE_LONG_BLOB: return 4+portable_sizeof_char_ptr; - case MYSQL_TYPE_GEOMETRY: return 4+portable_sizeof_char_ptr; - case MYSQL_TYPE_SET: - case MYSQL_TYPE_ENUM: - case MYSQL_TYPE_NEWDECIMAL: - abort(); return 0; // This shouldn't happen - case MYSQL_TYPE_BIT: return length / 8; - default: - return 0; - } -} - - uint pack_length_to_packflag(uint type) { switch (type) { @@ -10530,17 +10691,33 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length, uchar *null_pos, uchar null_bit, uint pack_flag, - enum_field_types field_type, + const Type_handler *handler, CHARSET_INFO *field_charset, Field::geometry_type geom_type, uint srid, Field::utype unireg_check, TYPELIB *interval, - const char *field_name) + const LEX_CSTRING *field_name, + uint32 flags) { uchar *UNINIT_VAR(bit_ptr); uchar UNINIT_VAR(bit_offset); - if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag)) + DBUG_PRINT("debug", ("field_type: %s, field_length: %u, interval: %p, pack_flag: %s%s%s%s%s", + handler->name().ptr(), field_length, interval, + FLAGSTR(pack_flag, FIELDFLAG_BINARY), + FLAGSTR(pack_flag, FIELDFLAG_INTERVAL), + FLAGSTR(pack_flag, FIELDFLAG_NUMBER), + FLAGSTR(pack_flag, FIELDFLAG_PACK), + FLAGSTR(pack_flag, FIELDFLAG_BLOB))); + + if (handler == &type_handler_row) + { + DBUG_ASSERT(field_length == 0); + DBUG_ASSERT(f_maybe_null(pack_flag)); + return new (mem_root) Field_row(ptr, field_name); + } + + if (handler->real_field_type() == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag)) { bit_ptr= null_pos; bit_offset= null_bit; @@ -10561,18 +10738,12 @@ Field *make_field(TABLE_SHARE *share, null_bit= ((uchar) 1) << null_bit; } - DBUG_PRINT("debug", ("field_type: %d, field_length: %u, interval: %p, pack_flag: %s%s%s%s%s", - field_type, field_length, interval, - FLAGSTR(pack_flag, FIELDFLAG_BINARY), - FLAGSTR(pack_flag, FIELDFLAG_INTERVAL), - FLAGSTR(pack_flag, FIELDFLAG_NUMBER), - FLAGSTR(pack_flag, FIELDFLAG_PACK), - FLAGSTR(pack_flag, FIELDFLAG_BLOB))); if (f_is_alpha(pack_flag)) { if (!f_is_packed(pack_flag)) { + enum_field_types field_type= handler->real_field_type(); if (field_type == MYSQL_TYPE_STRING || field_type == MYSQL_TYPE_DECIMAL || // 3.23 or 4.0 string field_type == MYSQL_TYPE_VAR_STRING) @@ -10581,6 +10752,16 @@ Field *make_field(TABLE_SHARE *share, unireg_check, field_name, field_charset); if (field_type == MYSQL_TYPE_VARCHAR) + { + if (unireg_check == Field::TMYSQL_COMPRESSED) + return new (mem_root) + Field_varstring_compressed( + ptr, field_length, + HA_VARCHAR_PACKLENGTH(field_length), + null_pos, null_bit, + unireg_check, field_name, + share, field_charset, zlib_compression_method); + return new (mem_root) Field_varstring(ptr,field_length, HA_VARCHAR_PACKLENGTH(field_length), @@ -10588,12 +10769,16 @@ Field *make_field(TABLE_SHARE *share, unireg_check, field_name, share, field_charset); + } return 0; // Error } - uint pack_length=calc_pack_length((enum_field_types) - f_packtype(pack_flag), - field_length); + // MYSQL_TYPE_VAR_STRING is handled above + DBUG_ASSERT(f_packtype(pack_flag) != MYSQL_TYPE_VAR_STRING); + const Type_handler *tmp; + tmp= Type_handler::get_handler_by_real_type((enum_field_types) + f_packtype(pack_flag)); + uint pack_length= tmp->calc_pack_length(field_length); #ifdef HAVE_SPATIAL if (f_is_geom(pack_flag)) @@ -10606,10 +10791,18 @@ Field *make_field(TABLE_SHARE *share, } #endif if (f_is_blob(pack_flag)) + { + if (unireg_check == Field::TMYSQL_COMPRESSED) + return new (mem_root) + Field_blob_compressed(ptr, null_pos, null_bit, + unireg_check, field_name, share, + pack_length, field_charset, zlib_compression_method); + 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)) @@ -10625,7 +10818,7 @@ Field *make_field(TABLE_SHARE *share, } } - switch (field_type) { + switch (handler->real_field_type()) { case MYSQL_TYPE_DECIMAL: return new (mem_root) Field_decimal(ptr,field_length,null_pos,null_bit, @@ -10689,11 +10882,22 @@ Field *make_field(TABLE_SHARE *share, f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); case MYSQL_TYPE_LONGLONG: - 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); + if (flags & (VERS_SYS_START_FLAG|VERS_SYS_END_FLAG)) + { + return new (mem_root) + Field_vers_trx_id(ptr, field_length, null_pos, null_bit, + unireg_check, field_name, + f_is_zerofill(pack_flag) != 0, + f_is_dec(pack_flag) == 0); + } + else + { + 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 ? @@ -10770,42 +10974,63 @@ Field *make_field(TABLE_SHARE *share, return 0; } +bool Field_vers_trx_id::test_if_equality_guarantees_uniqueness(const Item* item) const +{ + return item->type() == Item::DATE_ITEM; +} + /** Create a field suitable for create of table. */ Column_definition::Column_definition(THD *thd, Field *old_field, Field *orig_field) { + on_update= NULL; field_name= old_field->field_name; length= old_field->field_length; flags= old_field->flags; unireg_check=old_field->unireg_check; pack_length=old_field->pack_length(); key_length= old_field->key_length(); - sql_type= old_field->real_type(); + set_handler(old_field->type_handler()); charset= old_field->charset(); // May be NULL ptr comment= old_field->comment; decimals= old_field->decimals(); vcol_info= old_field->vcol_info; - default_value= orig_field ? orig_field->default_value : 0; - check_constraint= orig_field ? orig_field->check_constraint : 0; option_list= old_field->option_list; + pack_flag= 0; + compression_method_ptr= 0; + versioning= VERSIONING_NOT_SET; + invisible= old_field->invisible; - switch (sql_type) { - case MYSQL_TYPE_BLOB: - switch (pack_length - portable_sizeof_char_ptr) { - case 1: sql_type= MYSQL_TYPE_TINY_BLOB; break; - case 2: sql_type= MYSQL_TYPE_BLOB; break; - case 3: sql_type= MYSQL_TYPE_MEDIUM_BLOB; break; - default: sql_type= MYSQL_TYPE_LONG_BLOB; break; + if (orig_field) + { + default_value= orig_field->default_value; + check_constraint= orig_field->check_constraint; + if (orig_field->unireg_check == Field::TMYSQL_COMPRESSED) + { + unireg_check= Field::TMYSQL_COMPRESSED; + compression_method_ptr= zlib_compression_method; } + } + else + { + default_value= 0; + check_constraint= 0; + } + + switch (real_field_type()) { + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: length/= charset->mbmaxlen; key_length/= charset->mbmaxlen; break; case MYSQL_TYPE_STRING: /* Change CHAR -> VARCHAR if dynamic record length */ if (old_field->type() == MYSQL_TYPE_VAR_STRING) - sql_type= MYSQL_TYPE_VARCHAR; + set_handler(&type_handler_varchar); /* fall through */ case MYSQL_TYPE_ENUM: @@ -10813,7 +11038,8 @@ Column_definition::Column_definition(THD *thd, Field *old_field, case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: /* This is corrected in create_length_to_internal_length */ - length= (length+charset->mbmaxlen-1) / charset->mbmaxlen; + length= (length+charset->mbmaxlen-1) / charset->mbmaxlen - + MY_TEST(old_field->compression_method()); break; #ifdef HAVE_SPATIAL case MYSQL_TYPE_GEOMETRY: @@ -10849,6 +11075,9 @@ Column_definition::Column_definition(THD *thd, Field *old_field, interval= ((Field_enum*) old_field)->typelib; else interval=0; + + interval_list.empty(); // prepare_interval_field() needs this + char_length= (uint)length; /* @@ -10888,6 +11117,35 @@ Column_definition::Column_definition(THD *thd, Field *old_field, /** + The common part for data type redefinition: + CREATE TABLE t1 (a INT) AS SELECT a FROM t2; + See Type_handler::Column_definition_redefine_stage1() + for data type specific code. +*/ +void +Column_definition::redefine_stage1_common(const Column_definition *dup_field, + const handler *file, + const Schema_specification_st *schema) +{ + set_handler(dup_field->type_handler()); + default_value= dup_field->default_value; + charset= dup_field->charset ? dup_field->charset : + schema->default_table_charset; + length= dup_field->char_length; + pack_length= dup_field->pack_length; + key_length= dup_field->key_length; + decimals= dup_field->decimals; + unireg_check= dup_field->unireg_check; + flags= dup_field->flags; + interval= dup_field->interval; + vcol_info= dup_field->vcol_info; + invisible= dup_field->invisible; + check_constraint= dup_field->check_constraint; +} + + + +/** maximum possible character length for blob. This method is used in Item_field::set_field to calculate @@ -10903,6 +11161,12 @@ Column_definition::Column_definition(THD *thd, Field *old_field, uint32 Field_blob::char_length() const { + return Field_blob::octet_length(); +} + + +uint32 Field_blob::octet_length() const +{ switch (packlength) { case 1: @@ -10912,7 +11176,7 @@ uint32 Field_blob::char_length() const case 3: return 16777215; case 4: - return (uint32) 4294967295U; + return (uint32) UINT_MAX32; default: DBUG_ASSERT(0); // we should never go here return 0; @@ -10947,6 +11211,57 @@ bool Column_definition::has_default_expression() (flags & BLOB_FLAG))); } + +bool Column_definition::set_compressed(const char *method) +{ + if (!method || !strcmp(method, zlib_compression_method->name)) + { + unireg_check= Field::TMYSQL_COMPRESSED; + compression_method_ptr= zlib_compression_method; + return false; + } + my_error(ER_UNKNOWN_COMPRESSION_METHOD, MYF(0), method); + return true; +} + + +bool Column_definition::set_compressed_deprecated(THD *thd, const char *method) +{ + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WARN_DEPRECATED_SYNTAX, + ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX), + "<data type> <character set clause> ... COMPRESSED...", + "'<data type> COMPRESSED... <character set clause> ...'"); + return set_compressed(method); +} + + +bool +Column_definition::set_compressed_deprecated_column_attribute(THD *thd, + const char *pos, + const char *method) +{ + if (compression_method_ptr) + { + /* + Compression method has already been set, e.g.: + a VARCHAR(10) COMPRESSED DEFAULT 10 COMPRESSED + */ + thd->parse_error(ER_SYNTAX_ERROR, pos); + return true; + } + enum enum_field_types sql_type= real_field_type(); + /* We can't use f_is_blob here as pack_flag is not yet set */ + if (sql_type == MYSQL_TYPE_VARCHAR || sql_type == MYSQL_TYPE_TINY_BLOB || + sql_type == MYSQL_TYPE_BLOB || sql_type == MYSQL_TYPE_MEDIUM_BLOB || + sql_type == MYSQL_TYPE_LONG_BLOB) + return set_compressed_deprecated(thd, method); + else + my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name.str); + return true; +} + + /** maximum possible display length for blob. @@ -10954,7 +11269,7 @@ bool Column_definition::has_default_expression() length */ -uint32 Field_blob::max_display_length() +uint32 Field_blob::max_display_length() const { switch (packlength) { @@ -10965,7 +11280,7 @@ uint32 Field_blob::max_display_length() case 3: return 16777215 * field_charset->mbmaxlen; case 4: - return (uint32) 4294967295U; + return (uint32) UINT_MAX32; default: DBUG_ASSERT(0); // we should never go here return 0; @@ -10983,13 +11298,15 @@ uint32 Field_blob::max_display_length() @param level - level of message (Note/Warning/Error) @param code - error code of message to be produced @param cut_increment - whenever we should increase cut fields count + @current_row - current row number @note - This function won't produce warning and increase cut fields counter - if count_cuted_fields == CHECK_FIELD_IGNORE for current thread. + This function won't produce warning or notes or increase cut fields counter + if count_cuted_fields == CHECK_FIELD_IGNORE or CHECK_FIELD_EXPRESSION + for the current thread. - if count_cuted_fields == CHECK_FIELD_IGNORE then we ignore notes. - This allows us to avoid notes in optimisation, like convert_constant_item(). + This allows us to avoid notes in optimisation, like + convert_constant_item(). @retval 1 if count_cuted_fields == CHECK_FIELD_IGNORE and error level is not NOTE @@ -10999,18 +11316,19 @@ uint32 Field_blob::max_display_length() bool Field::set_warning(Sql_condition::enum_warning_level level, uint code, - int cut_increment) const + int cut_increment, ulong current_row) const { /* If this field was created only for type conversion purposes it will have table == NULL. */ THD *thd= get_thd(); - if (thd->count_cuted_fields) + if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION) { thd->cuted_fields+= cut_increment; - push_warning_printf(thd, level, code, ER_THD(thd, code), field_name, - thd->get_stmt_da()->current_row_for_warning()); + push_warning_printf(thd, level, code, ER_THD(thd, code), field_name.str, + current_row ? current_row + : thd->get_stmt_da()->current_row_for_warning()); return 0; } return level >= Sql_condition::WARN_LEVEL_WARN; @@ -11043,7 +11361,7 @@ void Field::set_datetime_warning(Sql_condition::enum_warning_level level, THD *thd= get_thd(); if (thd->really_abort_on_warning() && level >= Sql_condition::WARN_LEVEL_WARN) make_truncated_value_warning(thd, level, str, ts_type, - table->s, field_name); + table->s, field_name.str); else set_warning(level, code, cuted_increment); } @@ -11068,8 +11386,7 @@ void Field::set_warning_truncated_wrong_value(const char *type_arg, push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, ER_THD(thd, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), - type_arg, value, - db_name, table_name, field_name, + type_arg, value, db_name, table_name, field_name.str, static_cast<ulong>(thd->get_stmt_da()-> current_row_for_warning())); } @@ -11111,7 +11428,7 @@ bool Field::validate_value_in_record_with_warn(THD *thd, const uchar *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); + ErrConvString(&tmp).ptr(), field_name.str); } dbug_tmp_restore_column_map(table->read_set, old_map); return rc; @@ -11122,8 +11439,8 @@ bool Field::save_in_field_default_value(bool view_error_processing) { THD *thd= table->in_use; - if (flags & NO_DEFAULT_VALUE_FLAG && - real_type() != MYSQL_TYPE_ENUM) + if (unlikely(flags & NO_DEFAULT_VALUE_FLAG && + real_type() != MYSQL_TYPE_ENUM)) { if (reset()) { @@ -11146,7 +11463,7 @@ bool Field::save_in_field_default_value(bool view_error_processing) push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_NO_DEFAULT_FOR_FIELD, ER_THD(thd, ER_NO_DEFAULT_FOR_FIELD), - field_name); + field_name.str); } return true; } @@ -11179,3 +11496,22 @@ void Field::register_field_in_read_map() } bitmap_set_bit(table->read_set, field_index); } + + +bool Field::val_str_nopad(MEM_ROOT *mem_root, LEX_CSTRING *to) +{ + StringBuffer<MAX_FIELD_WIDTH> str; + bool rc= false; + THD *thd= get_thd(); + sql_mode_t sql_mode_backup= thd->variables.sql_mode; + thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH; + + val_str(&str); + if (!(to->length= str.length())) + *to= empty_clex_str; + else if ((rc= !(to->str= strmake_root(mem_root, str.ptr(), str.length())))) + to->length= 0; + + thd->variables.sql_mode= sql_mode_backup; + return rc; +} |