diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/field.cc | 185 | ||||
-rw-r--r-- | sql/field.h | 4 | ||||
-rw-r--r-- | sql/item.cc | 66 | ||||
-rw-r--r-- | sql/item.h | 21 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 37 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 9 | ||||
-rw-r--r-- | sql/item_create.cc | 225 | ||||
-rw-r--r-- | sql/item_create.h | 10 | ||||
-rw-r--r-- | sql/item_func.cc | 90 | ||||
-rw-r--r-- | sql/item_func.h | 22 | ||||
-rw-r--r-- | sql/item_strfunc.cc | 764 | ||||
-rw-r--r-- | sql/item_strfunc.h | 68 | ||||
-rw-r--r-- | sql/item_timefunc.cc | 43 | ||||
-rw-r--r-- | sql/item_timefunc.h | 1 | ||||
-rw-r--r-- | sql/lex.h | 6 | ||||
-rw-r--r-- | sql/my_decimal.cc | 47 | ||||
-rw-r--r-- | sql/my_decimal.h | 63 | ||||
-rw-r--r-- | sql/mysql_priv.h | 13 | ||||
-rw-r--r-- | sql/share/errmsg.txt | 17 | ||||
-rw-r--r-- | sql/sql_acl.cc | 3 | ||||
-rw-r--r-- | sql/sql_base.cc | 34 | ||||
-rw-r--r-- | sql/sql_string.h | 12 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 198 | ||||
-rw-r--r-- | sql/time.cc | 40 |
24 files changed, 1748 insertions, 230 deletions
diff --git a/sql/field.cc b/sql/field.cc index 461d6c1eda2..2f7867eedc0 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1578,17 +1578,19 @@ longlong Field::convert_decimal2longlong(const my_decimal *val, i= 0; *err= 1; } - else if (warn_if_overflow(my_decimal2int(E_DEC_ERROR & - ~E_DEC_OVERFLOW & ~E_DEC_TRUNCATED, - val, TRUE, &i))) + else if (warn_if_overflow(my_decimal2int((E_DEC_ERROR & + ~E_DEC_OVERFLOW & + ~E_DEC_TRUNCATED), + val, TRUE, &i))) { i= ~(longlong) 0; *err= 1; } } - else if (warn_if_overflow(my_decimal2int(E_DEC_ERROR & - ~E_DEC_OVERFLOW & ~E_DEC_TRUNCATED, - val, FALSE, &i))) + else if (warn_if_overflow(my_decimal2int((E_DEC_ERROR & + ~E_DEC_OVERFLOW & + ~E_DEC_TRUNCATED), + val, FALSE, &i))) { i= (val->sign() ? LONGLONG_MIN : LONGLONG_MAX); *err= 1; @@ -1753,7 +1755,10 @@ bool Field::get_time(MYSQL_TIME *ltime) char buff[40]; String tmp(buff,sizeof(buff),&my_charset_bin),*res; if (!(res=val_str(&tmp)) || - str_to_time_with_warn(res->ptr(), res->length(), ltime)) + str_to_time_with_warn(res->ptr(), res->length(), ltime, + table->in_use->variables.sql_mode & + (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | + MODE_INVALID_DATES))) return 1; return 0; } @@ -3866,40 +3871,11 @@ int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs) int Field_longlong::store(double nr) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; - int error= 0; + bool error; longlong res; - nr= rint(nr); - if (unsigned_flag) - { - if (nr < 0) - { - res=0; - error= 1; - } - else if (nr >= (double) ULONGLONG_MAX) - { - res= ~(longlong) 0; - error= 1; - } - else - res=(longlong) double2ulonglong(nr); - } - else - { - if (nr <= (double) LONGLONG_MIN) - { - res= LONGLONG_MIN; - error= (nr < (double) LONGLONG_MIN); - } - else if (nr >= (double) (ulonglong) LONGLONG_MAX) - { - res= LONGLONG_MAX; - error= (nr > (double) LONGLONG_MAX); - } - else - res=(longlong) nr; - } + res= double_to_longlong(nr, unsigned_flag, &error); + if (error) set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); @@ -4144,7 +4120,18 @@ int Field_float::store(const char *from,uint len,CHARSET_INFO *cs) int Field_float::store(double nr) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; - int error= truncate(&nr, FLT_MAX); + int error= truncate_double(&nr, field_length, + not_fixed ? NOT_FIXED_DEC : dec, + unsigned_flag, FLT_MAX); + if (error) + { + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + if (error < 0) // Wrong double value + { + error= 1; + set_null(); + } + } float j= (float)nr; #ifdef WORDS_BIGENDIAN @@ -4406,7 +4393,18 @@ int Field_double::store(const char *from,uint len,CHARSET_INFO *cs) int Field_double::store(double nr) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; - int error= truncate(&nr, DBL_MAX); + int error= truncate_double(&nr, field_length, + not_fixed ? NOT_FIXED_DEC : dec, + unsigned_flag, DBL_MAX); + if (error) + { + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + if (error < 0) // Wrong double value + { + error= 1; + set_null(); + } + } #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) @@ -4430,28 +4428,31 @@ int Field_double::store(longlong nr, bool unsigned_val) If a field has fixed length, truncate the double argument pointed to by 'nr' appropriately. Also ensure that the argument is within [-max_value; max_value] range. + + return + 0 ok + -1 Illegal double value + 1 Value was truncated */ -int Field_real::truncate(double *nr, double max_value) +int truncate_double(double *nr, uint field_length, uint dec, + bool unsigned_flag, double max_value) { - int error= 1; + int error= 0; double res= *nr; if (isnan(res)) { - res= 0; - set_null(); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); - goto end; + *nr= 0; + return -1; } else if (unsigned_flag && res < 0) { - res= 0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); - goto end; + *nr= 0; + return 1; } - if (!not_fixed) + if (dec < NOT_FIXED_DEC) { uint order= field_length - dec; uint step= array_elements(log_10) - 1; @@ -4467,22 +4468,70 @@ int Field_real::truncate(double *nr, double max_value) if (res < -max_value) { - res= -max_value; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + res= -max_value; + error= 1; } else if (res > max_value) { res= max_value; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + error= 1; } - else - error= 0; -end: *nr= res; return error; } +/* + Convert double to longlong / ulonglong. + If double is outside of range, adjust return value and set error. + + SYNOPSIS + double_to_longlong() + nr Number to convert + unsigned_flag 1 if result is unsigned + error Will be set to 1 in case of overflow. +*/ + +longlong double_to_longlong(double nr, bool unsigned_flag, bool *error) +{ + longlong res; + + *error= 0; + + nr= rint(nr); + if (unsigned_flag) + { + if (nr < 0) + { + res= 0; + *error= 1; + } + else if (nr >= (double) ULONGLONG_MAX) + { + res= ~(longlong) 0; + *error= 1; + } + else + res= (longlong) double2ulonglong(nr); + } + else + { + if (nr <= (double) LONGLONG_MIN) + { + res= LONGLONG_MIN; + *error= (nr < (double) LONGLONG_MIN); + } + else if (nr >= (double) (ulonglong) LONGLONG_MAX) + { + res= LONGLONG_MAX; + *error= (nr > (double) LONGLONG_MAX); + } + else + res= (longlong) nr; + } + return res; +} + int Field_real::store_decimal(const my_decimal *dm) { @@ -4511,6 +4560,7 @@ longlong Field_double::val_int(void) ASSERT_COLUMN_MARKED_FOR_READ; double j; longlong res; + bool error; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) { @@ -4519,20 +4569,9 @@ longlong Field_double::val_int(void) else #endif doubleget(j,ptr); - /* Check whether we fit into longlong range */ - if (j <= (double) LONGLONG_MIN) - { - res= (longlong) LONGLONG_MIN; - goto warn; - } - if (j >= (double) (ulonglong) LONGLONG_MAX) - { - res= (longlong) LONGLONG_MAX; - goto warn; - } - return (longlong) rint(j); -warn: + res= double_to_longlong(j, 0, &error); + if (error) { char buf[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE]; String tmp(buf, sizeof(buf), &my_charset_latin1), *str; @@ -5147,7 +5186,10 @@ int Field_time::store(const char *from,uint len,CHARSET_INFO *cs) int error= 0; int warning; - if (str_to_time(from, len, <ime, &warning)) + if (str_to_time(from, len, <ime, + table->in_use->variables.sql_mode & + (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | + MODE_INVALID_DATES), &warning)) { tmp=0L; error= 2; @@ -5305,6 +5347,7 @@ String *Field_time::val_str(String *val_buffer, ltime.hour= (uint) (tmp/10000); ltime.minute= (uint) (tmp/100 % 100); ltime.second= (uint) (tmp % 100); + ltime.second_part= 0; make_time((DATE_TIME_FORMAT*) 0, <ime, val_buffer); return val_buffer; } diff --git a/sql/field.h b/sql/field.h index d695479f197..3c174680cb6 100644 --- a/sql/field.h +++ b/sql/field.h @@ -33,6 +33,9 @@ class Relay_log_info; struct st_cache_field; int field_conv(Field *to,Field *from); +int truncate_double(double *nr, uint field_length, uint dec, + bool unsigned_flag, double max_value); +longlong double_to_longlong(double nr, bool unsigned_flag, bool *error); inline uint get_enum_pack_length(int elements) { @@ -808,7 +811,6 @@ public: {} int store_decimal(const my_decimal *); my_decimal *val_decimal(my_decimal *); - int truncate(double *nr, double max_length); uint32 max_display_length() { return field_length; } uint size_of() const { return sizeof(*this); } virtual const uchar *unpack(uchar* to, const uchar *from, diff --git a/sql/item.cc b/sql/item.cc index b0530f0e17e..cf44e25ccf3 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -985,19 +985,13 @@ bool Item_string::eq(const Item *item, bool binary_cmp) const bool Item::get_date(MYSQL_TIME *ltime,uint fuzzydate) { - if (result_type() == STRING_RESULT) - { - char buff[40]; - String tmp(buff,sizeof(buff), &my_charset_bin),*res; - if (!(res=val_str(&tmp)) || - str_to_datetime_with_warn(res->ptr(), res->length(), - ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR) - goto err; - } - else + switch (result_type()) { + case INT_RESULT: { - longlong value= val_int(); int was_cut; + longlong value= val_int(); + if (null_value) + goto err; if (number_to_datetime(value, ltime, fuzzydate, &was_cut) == LL(-1)) { char buff[22], *end; @@ -1005,8 +999,52 @@ bool Item::get_date(MYSQL_TIME *ltime,uint fuzzydate) make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, buff, (int) (end-buff), MYSQL_TIMESTAMP_NONE, NullS); + null_value= 1; + goto err; + } + break; + } + case REAL_RESULT: + { + double value= val_real(); + if (null_value) + goto err; + if (double_to_datetime_with_warn(value, ltime, fuzzydate)) + { + null_value= 1; goto err; } + break; + } + case DECIMAL_RESULT: + { + my_decimal value, *res; + if (!(res= val_decimal(&value))) + goto err; // Null + if (decimal_to_datetime_with_warn(res, ltime, fuzzydate)) + { + null_value= 1; + goto err; + } + break; + } + default: + { + /* + Default go trough string as this is the safest way to ensure that + we also get the microseconds. + */ + char buff[40]; + String tmp(buff,sizeof(buff), &my_charset_bin),*res; + if (!(res=val_str(&tmp)) || + str_to_datetime_with_warn(res->ptr(), res->length(), + ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR) + { + null_value= 1; + goto err; + } + break; + } } return 0; @@ -1026,7 +1064,11 @@ bool Item::get_time(MYSQL_TIME *ltime) char buff[40]; String tmp(buff,sizeof(buff),&my_charset_bin),*res; if (!(res=val_str(&tmp)) || - str_to_time_with_warn(res->ptr(), res->length(), ltime)) + str_to_time_with_warn(res->ptr(), res->length(), ltime, + TIME_FUZZY_DATE | + (current_thd->variables.sql_mode & + (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | + MODE_INVALID_DATES)))) { bzero((char*) ltime,sizeof(*ltime)); return 1; diff --git a/sql/item.h b/sql/item.h index 120ff358098..991e5d47d67 100644 --- a/sql/item.h +++ b/sql/item.h @@ -18,6 +18,10 @@ #pragma interface /* gcc class implementation */ #endif +C_MODE_START +#include <ma_dyncol.h> +C_MODE_END + inline bool trace_unsupported_func(const char *where, const char *processor_name) { @@ -471,6 +475,17 @@ public: }; +struct st_dyncall_create_def +{ + Item *num, *value; + CHARSET_INFO *cs; + uint len, frac; + DYNAMIC_COLUMN_TYPE type; +}; + +typedef struct st_dyncall_create_def DYNCALL_CREATE_DEF; + + typedef bool (Item::*Item_processor) (uchar *arg); /* Analyzer function @@ -804,7 +819,11 @@ public: { return val_decimal(val); } virtual bool val_bool_result() { return val_bool(); } virtual bool is_null_result() { return is_null(); } - + /* + Returns 1 if result type and collation for val_str() can change between + calls + */ + virtual bool dynamic_result() { return 0; } /* Bitmap of tables used by item (note: if you need to check dependencies on individual columns, check out diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index cc6f2f41982..6cc74570426 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -6041,7 +6041,7 @@ Item* Item_equal::get_first(Item *field_item) } else { -#if 0 +#if TO_BE_DELETED /* The field is not in SJ-Materialization nest. We must return the first field that's not embedded in a SJ-Materialization nest. @@ -6050,8 +6050,8 @@ Item* Item_equal::get_first(Item *field_item) SJ-Mat(it1 it2) ot1 ot2 and equality ot2.col = ot1.col = it2.col - If we're looking for best substitute for 'ot2.col', we should pick ot1.col - and not it2.col, because when we run a join between ot1 and ot2 + If we're looking for best substitute for 'ot2.col', we should pick + ot1.col and not it2.col, because when we run a join between ot1 and ot2 execution of SJ-Mat(...) has already finished and we can't rely on the value of it*.*. psergey-fix-fix: ^^ THAT IS INCORRECT ^^. Pick the first, whatever that @@ -6074,3 +6074,34 @@ Item* Item_equal::get_first(Item *field_item) DBUG_ASSERT(0); return NULL; } + + +longlong Item_func_dyncol_exists::val_int() +{ + char buff[STRING_BUFFER_USUAL_SIZE]; + String tmp(buff, sizeof(buff), &my_charset_bin); + DYNAMIC_COLUMN col; + String *str; + ulonglong num; + enum enum_dyncol_func_result rc; + + num= args[1]->val_int(); + str= args[0]->val_str(&tmp); + if (args[0]->null_value || args[1]->null_value || num > UINT_MAX16) + goto null; + col.length= str->length(); + /* We do not change the string, so could do this trick */ + col.str= (char *)str->ptr(); + rc= dynamic_column_exists(&col, (uint) num); + if (rc < 0) + { + dynamic_column_error_message(rc); + goto null; + } + null_value= FALSE; + return rc == ER_DYNCOL_YES; + +null: + null_value= TRUE; + return 0; +} diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 9f4efbc08be..61cc0af1c3f 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1,4 +1,5 @@ /* Copyright (c) 2000, 2010, Oracle and/or its affiliates. + Copyright (c) 2009-2011, Monty Program Ab 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 @@ -1869,3 +1870,11 @@ Item *and_expressions(Item *a, Item *b, Item **org_item); bool get_mysql_time_from_str(THD *thd, String *str, timestamp_type warn_type, const char *warn_name, MYSQL_TIME *l_time); + +class Item_func_dyncol_exists :public Item_bool_func +{ +public: + Item_func_dyncol_exists(Item *str, Item *num) :Item_bool_func(str, num) {} + longlong val_int(); + const char *func_name() const { return "column_exists"; } +}; diff --git a/sql/item_create.cc b/sql/item_create.cc index b3a0e7cf3b2..9c66c94382d 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -27,6 +27,50 @@ /* ============================================================================= + HELPER FUNCTIONS +============================================================================= +*/ + +/* + Get precision and scale for a declaration + + return + 0 ok + 1 error +*/ + +bool get_length_and_scale(ulonglong length, ulonglong decimals, + ulong *out_length, uint *out_decimals, + uint max_precision, uint max_scale, + const char *name) +{ + if (length > (ulonglong) max_precision) + { + my_error(ER_TOO_BIG_PRECISION, MYF(0), (int) length, name, + max_precision); + return 1; + } + if (decimals > (ulonglong) max_scale) + { + my_error(ER_TOO_BIG_SCALE, MYF(0), (int) decimals, name, + DECIMAL_MAX_SCALE); + return 1; + } + + *out_length= (ulong) length; + *out_decimals= (uint) decimals; + my_decimal_trim(out_length, out_decimals); + + if (*out_length < *out_decimals) + { + my_error(ER_M_BIGGER_THAN_D, MYF(0), ""); + return 1; + } + return 0; +} + +/* +============================================================================= LOCAL DECLARATIONS ============================================================================= */ @@ -5054,6 +5098,17 @@ create_func_cast(THD *thd, Item *a, Cast_target cast_type, CHARSET_INFO *cs) { Item *UNINIT_VAR(res); + ulonglong length= 0, decimals= 0; + int error; + + /* + We don't have to check for error here as sql_yacc.yy has guaranteed + that the values are in range of ulonglong + */ + if (c_len) + length= (ulonglong) my_strtoll10(c_len, NULL, &error); + if (c_dec) + decimals= (ulonglong) my_strtoll10(c_dec, NULL, &error); switch (cast_type) { case ITEM_CAST_BINARY: @@ -5076,55 +5131,31 @@ create_func_cast(THD *thd, Item *a, Cast_target cast_type, break; case ITEM_CAST_DECIMAL: { - ulong len= 0; - uint dec= 0; - - if (c_len) - { - ulong decoded_size; - errno= 0; - decoded_size= strtoul(c_len, NULL, 10); - if (errno != 0) - { - my_error(ER_TOO_BIG_PRECISION, MYF(0), c_len, a->name, - DECIMAL_MAX_PRECISION); - return NULL; - } - len= decoded_size; - } + ulong len; + uint dec; + if (get_length_and_scale(length, decimals, &len, &dec, + DECIMAL_MAX_PRECISION, DECIMAL_MAX_SCALE, + a->name)) + return NULL; + res= new (thd->mem_root) Item_decimal_typecast(a, len, dec); + break; + } + case ITEM_CAST_DOUBLE: + { + ulong len; + uint dec; - if (c_dec) + if (!c_len) { - ulong decoded_size; - errno= 0; - decoded_size= strtoul(c_dec, NULL, 10); - if ((errno != 0) || (decoded_size > UINT_MAX)) - { - my_error(ER_TOO_BIG_SCALE, MYF(0), c_dec, a->name, - DECIMAL_MAX_SCALE); - return NULL; - } - dec= decoded_size; - } - my_decimal_trim(&len, &dec); - if (len < dec) - { - my_error(ER_M_BIGGER_THAN_D, MYF(0), ""); - return 0; + length= DBL_DIG+7; + decimals= NOT_FIXED_DEC; } - if (len > DECIMAL_MAX_PRECISION) - { - my_error(ER_TOO_BIG_PRECISION, MYF(0), len, a->name, - DECIMAL_MAX_PRECISION); - return 0; - } - if (dec > DECIMAL_MAX_SCALE) - { - my_error(ER_TOO_BIG_SCALE, MYF(0), dec, a->name, - DECIMAL_MAX_SCALE); - return 0; - } - res= new (thd->mem_root) Item_decimal_typecast(a, len, dec); + else if (get_length_and_scale(length, decimals, &len, &dec, + DECIMAL_MAX_PRECISION, NOT_FIXED_DEC-1, + a->name)) + return NULL; + res= new (thd->mem_root) Item_double_typecast(a, (uint) length, + (uint) decimals); break; } case ITEM_CAST_CHAR: @@ -5133,15 +5164,13 @@ create_func_cast(THD *thd, Item *a, Cast_target cast_type, CHARSET_INFO *real_cs= (cs ? cs : thd->variables.collation_connection); if (c_len) { - ulong decoded_size; - errno= 0; - decoded_size= strtoul(c_len, NULL, 10); - if ((errno != 0) || (decoded_size > MAX_FIELD_BLOBLENGTH)) + if (length > MAX_FIELD_BLOBLENGTH) { - my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), "cast as char", MAX_FIELD_BLOBLENGTH); + my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), "cast as char", + MAX_FIELD_BLOBLENGTH); return NULL; } - len= (int) decoded_size; + len= (int) length; } res= new (thd->mem_root) Item_char_typecast(a, len, real_cs); break; @@ -5155,3 +5184,95 @@ create_func_cast(THD *thd, Item *a, Cast_target cast_type, } return res; } + + +static List<Item> *create_func_dyncol_prepare(THD *thd, + DYNCALL_CREATE_DEF **dfs, + List<DYNCALL_CREATE_DEF> &list) +{ + DYNCALL_CREATE_DEF *def; + List_iterator_fast<DYNCALL_CREATE_DEF> li(list); + List<Item> *args= new (thd->mem_root) List<Item>; + + *dfs= (DYNCALL_CREATE_DEF *)alloc_root(thd->mem_root, + sizeof(DYNCALL_CREATE_DEF) * + list.elements); + + if (!args || !*dfs) + return NULL; + + for (uint i= 0; (def= li++) ;) + { + dfs[0][i++]= *def; + args->push_back(def->num); + args->push_back(def->value); + } + return args; +} + +Item *create_func_dyncol_create(THD *thd, List<DYNCALL_CREATE_DEF> &list) +{ + List<Item> *args; + DYNCALL_CREATE_DEF *dfs; + if (!(args= create_func_dyncol_prepare(thd, &dfs, list))) + return NULL; + + return new (thd->mem_root) Item_func_dyncol_create(*args, dfs); +} + + +Item *create_func_dyncol_add(THD *thd, Item *str, + List<DYNCALL_CREATE_DEF> &list) +{ + List<Item> *args; + DYNCALL_CREATE_DEF *dfs; + + if (!(args= create_func_dyncol_prepare(thd, &dfs, list))) + return NULL; + + args->push_back(str); + + return new (thd->mem_root) Item_func_dyncol_add(*args, dfs); +} + + + +Item *create_func_dyncol_delete(THD *thd, Item *str, List<Item> &nums) +{ + DYNCALL_CREATE_DEF *dfs; + Item *num; + List_iterator_fast<Item> it(nums); + List<Item> *args= new (thd->mem_root) List<Item>; + + dfs= (DYNCALL_CREATE_DEF *)alloc_root(thd->mem_root, + sizeof(DYNCALL_CREATE_DEF) * + nums.elements); + if (!args || !dfs) + return NULL; + + for (uint i= 0; (num= it++); i++) + { + dfs[i].num= num; + dfs[i].value= new Item_null(); + dfs[i].type= DYN_COL_INT; + args->push_back(dfs[i].num); + args->push_back(dfs[i].value); + } + + args->push_back(str); + + return new (thd->mem_root) Item_func_dyncol_add(*args, dfs); +} + + +Item *create_func_dyncol_get(THD *thd, Item *str, Item *num, + Cast_target cast_type, + const char *c_len, const char *c_dec, + CHARSET_INFO *cs) +{ + Item *res; + + if (!(res= new (thd->mem_root) Item_dyncol_get(str, num))) + return res; // Return NULL + return create_func_cast(thd, res, cast_type, c_len, c_dec, cs); +} diff --git a/sql/item_create.h b/sql/item_create.h index e9824a71132..c075302dd1d 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -164,5 +164,15 @@ Item * create_func_cast(THD *thd, Item *a, Cast_target cast_type, const char *len, const char *dec, CHARSET_INFO *cs); + +Item *create_func_dyncol_create(THD *thd, List<DYNCALL_CREATE_DEF> &list); +Item *create_func_dyncol_add(THD *thd, Item *str, + List<DYNCALL_CREATE_DEF> &list); +Item *create_func_dyncol_delete(THD *thd, Item *str, List<Item> &nums); +Item *create_func_dyncol_get(THD *thd, Item *num, Item *str, + Cast_target cast_type, + const char *c_len, const char *c_dec, + CHARSET_INFO *cs); + #endif diff --git a/sql/item_func.cc b/sql/item_func.cc index 63b8419aaaa..715194b5d92 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -989,14 +989,26 @@ longlong Item_func_signed::val_int() null_value= args[0]->null_value; return value; } + else if (args[0]->dynamic_result()) + { + /* We come here when argument has an unknown type */ + args[0]->unsigned_flag= 0; // Mark that we want to have a signed value + value= args[0]->val_int(); + null_value= args[0]->null_value; + if (!null_value && args[0]->unsigned_flag && value < 0) + goto err; // Warn about overflow + return value; + } value= val_int_from_str(&error); if (value < 0 && error == 0) - { - push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, - "Cast to signed converted positive out-of-range integer to " - "it's negative complement"); - } + goto err; + return value; + +err: + push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, + "Cast to signed converted positive out-of-range integer to " + "it's negative complement"); return value; } @@ -1024,19 +1036,36 @@ longlong Item_func_unsigned::val_int() value= 0; return value; } + else if (args[0]->dynamic_result()) + { + /* We come here when argument has an unknown type */ + args[0]->unsigned_flag= 1; // Mark that we want to have an unsigned value + value= args[0]->val_int(); + null_value= args[0]->null_value; + if (!null_value && args[0]->unsigned_flag == 0 && value < 0) + goto err; // Warn about overflow + return value; + } else if (args[0]->cast_to_int_type() != STRING_RESULT || args[0]->result_as_longlong()) { value= args[0]->val_int(); null_value= args[0]->null_value; + if (!null_value && args[0]->unsigned_flag == 0 && value < 0) + goto err; // Warn about overflow return value; } value= val_int_from_str(&error); if (error < 0) - push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, - "Cast to unsigned converted negative integer to it's " - "positive complement"); + goto err; + + return value; + +err: + push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, + "Cast to unsigned converted negative integer to it's " + "positive complement"); return value; } @@ -1134,6 +1163,51 @@ void Item_decimal_typecast::print(String *str, enum_query_type query_type) } +double Item_double_typecast::val_real() +{ + int error; + double tmp= args[0]->val_real(); + if ((null_value= args[0]->null_value)) + return 0.0; + + if ((error= truncate_double(&tmp, max_length, decimals, 0, DBL_MAX))) + { + push_warning_printf(current_thd, + MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_OUT_OF_RANGE, + ER(ER_WARN_DATA_OUT_OF_RANGE), + name, 1); + if (error < 0) + { + null_value= 1; // Illegal value + tmp= 0.0; + } + } + return tmp; +} + + +void Item_double_typecast::print(String *str, enum_query_type query_type) +{ + char len_buf[20*3 + 1]; + char *end; + + str->append(STRING_WITH_LEN("cast(")); + args[0]->print(str, query_type); + str->append(STRING_WITH_LEN(" as double")); + if (decimals != NOT_FIXED_DEC) + { + str->append('('); + end= int10_to_str(max_length, len_buf,10); + str->append(len_buf, (uint32) (end - len_buf)); + str->append(','); + end= int10_to_str(decimals, len_buf,10); + str->append(len_buf, (uint32) (end - len_buf)); + str->append(')'); + } + str->append(')'); +} + double Item_func_plus::real_op() { double value= args[0]->val_real() + args[1]->val_real(); diff --git a/sql/item_func.h b/sql/item_func.h index 5c5ea33f247..0ba73f6e960 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -470,12 +470,29 @@ public: my_decimal *val_decimal(my_decimal*); enum Item_result result_type () const { return DECIMAL_RESULT; } enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; } - void fix_length_and_dec() {}; + void fix_length_and_dec() {} const char *func_name() const { return "decimal_typecast"; } virtual void print(String *str, enum_query_type query_type); }; +class Item_double_typecast :public Item_real_func +{ +public: + Item_double_typecast(Item *a, int len, int dec) :Item_real_func(a) + { + decimals= dec; + max_length= len; + } + double val_real(); + enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } + void fix_length_and_dec() { maybe_null= 1; } + const char *func_name() const { return "double_typecast"; } + virtual void print(String *str, enum_query_type query_type); +}; + + + class Item_func_additive_op :public Item_num_op { public: @@ -1713,7 +1730,7 @@ enum Cast_target { ITEM_CAST_BINARY, ITEM_CAST_SIGNED_INT, ITEM_CAST_UNSIGNED_INT, ITEM_CAST_DATE, ITEM_CAST_TIME, ITEM_CAST_DATETIME, ITEM_CAST_CHAR, - ITEM_CAST_DECIMAL + ITEM_CAST_DECIMAL, ITEM_CAST_DOUBLE }; @@ -1877,4 +1894,3 @@ public: return trace_unsupported_by_check_vcol_func_processor(func_name()); } }; - diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 39776643ddd..1471fcab001 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -3451,3 +3451,767 @@ String *Item_func_uuid::val_str(String *str) return str; } + + +Item_func_dyncol_create::Item_func_dyncol_create(List<Item> &args, + DYNCALL_CREATE_DEF *dfs) + : Item_str_func(args), defs(dfs), vals(0), nums(0) +{ + DBUG_ASSERT((args.elements & 0x1) == 0); // even number of arguments +} + + +bool Item_func_dyncol_create::fix_fields(THD *thd, Item **ref) +{ + bool res= Item_func::fix_fields(thd, ref); // no need Item_str_func here + vals= (DYNAMIC_COLUMN_VALUE *) alloc_root(thd->mem_root, + sizeof(DYNAMIC_COLUMN_VALUE) * + (arg_count / 2)); + nums= (uint *) alloc_root(thd->mem_root, + sizeof(uint) * (arg_count / 2)); + return res || vals == 0 || nums == 0; +} + + +void Item_func_dyncol_create::fix_length_and_dec() +{ + maybe_null= TRUE; + collation.set(&my_charset_bin); + decimals= 0; +} + +void Item_func_dyncol_create::prepare_arguments() +{ + char buff[STRING_BUFFER_USUAL_SIZE]; + String *res, tmp(buff, sizeof(buff), &my_charset_bin); + uint column_count= (arg_count / 2); + uint i; + my_decimal dtmp, *dres; + + /* get values */ + for (i= 0; i < column_count; i++) + { + uint valpos= i * 2 + 1; + DYNAMIC_COLUMN_TYPE type= defs[i].type; + if (type == DYN_COL_NULL) // auto detect + { + /* + We don't have a default here to ensure we get a warning if + one adds a new not handled MYSQL_TYPE_... + */ + switch (args[valpos]->field_type()) { + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + type= DYN_COL_DECIMAL; + break; + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_BIT: + type= DYN_COL_INT; + break; + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + type= DYN_COL_DOUBLE; + break; + case MYSQL_TYPE_NULL: + type= DYN_COL_NULL; + break; + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATETIME: + type= DYN_COL_DATETIME; + break; + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_NEWDATE: + type= DYN_COL_DATE; + break; + case MYSQL_TYPE_TIME: + type= DYN_COL_TIME; + break; + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + 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: + type= DYN_COL_STRING; + break; + } + } + nums[i]= (uint) args[i * 2]->val_int(); + vals[i].type= type; + switch (type) { + case DYN_COL_NULL: + DBUG_ASSERT(args[valpos]->field_type() == MYSQL_TYPE_NULL); + break; + case DYN_COL_INT: + vals[i].long_value= args[valpos]->val_int(); + break; + case DYN_COL_UINT: + vals[i].ulong_value= args[valpos]->val_int(); + break; + case DYN_COL_DOUBLE: + vals[i].double_value= args[valpos]->val_real(); + break; + case DYN_COL_STRING: + res= args[valpos]->val_str(&tmp); + if (res && + (vals[i].string_value.str= my_strndup(res->ptr(), res->length(), + MYF(MY_WME)))) + { + vals[i].string_value.length= res->length(); + vals[i].charset= res->charset(); + } + else + { + args[valpos]->null_value= 1; // In case of out of memory + vals[i].string_value.str= NULL; + vals[i].string_value.length= 0; // just to be safe + } + break; + case DYN_COL_DECIMAL: + if ((dres= args[valpos]->val_decimal(&dtmp))) + { + dynamic_column_prepare_decimal(&vals[i]); + DBUG_ASSERT(vals[i].decimal_value.len == dres->len); + vals[i].decimal_value.intg= dres->intg; + vals[i].decimal_value.frac= dres->frac; + vals[i].decimal_value.sign= dres->sign(); + memcpy(vals[i].decimal_buffer, dres->buf, + sizeof(vals[i].decimal_buffer)); + } + else + { + dynamic_column_prepare_decimal(&vals[i]); // just to be safe + DBUG_ASSERT(args[valpos]->null_value); + } + break; + case DYN_COL_DATETIME: + args[valpos]->get_date(&vals[i].time_value, TIME_FUZZY_DATE); + break; + case DYN_COL_DATE: + args[valpos]->get_date(&vals[i].time_value, TIME_FUZZY_DATE); + break; + case DYN_COL_TIME: + args[valpos]->get_time(&vals[i].time_value); + break; + default: + DBUG_ASSERT(0); + vals[i].type= DYN_COL_NULL; + } + if (vals[i].type != DYN_COL_NULL && args[valpos]->null_value) + { + if (vals[i].type == DYN_COL_STRING) + my_free(vals[i].string_value.str, MYF(MY_ALLOW_ZERO_PTR)); + vals[i].type= DYN_COL_NULL; + } + } +} + +void Item_func_dyncol_create::cleanup_arguments() +{ + uint column_count= (arg_count / 2); + uint i; + + for (i= 0; i < column_count; i++) + { + if (vals[i].type == DYN_COL_STRING) + my_free(vals[i].string_value.str, MYF(MY_ALLOW_ZERO_PTR)); + } +} + +String *Item_func_dyncol_create::val_str(String *str) +{ + DYNAMIC_COLUMN col; + String *res; + uint column_count= (arg_count / 2); + enum enum_dyncol_func_result rc; + DBUG_ASSERT((arg_count & 0x1) == 0); // even number of arguments + + prepare_arguments(); + + if ((rc= dynamic_column_create_many(&col, column_count, nums, vals))) + { + dynamic_column_error_message(rc); + dynamic_column_column_free(&col); + res= NULL; + null_value= TRUE; + } + else + { + /* Move result from DYNAMIC_COLUMN to str_value */ + char *ptr; + size_t length, alloc_length; + dynamic_column_reassociate(&col, &ptr, &length, &alloc_length); + str_value.reassociate(ptr, (uint32) length, (uint32) alloc_length, + &my_charset_bin); + res= &str_value; + } + + /* cleanup */ + cleanup_arguments(); + + return res; +} + +void Item_func_dyncol_create::print_arguments(String *str, + enum_query_type query_type) +{ + uint i; + uint column_count= (arg_count / 2); + for (i= 0; i < column_count; i++) + { + args[i*2]->print(str, query_type); + str->append(','); + args[i*2 + 1]->print(str, query_type); + switch (defs[i].type) { + case DYN_COL_NULL: // automatic type => write nothing + break; + case DYN_COL_INT: + str->append(STRING_WITH_LEN(" AS int")); + break; + case DYN_COL_UINT: + str->append(STRING_WITH_LEN(" AS unsigned int")); + break; + case DYN_COL_DOUBLE: + str->append(STRING_WITH_LEN(" AS double")); + break; + case DYN_COL_STRING: + str->append(STRING_WITH_LEN(" AS char")); + if (defs[i].cs) + { + str->append(STRING_WITH_LEN(" charset ")); + str->append(defs[i].cs->csname); + str->append(' '); + } + break; + case DYN_COL_DECIMAL: + str->append(STRING_WITH_LEN(" AS decimal")); + break; + case DYN_COL_DATETIME: + str->append(STRING_WITH_LEN(" AS datetime")); + break; + case DYN_COL_DATE: + str->append(STRING_WITH_LEN(" AS date")); + break; + case DYN_COL_TIME: + str->append(STRING_WITH_LEN(" AS time")); + break; + } + if (i < column_count - 1) + str->append(','); + } +} + + +void Item_func_dyncol_create::print(String *str, + enum_query_type query_type) +{ + DBUG_ASSERT((arg_count & 0x1) == 0); // even number of arguments + str->append(STRING_WITH_LEN("column_create(")); + print_arguments(str, query_type); + str->append(')'); +} + + +String *Item_func_dyncol_add::val_str(String *str) +{ + DYNAMIC_COLUMN col; + String *res; + uint column_count= (arg_count / 2); + enum enum_dyncol_func_result rc; + DBUG_ASSERT((arg_count & 0x1) == 1); // odd number of arguments + + /* We store the packed data last */ + res= args[arg_count - 1]->val_str(str); + if (args[arg_count - 1]->null_value) + goto null; + init_dynamic_string(&col, NULL, res->length() + STRING_BUFFER_USUAL_SIZE, + STRING_BUFFER_USUAL_SIZE); + + col.length= res->length(); + memcpy(col.str, res->ptr(), col.length); + + prepare_arguments(); + + if ((rc= dynamic_column_update_many(&col, column_count, nums, vals))) + { + dynamic_column_error_message(rc); + dynamic_column_column_free(&col); + cleanup_arguments(); + goto null; + } + + { + /* Move result from DYNAMIC_COLUMN to str */ + char *ptr; + size_t length, alloc_length; + dynamic_column_reassociate(&col, &ptr, &length, &alloc_length); + str->reassociate(ptr, (uint32) length, (uint32) alloc_length, + &my_charset_bin); + } + + /* cleanup */ + dynamic_column_column_free(&col); + cleanup_arguments(); + + return str; + +null: + null_value= TRUE; + return NULL; +} + + +void Item_func_dyncol_add::print(String *str, + enum_query_type query_type) +{ + DBUG_ASSERT((arg_count & 0x1) == 1); // odd number of arguments + str->append(STRING_WITH_LEN("column_create(")); + args[arg_count - 1]->print(str, query_type); + str->append(','); + print_arguments(str, query_type); + str->append(')'); +} + +bool Item_dyncol_get::get_dyn_value(DYNAMIC_COLUMN_VALUE *val, String *tmp) +{ + DYNAMIC_COLUMN dyn_str; + String *res; + longlong num; + enum enum_dyncol_func_result rc; + + num= args[1]->val_int(); + if (args[1]->null_value || num < 0 || num > INT_MAX) + { + null_value= 1; + return 1; + } + + res= args[0]->val_str(tmp); + if (args[0]->null_value) + { + null_value= 1; + return 1; + } + + dyn_str.str= (char*) res->ptr(); + dyn_str.length= res->length(); + if ((rc= dynamic_column_get(&dyn_str, (uint) num, val))) + { + dynamic_column_error_message(rc); + null_value= 1; + return 1; + } + + null_value= 0; + return 0; // ok +} + + +String *Item_dyncol_get::val_str(String *str_result) +{ + DYNAMIC_COLUMN_VALUE val; + char buff[STRING_BUFFER_USUAL_SIZE]; + String tmp(buff, sizeof(buff), &my_charset_bin); + + if (get_dyn_value(&val, &tmp)) + return NULL; + + switch (val.type) { + case DYN_COL_NULL: + goto null; + case DYN_COL_INT: + case DYN_COL_UINT: + str_result->set_int(val.long_value, test(val.type == DYN_COL_UINT), + &my_charset_latin1); + break; + case DYN_COL_DOUBLE: + str_result->set_real(val.double_value, NOT_FIXED_DEC, &my_charset_latin1); + break; + case DYN_COL_STRING: + if ((char*) tmp.ptr() <= val.string_value.str && + (char*) tmp.ptr() + tmp.length() >= val.string_value.str) + { + /* value is allocated in tmp buffer; We have to make a copy */ + str_result->copy(val.string_value.str, val.string_value.length, + val.charset); + } + else + { + /* + It's safe to use the current value because it's either pointing + into a field or in a buffer for another item and this buffer + is not going to be deleted during expression evaluation + */ + str_result->set(val.string_value.str, val.string_value.length, + val.charset); + } + break; + case DYN_COL_DECIMAL: + { + int res; + int length= + my_decimal_string_length((const my_decimal*)&val.decimal_value); + if (str_result->alloc(length)) + goto null; + if ((res= decimal2string(&val.decimal_value, (char*) str_result->ptr(), + &length, 0, 0, ' ')) != E_DEC_OK) + { + char buff[40]; + int len= sizeof(buff); + DBUG_ASSERT(length < (int)sizeof(buff)); + decimal2string(&val.decimal_value, buff, &len, 0, 0, ' '); + decimal_operation_results(res, buff, "CHAR"); + } + str_result->set_charset(&my_charset_latin1); + str_result->length(length); + break; + } + case DYN_COL_DATETIME: + case DYN_COL_DATE: + case DYN_COL_TIME: + { + int length; + if (str_result->alloc(MAX_DATE_STRING_REP_LENGTH) || + !(length= my_TIME_to_str(&val.time_value, (char*) str_result->ptr()))) + goto null; + str_result->set_charset(&my_charset_latin1); + str_result->length(length); + break; + } + } + return str_result; + +null: + null_value= TRUE; + return 0; +} + + +longlong Item_dyncol_get::val_int() +{ + DYNAMIC_COLUMN_VALUE val; + char buff[STRING_BUFFER_USUAL_SIZE]; + String tmp(buff, sizeof(buff), &my_charset_bin); + + if (get_dyn_value(&val, &tmp)) + return 0; + + switch (val.type) { + case DYN_COL_NULL: + goto null; + case DYN_COL_UINT: + unsigned_flag= 1; // Make it possible for caller to detect sign + return val.long_value; + case DYN_COL_INT: + unsigned_flag= 0; // Make it possible for caller to detect sign + return val.long_value; + case DYN_COL_DOUBLE: + { + bool error; + longlong num; + + num= double_to_longlong(val.double_value, unsigned_flag, &error); + if (error) + { + char buff[30]; + sprintf(buff, "%lg", val.double_value); + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_DATA_OVERFLOW, + ER(ER_DATA_OVERFLOW), + buff, + unsigned_flag ? "UNSIGNED INT" : "INT"); + } + return num; + } + case DYN_COL_STRING: + { + int error; + longlong num; + char *end= val.string_value.str + val.string_value.length, *org_end= end; + + num= my_strtoll10(val.string_value.str, &end, &error); + if (end != org_end || error > 0) + { + char buff[80]; + strmake(buff, val.string_value.str, min(sizeof(buff)-1, + val.string_value.length)); + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_BAD_DATA, + ER(ER_BAD_DATA), + buff, + unsigned_flag ? "UNSIGNED INT" : "INT"); + } + unsigned_flag= error >= 0; + return num; + } + case DYN_COL_DECIMAL: + { + longlong num; + my_decimal2int(E_DEC_FATAL_ERROR, &val.decimal_value, unsigned_flag, + &num); + return num; + } + case DYN_COL_DATETIME: + case DYN_COL_DATE: + case DYN_COL_TIME: + unsigned_flag= 1; + return TIME_to_ulonglong(&val.time_value); + } + +null: + null_value= TRUE; + return 0; +} + + +double Item_dyncol_get::val_real() +{ + DYNAMIC_COLUMN_VALUE val; + char buff[STRING_BUFFER_USUAL_SIZE]; + String tmp(buff, sizeof(buff), &my_charset_bin); + + if (get_dyn_value(&val, &tmp)) + return 0.0; + + switch (val.type) { + case DYN_COL_NULL: + goto null; + case DYN_COL_UINT: + return ulonglong2double(val.ulong_value); + case DYN_COL_INT: + return (double) val.long_value; + case DYN_COL_DOUBLE: + return (double) val.double_value; + case DYN_COL_STRING: + { + int error; + char *end; + double res= my_strntod(val.charset, (char*) val.string_value.str, + val.string_value.length, &end, &error); + + if (end != (char*) val.string_value.str + val.string_value.length || + error) + { + char buff[80]; + strmake(buff, val.string_value.str, min(sizeof(buff)-1, + val.string_value.length)); + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_BAD_DATA, + ER(ER_BAD_DATA), + buff, "DOUBLE"); + } + return res; + } + case DYN_COL_DECIMAL: + { + double res; + /* This will always succeed */ + decimal2double(&val.decimal_value, &res); + return res; + } + case DYN_COL_DATETIME: + case DYN_COL_DATE: + case DYN_COL_TIME: + return (ulonglong2double(TIME_to_ulonglong(&val.time_value)) + + val.time_value.second_part / (double) TIME_SUBSECOND_RANGE); + } + +null: + null_value= TRUE; + return 0.0; +} + + +my_decimal *Item_dyncol_get::val_decimal(my_decimal *decimal_value) +{ + DYNAMIC_COLUMN_VALUE val; + char buff[STRING_BUFFER_USUAL_SIZE]; + String tmp(buff, sizeof(buff), &my_charset_bin); + + if (get_dyn_value(&val, &tmp)) + return NULL; + + switch (val.type) { + case DYN_COL_NULL: + goto null; + case DYN_COL_UINT: + int2my_decimal(E_DEC_FATAL_ERROR, val.long_value, TRUE, decimal_value); + break; + case DYN_COL_INT: + int2my_decimal(E_DEC_FATAL_ERROR, val.long_value, FALSE, decimal_value); + break; + case DYN_COL_DOUBLE: + double2my_decimal(E_DEC_FATAL_ERROR, val.double_value, decimal_value); + break; + case DYN_COL_STRING: + { + int rc; + rc= str2my_decimal(0, val.string_value.str, val.string_value.length, + val.charset, decimal_value); + char buff[80]; + strmake(buff, val.string_value.str, min(sizeof(buff)-1, + val.string_value.length)); + if (rc != E_DEC_OK) + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_BAD_DATA, + ER(ER_BAD_DATA), + buff, "DECIMAL"); + } + break; + } + case DYN_COL_DECIMAL: + { + int length= STRING_BUFFER_USUAL_SIZE; + decimal2string(&val.decimal_value, buff, &length, 0,0, 0); + decimal2my_decimal(&val.decimal_value, decimal_value); + + break; + } + case DYN_COL_DATETIME: + case DYN_COL_DATE: + case DYN_COL_TIME: + { + double tmp= (ulonglong2double(TIME_to_ulonglong(&val.time_value)) + + val.time_value.second_part / (double) TIME_SUBSECOND_RANGE); + /* This can't overflow as time is always in the range of decimal */ + double2my_decimal(E_DEC_FATAL_ERROR, tmp, decimal_value); + break; + } + } + return decimal_value; + +null: + null_value= TRUE; + return 0; +} + + +bool Item_dyncol_get::get_date(MYSQL_TIME *ltime, uint fuzzy_date) +{ + DYNAMIC_COLUMN_VALUE val; + char buff[STRING_BUFFER_USUAL_SIZE]; + String tmp(buff, sizeof(buff), &my_charset_bin); + bool signed_value= 0; + + if (get_dyn_value(&val, &tmp)) + return 0; + + switch (val.type) { + case DYN_COL_NULL: + goto null; + case DYN_COL_INT: + signed_value= 1; // For error message + /* fall_trough */ + case DYN_COL_UINT: + { + ulonglong num; + int error; + + num= val.ulong_value; + number_to_datetime(num, ltime, fuzzy_date, &error); + if (error) + { + char buff[65]; + int errnum= error == 2 ? ER_DATA_OVERFLOW : ER_BAD_DATA; + longlong2str(num, buff, signed_value ? -10 : 10, 1); + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + errnum, + ER(errnum), + buff, "DATE or DATETIME"); + goto null; + } + return 0; + } + case DYN_COL_DOUBLE: + { + if (double_to_datetime_with_warn(val.double_value, ltime, fuzzy_date)) + goto null; + return 0; + } + case DYN_COL_DECIMAL: + if (decimal_to_datetime_with_warn(&val.decimal_value, ltime, fuzzy_date)) + goto null; + return 0; + case DYN_COL_STRING: + { + if (str_to_datetime_with_warn(val.string_value.str, + val.string_value.length, + ltime, fuzzy_date) <= MYSQL_TIMESTAMP_ERROR) + goto null; + return 0; + } + case DYN_COL_DATETIME: + case DYN_COL_DATE: + case DYN_COL_TIME: + *ltime= val.time_value; + return 0; + } + +null: + null_value= TRUE; + return 1; +} + + +void Item_dyncol_get::print(String *str, enum_query_type query_type) +{ + str->append(STRING_WITH_LEN("column_get(")); + args[0]->print(str, query_type); + str->append(','); + args[1]->print(str, query_type); + str->append(')'); +} + + +String *Item_func_dyncol_list::val_str(String *str) +{ + uint i; + enum enum_dyncol_func_result rc; + DYNAMIC_ARRAY arr; + DYNAMIC_COLUMN col; + String *res= args[0]->val_str(str); + + if (args[0]->null_value) + goto null; + col.length= res->length(); + /* We do not change the string, so could do this trick */ + col.str= (char *)res->ptr(); + if ((rc= dynamic_column_list(&col, &arr))) + { + dynamic_column_error_message(rc); + delete_dynamic(&arr); + goto null; + } + + /* + We support elements from 0 - 65536, so max size for one element is + 6 (including ,). + */ + if (str->alloc(arr.elements * 6)) + goto null; + + str->length(0); + for (i= 0; i < arr.elements; i++) + { + str->qs_append(*dynamic_element(&arr, i, uint*)); + if (i < arr.elements - 1) + str->qs_append(','); + } + + delete_dynamic(&arr); + return str; + +null: + null_value= TRUE; + return NULL; +} diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 53219e70973..4b88c329610 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -871,3 +871,71 @@ public: } }; + +class Item_func_dyncol_create: public Item_str_func +{ +protected: + DYNCALL_CREATE_DEF *defs; + DYNAMIC_COLUMN_VALUE *vals; + uint *nums; + void prepare_arguments(); + void cleanup_arguments(); + void print_arguments(String *str, enum_query_type query_type); +public: + Item_func_dyncol_create(List<Item> &args, DYNCALL_CREATE_DEF *dfs); + bool fix_fields(THD *thd, Item **ref); + void fix_length_and_dec(); + const char *func_name() const{ return "column_create"; } + String *val_str(String *); + virtual void print(String *str, enum_query_type query_type); +}; + + +class Item_func_dyncol_add: public Item_func_dyncol_create +{ +public: + Item_func_dyncol_add(List<Item> &args, DYNCALL_CREATE_DEF *dfs) + :Item_func_dyncol_create(args, dfs) + {} + const char *func_name() const{ return "column_add"; } + String *val_str(String *); + virtual void print(String *str, enum_query_type query_type); +}; + + +/* + The following functions is always called from an Item_cast function +*/ + +class Item_dyncol_get: public Item_str_func +{ +public: + Item_dyncol_get(Item *str, Item *num) + :Item_str_func(str, num) + { + max_length= MAX_FIELD_BLOBLENGTH; + } + void fix_length_and_dec() + { maybe_null= 1; } + /* Mark that collation can change between calls */ + bool dynamic_result() { return 1; } + + const char *func_name() const { return "column_get"; } + String *val_str(String *); + longlong val_int(); + double val_real(); + my_decimal *val_decimal(my_decimal *); + bool get_dyn_value(DYNAMIC_COLUMN_VALUE *val, String *tmp); + bool get_date(MYSQL_TIME *ltime,uint fuzzydate); + void print(String *str, enum_query_type query_type); +}; + + +class Item_func_dyncol_list: public Item_str_func +{ +public: + Item_func_dyncol_list(Item *str) :Item_str_func(str) {}; + void fix_length_and_dec() { maybe_null= 1; max_length= MAX_BLOB_WIDTH; }; + const char *func_name() const{ return "column_list"; } + String *val_str(String *); +}; diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 9cf56148994..91b7ffbec71 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1952,7 +1952,10 @@ String *Item_func_date_format::val_str(String *str) { String *res; if (!(res=args[0]->val_str(str)) || - (str_to_time_with_warn(res->ptr(), res->length(), &l_time))) + (str_to_time_with_warn(res->ptr(), res->length(), &l_time, + current_thd->variables.sql_mode & + (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | + MODE_INVALID_DATES)))) goto null_date; l_time.year=l_time.month=l_time.day=0; @@ -2322,7 +2325,11 @@ longlong Item_extract::val_int() char buf[40]; String value(buf, sizeof(buf), &my_charset_bin);; String *res= args[0]->val_str(&value); - if (!res || str_to_time_with_warn(res->ptr(), res->length(), <ime)) + if (!res || + str_to_time_with_warn(res->ptr(), res->length(), <ime, + current_thd->variables.sql_mode & + (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | + MODE_INVALID_DATES))) { null_value=1; return 0; @@ -2461,10 +2468,15 @@ String *Item_char_typecast::val_str(String *str) } else { - // Convert character set if differ + /* + Convert character set if differ + from_cs is 0 in the case where the result set may vary between calls, + for example with dynamic columns. + */ uint dummy_errors; if (!(res= args[0]->val_str(str)) || - tmp_value.copy(res->ptr(), res->length(), from_cs, + tmp_value.copy(res->ptr(), res->length(), + from_cs ? from_cs : res->charset(), cast_cs, &dummy_errors)) { null_value= 1; @@ -2542,15 +2554,19 @@ void Item_char_typecast::fix_length_and_dec() and thus avoid unnecessary character set conversion. - If the argument is not a number, then from_cs is set to the argument's charset. + - If argument has a dynamic collation (can change from call to call) + we set from_cs to 0 as a marker that we have to take the collation + from the result string. Note (TODO): we could use repertoire technique here. */ - from_cs= (args[0]->result_type() == INT_RESULT || - args[0]->result_type() == DECIMAL_RESULT || - args[0]->result_type() == REAL_RESULT) ? - (cast_cs->mbminlen == 1 ? cast_cs : &my_charset_latin1) : - args[0]->collation.collation; - charset_conversion= (cast_cs->mbmaxlen > 1) || + from_cs= ((args[0]->result_type() == INT_RESULT || + args[0]->result_type() == DECIMAL_RESULT || + args[0]->result_type() == REAL_RESULT) ? + (cast_cs->mbminlen == 1 ? cast_cs : &my_charset_latin1) : + args[0]->dynamic_result() ? 0 : + args[0]->collation.collation); + charset_conversion= !from_cs || (cast_cs->mbmaxlen > 1) || (!my_charset_same(from_cs, cast_cs) && from_cs != &my_charset_bin && cast_cs != &my_charset_bin); @@ -2604,6 +2620,11 @@ bool Item_time_typecast::get_time(MYSQL_TIME *ltime) return res; } +bool Item_time_typecast::get_date(MYSQL_TIME *ltime, uint fuzzy_date) +{ + return get_time(ltime); +} + longlong Item_time_typecast::val_int() { @@ -2621,7 +2642,7 @@ String *Item_time_typecast::val_str(String *str) DBUG_ASSERT(fixed == 1); MYSQL_TIME ltime; - if (!get_arg0_time(<ime) && + if (!get_time(<ime) && !make_datetime(ltime.second_part ? TIME_MICROSECOND : TIME_ONLY, <ime, str)) return str; diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 87af384923e..7e73f6a4765 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -937,6 +937,7 @@ class Item_time_typecast :public Item_typecast_maybe_null public: Item_time_typecast(Item *a) :Item_typecast_maybe_null(a) {} const char *func_name() const { return "cast_as_time"; } + bool get_date(MYSQL_TIME *ltime, uint fuzzy_date); String *val_str(String *str); bool get_time(MYSQL_TIME *ltime); const char *cast_type() const { return "time"; } diff --git a/sql/lex.h b/sql/lex.h index e763b0325ea..6ab234f8d81 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -116,6 +116,12 @@ static SYMBOL symbols[] = { { "COLLATION", SYM(COLLATION_SYM)}, { "COLUMN", SYM(COLUMN_SYM)}, { "COLUMNS", SYM(COLUMNS)}, + { "COLUMN_ADD", SYM(COLUMN_ADD_SYM)}, + { "COLUMN_CREATE", SYM(COLUMN_CREATE_SYM)}, + { "COLUMN_DELETE", SYM(COLUMN_DELETE_SYM)}, + { "COLUMN_EXISTS", SYM(COLUMN_EXISTS_SYM)}, + { "COLUMN_GET", SYM(COLUMN_GET_SYM)}, + { "COLUMN_LIST", SYM(COLUMN_LIST_SYM)}, { "COMMENT", SYM(COMMENT_SYM)}, { "COMMIT", SYM(COMMIT_SYM)}, { "COMMITTED", SYM(COMMITTED_SYM)}, diff --git a/sql/my_decimal.cc b/sql/my_decimal.cc index 2bbdeed2d87..6bf72e32dfe 100644 --- a/sql/my_decimal.cc +++ b/sql/my_decimal.cc @@ -30,21 +30,20 @@ result */ -int decimal_operation_results(int result) +int decimal_operation_results(int result, const char *value, const char *type) { switch (result) { case E_DEC_OK: break; case E_DEC_TRUNCATED: push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, - WARN_DATA_TRUNCATED, ER(WARN_DATA_TRUNCATED), - "", (ulong) 0); + ER_DATA_TRUNCATED, ER(ER_DATA_TRUNCATED), + value, type); break; case E_DEC_OVERFLOW: push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, - ER_TRUNCATED_WRONG_VALUE, - ER(ER_TRUNCATED_WRONG_VALUE), - "DECIMAL", ""); + ER_DATA_OVERFLOW, ER(ER_DATA_OVERFLOW), + value, type); break; case E_DEC_DIV_ZERO: push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, @@ -52,9 +51,8 @@ int decimal_operation_results(int result) break; case E_DEC_BAD_NUM: push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, - ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, - ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), - "decimal", "", "", (ulong) 0); + ER_BAD_DATA, ER(ER_BAD_DATA), + value, type); break; case E_DEC_OOM: my_error(ER_OUT_OF_RESOURCES, MYF(0)); @@ -237,6 +235,34 @@ void my_decimal_trim(ulong *precision, uint *scale) } +/* + Convert a decimal to an ulong with a descreptive error message +*/ + +int my_decimal2int(uint mask, const decimal_t *d, bool unsigned_flag, + longlong *l) +{ + int res; + my_decimal rounded; + /* decimal_round can return only E_DEC_TRUNCATED */ + decimal_round(d, &rounded, 0, HALF_UP); + res= (unsigned_flag ? + decimal2ulonglong(&rounded, (ulonglong *) l) : + decimal2longlong(&rounded, l)); + if (res & mask) + { + char buff[DECIMAL_MAX_STR_LENGTH]; + int length= sizeof(buff); + decimal2string(d, buff, &length, 0, 0, 0); + + decimal_operation_results(res, buff, + unsigned_flag ? "UNSIGNED INT" : + "INT"); + } + return res; +} + + #ifndef DBUG_OFF /* routines for debugging print */ @@ -283,7 +309,6 @@ const char *dbug_decimal_as_string(char *buff, const my_decimal *val) return buff; } -#endif /*DBUG_OFF*/ - +#endif /*DBUG_OFF*/ #endif /*MYSQL_CLIENT*/ diff --git a/sql/my_decimal.h b/sql/my_decimal.h index 86f5db29ed7..7ea720f4e63 100644 --- a/sql/my_decimal.h +++ b/sql/my_decimal.h @@ -32,32 +32,7 @@ C_MODE_START #include <decimal.h> C_MODE_END -#define DECIMAL_LONGLONG_DIGITS 22 -#define DECIMAL_LONG_DIGITS 10 -#define DECIMAL_LONG3_DIGITS 8 - -/** maximum length of buffer in our big digits (uint32). */ -#define DECIMAL_BUFF_LENGTH 9 - -/* the number of digits that my_decimal can possibly contain */ -#define DECIMAL_MAX_POSSIBLE_PRECISION (DECIMAL_BUFF_LENGTH * 9) - - -/** - maximum guaranteed precision of number in decimal digits (number of our - digits * number of decimal digits in one our big digit - number of decimal - digits in one our big digit decreased by 1 (because we always put decimal - point on the border of our big digits)) -*/ -#define DECIMAL_MAX_PRECISION (DECIMAL_MAX_POSSIBLE_PRECISION - 8*2) -#define DECIMAL_MAX_SCALE 30 -#define DECIMAL_NOT_SPECIFIED 31 - -/** - maximum length of string representation (number of maximum decimal - digits + 1 position for sign + 1 position for decimal point) -*/ -#define DECIMAL_MAX_STR_LENGTH (DECIMAL_MAX_POSSIBLE_PRECISION + 2) +#include <my_decimal_limits.h> /** maximum size of packet length. @@ -134,9 +109,10 @@ const char *dbug_decimal_as_string(char *buff, const my_decimal *val); #endif #ifndef MYSQL_CLIENT -int decimal_operation_results(int result); +int decimal_operation_results(int result, const char *value, const char *type); #else -inline int decimal_operation_results(int result) +inline int decimal_operation_results(int result, const char *value, + const char *type) { return result; } @@ -158,7 +134,7 @@ inline void max_internal_decimal(my_decimal *to) inline int check_result(uint mask, int result) { if (result & mask) - decimal_operation_results(result); + decimal_operation_results(result, "", "DECIMAL"); return result; } @@ -294,24 +270,14 @@ int my_decimal2string(uint mask, const my_decimal *d, uint fixed_prec, uint fixed_dec, char filler, String *str); #endif -inline -int my_decimal2int(uint mask, const my_decimal *d, my_bool unsigned_flag, - longlong *l) -{ - my_decimal rounded; - /* decimal_round can return only E_DEC_TRUNCATED */ - decimal_round((decimal_t*)d, &rounded, 0, HALF_UP); - return check_result(mask, (unsigned_flag ? - decimal2ulonglong(&rounded, (ulonglong *)l) : - decimal2longlong(&rounded, l))); -} - +int my_decimal2int(uint mask, const decimal_t *d, bool unsigned_flag, + longlong *l); inline -int my_decimal2double(uint, const my_decimal *d, double *result) +int my_decimal2double(uint, const decimal_t *d, double *result) { /* No need to call check_result as this will always succeed */ - return decimal2double((decimal_t*) d, result); + return decimal2double(d, result); } @@ -354,6 +320,16 @@ int int2my_decimal(uint mask, longlong i, my_bool unsigned_flag, my_decimal *d) longlong2decimal(i, d))); } +inline +void decimal2my_decimal(decimal_t *from, my_decimal *to) +{ + DBUG_ASSERT(to->len >= from->len); + to->intg= from->intg; + to->frac= from->frac; + to->sign(from->sign); + memcpy(to->buf, from->buf, to->len*sizeof(decimal_digit_t)); +} + inline void my_decimal_neg(decimal_t *arg) @@ -417,7 +393,6 @@ int my_decimal_mod(uint mask, my_decimal *res, const my_decimal *a, res); } - /** @return -1 if a<b, 1 if a>b and 0 if a==b diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 912f33ac983..fd4b444d222 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1412,6 +1412,7 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, Field * find_field_in_table_sef(TABLE *table, const char *name); int update_virtual_fields(THD *thd, TABLE *table, bool ignore_stored= FALSE); +int dynamic_column_error_message(enum_dyncol_func_result rc); #endif /* MYSQL_SERVER */ @@ -2322,16 +2323,22 @@ ulong convert_month_to_period(ulong month); void get_date_from_daynr(long daynr,uint *year, uint *month, uint *day); my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, my_bool *not_exist); -bool str_to_time_with_warn(const char *str,uint length,MYSQL_TIME *l_time); +bool str_to_time_with_warn(const char *str,uint length,MYSQL_TIME *l_time, + ulong fuzzydate); timestamp_type str_to_datetime_with_warn(const char *str, uint length, - MYSQL_TIME *l_time, uint flags); + MYSQL_TIME *l_time, ulong flags); void localtime_to_TIME(MYSQL_TIME *to, struct tm *from); void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds); -void make_truncated_value_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, +void make_truncated_value_warning(THD *thd, + MYSQL_ERROR::enum_warning_level level, const char *str_val, uint str_length, timestamp_type time_type, const char *field_name); +bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime, + ulong fuzzy_date); +bool decimal_to_datetime_with_warn(decimal_t *value, MYSQL_TIME *ltime, + ulong fuzzy_date); bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL interval); bool calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 853c9fac56e..5d23a76d121 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5321,7 +5321,7 @@ ER_NO_DEFAULT_FOR_FIELD ER_DIVISION_BY_ZERO 22012 eng "Division by 0" ger "Division durch 0" -ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +ER_TRUNCATED_WRONG_VALUE_FOR_FIELD 22007 eng "Incorrect %-.32s value: '%-.128s' for column '%.192s' at row %lu" ger "Falscher %-.32s-Wert: '%-.128s' für Feld '%.192s' in Zeile %lu" ER_ILLEGAL_VALUE_FOR_TYPE 22007 @@ -6251,4 +6251,17 @@ ER_BAD_OPTION_VALUE eng "Incorrect value '%-.64s' for option '%-.64s'" ER_CANT_DO_ONLINE eng "Can't execute the given '%s' command as online" - +ER_DATA_OVERFLOW 22003 + eng "Got overflow when converting '%-.128s' to %-.32s. Value truncated." +ER_DATA_TRUNCATED 22003 + eng "Truncated value '%-.128s' when converting to %-.32s" +ER_BAD_DATA 22007 + eng "Encountered illegal value '%-.128s' when converting to %-.32s" +ER_DYN_COL_WRONG_FORMAT + eng "Encountered illegal format of dynamic column string" +ER_DYN_COL_IMPLEMENTATION_LIMIT + eng "Dynamic column implementation limit reached" +ER_DYN_COL_DATA 22007 + eng "Illegal value used as argument of dynamic column function" +ER_DYN_COL_WRONG_CHARSET + eng "Dynamic column contains unknown character set" diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 47d85238cff..2c652d2da6a 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -7999,7 +7999,8 @@ static int do_auth_once(THD *thd, LEX_STRING *auth_plugin_name, @retval 1 error */ -bool acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len) +bool acl_authenticate(THD *thd, uint connect_errors, + uint com_change_user_pkt_len) { int res= CR_OK; MPVIO_EXT mpvio; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index fa495a776e1..b4096550fa8 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -9406,6 +9406,40 @@ void close_performance_schema_table(THD *thd, Open_tables_state *backup) thd->restore_backup_open_tables_state(backup); } + +/** + Check result of dynamic column function and issue error if it is needed + + @param rc The result code of dynamic column function + + @return the result code which was get as an argument\ +*/ + +int dynamic_column_error_message(enum_dyncol_func_result rc) +{ + switch (rc) { + case ER_DYNCOL_YES: + case ER_DYNCOL_OK: + break; // it is not an error + case ER_DYNCOL_FORMAT: + my_error(ER_DYN_COL_WRONG_FORMAT, MYF(0)); + break; + case ER_DYNCOL_LIMIT: + my_error(ER_DYN_COL_IMPLEMENTATION_LIMIT, MYF(0)); + break; + case ER_DYNCOL_RESOURCE: + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + break; + case ER_DYNCOL_DATA: + my_error(ER_DYN_COL_DATA, MYF(0)); + break; + case ER_DYNCOL_UNKNOWN_CHARSET: + my_error(ER_DYN_COL_WRONG_CHARSET, MYF(0)); + break; + } + return rc; +} + /** @} (end of group Data_Dictionary) */ diff --git a/sql/sql_string.h b/sql/sql_string.h index c9eaf924e4d..ca6bd2f0116 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -176,6 +176,18 @@ public: { return set_int((longlong)num, true, cs); } bool set_real(double num,uint decimals, CHARSET_INFO *cs); + /* Move handling of buffer from some other object to String */ + void reassociate(char *ptr, uint32 length, uint32 alloced_length, + CHARSET_INFO *cs) + { + free(); + Ptr= ptr; + str_length= length; + Alloced_length= alloced_length; + str_charset= cs; + alloced= ptr != 0; + } + /* PMG 2004.11.12 This is a method that works the same as perl's "chop". It simply diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index fbced869951..71dcd637d64 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -34,6 +34,7 @@ #define YYINITDEPTH 100 #define YYMAXDEPTH 3200 /* Because of 64K stack */ #define Lex (YYTHD->lex) + #define Select Lex->current_select #include "mysql_priv.h" #include "slave.h" @@ -671,6 +672,8 @@ static bool add_create_index (LEX *lex, Key::Keytype type, const char *name, sp_head *sphead; struct p_elem_val *p_elem_value; enum index_hint_type index_hint; + DYNCALL_CREATE_DEF *dyncol_def; + List<DYNCALL_CREATE_DEF> *dyncol_def_list; } %{ @@ -770,6 +773,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token COLLATE_SYM /* SQL-2003-R */ %token COLLATION_SYM /* SQL-2003-N */ %token COLUMNS +%token COLUMN_ADD_SYM +%token COLUMN_CREATE_SYM +%token COLUMN_DELETE_SYM +%token COLUMN_EXISTS_SYM +%token COLUMN_GET_SYM +%token COLUMN_LIST_SYM %token COLUMN_SYM /* SQL-2003-R */ %token COMMENT_SYM %token COMMITTED_SYM /* SQL-2003-N */ @@ -1338,7 +1347,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_natural_language_mode opt_query_expansion opt_ev_status opt_ev_on_completion ev_on_completion opt_ev_comment ev_alter_on_schedule_completion opt_ev_rename_to opt_ev_sql_stmt - optional_flush_tables_arguments + optional_flush_tables_arguments opt_dyncol_type dyncol_type %type <ulong_num> ulong_num real_ulong_num merge_insert_types @@ -1438,6 +1447,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <boolfunc2creator> comp_op +%type <dyncol_def> dyncall_create_element + +%type <dyncol_def_list> dyncall_create_list + %type <NONE> query verb_clause create change select do drop insert replace insert2 insert_values update delete truncate rename @@ -7506,6 +7519,131 @@ all_or_any: | ANY_SYM { $$ = 0; } ; +opt_dyncol_type: + /* empty */ + { + LEX *lex= Lex; + $$= DYN_COL_NULL; /* automatic type */ + lex->charset= NULL; + lex->length= lex->dec= 0; + } + | AS dyncol_type { $$= $2; } + ; + +dyncol_type: + INT_SYM + { + LEX *lex= Lex; + $$= DYN_COL_INT; + lex->charset= NULL; + lex->length= lex->dec= 0; + } + | UNSIGNED INT_SYM + { + LEX *lex= Lex; + $$= DYN_COL_UINT; + lex->charset= NULL; + lex->length= lex->dec= 0; + } + | DOUBLE_SYM + { + LEX *lex= Lex; + $$= DYN_COL_DOUBLE; + lex->charset= NULL; + lex->length= lex->dec= 0; + } + | REAL + { + LEX *lex= Lex; + $$= DYN_COL_DOUBLE; + lex->charset= NULL; + lex->length= lex->dec= 0; + } + | FLOAT_SYM + { + LEX *lex= Lex; + $$= DYN_COL_DOUBLE; + lex->charset= NULL; + lex->length= lex->dec= 0; + } + | DECIMAL_SYM float_options + { + $$= DYN_COL_DECIMAL; + Lex->charset= NULL; + } + | char opt_binary + { + LEX *lex= Lex; + $$= DYN_COL_STRING; + lex->length= lex->dec= 0; + } + | nchar + { + LEX *lex= Lex; + $$= DYN_COL_STRING; + lex->charset= national_charset_info; + lex->length= lex->dec= 0; + } + | DATE_SYM + { + LEX *lex= Lex; + $$= DYN_COL_DATE; + lex->charset= NULL; + lex->length= lex->dec= 0; + } + | TIME_SYM + { + LEX *lex= Lex; + $$= DYN_COL_TIME; + lex->charset= NULL; + lex->length= lex->dec= 0; + } + | DATETIME + { + LEX *lex= Lex; + $$= DYN_COL_DATETIME; + lex->charset= NULL; + lex->length= lex->dec= 0; + } + ; + +dyncall_create_element: + expr ',' expr opt_dyncol_type + { + LEX *lex= Lex; + $$= (DYNCALL_CREATE_DEF *) + alloc_root(YYTHD->mem_root, sizeof(DYNCALL_CREATE_DEF)); + if ($$ == NULL) + MYSQL_YYABORT; + $$->num= $1; + $$->value= $3; + $$->type= (DYNAMIC_COLUMN_TYPE)$4; + $$->cs= lex->charset; + if (lex->length) + $$->len= strtoul(lex->length, NULL, 10); + else + $$->len= 0; + if (lex->dec) + $$->frac= strtoul(lex->dec, NULL, 10); + else + $$->len= 0; + } + +dyncall_create_list: + dyncall_create_element + { + $$= new (YYTHD->mem_root) List<DYNCALL_CREATE_DEF>; + if ($$ == NULL) + MYSQL_YYABORT; + $$->push_back($1); + } + | dyncall_create_list ',' dyncall_create_element + { + $1->push_back($3); + $$= $1; + } + ; + simple_expr: simple_ident | function_call_keyword @@ -8040,6 +8178,51 @@ function_call_nonkeyword: MYSQL_YYABORT; Lex->safe_to_cache_query=0; } + | + COLUMN_ADD_SYM '(' expr ',' dyncall_create_list ')' + { + $$= create_func_dyncol_add(YYTHD, $3, *$5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + COLUMN_DELETE_SYM '(' expr ',' expr_list ')' + { + $$= create_func_dyncol_delete(YYTHD, $3, *$5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + COLUMN_EXISTS_SYM '(' expr ',' expr ')' + { + $$= new (YYTHD->mem_root) Item_func_dyncol_exists($3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + COLUMN_LIST_SYM '(' expr ')' + { + $$= new (YYTHD->mem_root) Item_func_dyncol_list($3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + COLUMN_CREATE_SYM '(' dyncall_create_list ')' + { + $$= create_func_dyncol_create(YYTHD, *$3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + COLUMN_GET_SYM '(' expr ',' expr AS cast_type ')' + { + LEX *lex= Lex; + $$= create_func_dyncol_get(YYTHD, $3, $5, $7, + lex->length, lex->dec, + lex->charset); + if ($$ == NULL) + MYSQL_YYABORT; + } ; /* @@ -8641,6 +8824,8 @@ cast_type: { $$=ITEM_CAST_CHAR; Lex->dec= 0; } | NCHAR_SYM opt_field_length { $$=ITEM_CAST_CHAR; Lex->charset= national_charset_info; Lex->dec=0; } + | INT_SYM + { $$=ITEM_CAST_SIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } | SIGNED_SYM { $$=ITEM_CAST_SIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } | SIGNED_SYM INT_SYM @@ -8657,7 +8842,10 @@ cast_type: { $$=ITEM_CAST_DATETIME; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } | DECIMAL_SYM float_options { $$=ITEM_CAST_DECIMAL; Lex->charset= NULL; } - ; + | DOUBLE_SYM + { Lex->charset= NULL; Lex->length= Lex->dec= 0;} + opt_precision + { $$=ITEM_CAST_DOUBLE; } opt_expr_list: /* empty */ { $$= NULL; } @@ -11887,6 +12075,12 @@ keyword: | CHECKSUM_SYM {} | CHECKPOINT_SYM {} | CLOSE_SYM {} + | COLUMN_ADD_SYM {} + | COLUMN_CREATE_SYM {} + | COLUMN_DELETE_SYM {} + | COLUMN_EXISTS_SYM {} + | COLUMN_GET_SYM {} + | COLUMN_LIST_SYM {} | COMMENT_SYM {} | COMMIT_SYM {} | CONTAINS_SYM {} diff --git a/sql/time.cc b/sql/time.cc index c4f6c3c9539..a3b78eb5e62 100644 --- a/sql/time.cc +++ b/sql/time.cc @@ -223,7 +223,7 @@ ulong convert_month_to_period(ulong month) timestamp_type str_to_datetime_with_warn(const char *str, uint length, MYSQL_TIME *l_time, - uint flags) + ulong flags) { int was_cut; THD *thd= current_thd; @@ -232,15 +232,42 @@ str_to_datetime_with_warn(const char *str, uint length, MYSQL_TIME *l_time, ts_type= str_to_datetime(str, length, l_time, (flags | (thd->variables.sql_mode & (MODE_INVALID_DATES | + MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE))), &was_cut); if (was_cut || ts_type <= MYSQL_TIMESTAMP_ERROR) - make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, str, length, ts_type, NullS); return ts_type; } +bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime, + ulong fuzzydate) +{ + if (double_to_datetime(value, ltime, fuzzydate)) + { + char buff[40]; + uint length= my_sprintf(buff, (buff, "%-30.21g", value)); + make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + buff, length, MYSQL_TIMESTAMP_DATETIME, + NullS); + return 1; + } + return 0; +} + +bool decimal_to_datetime_with_warn(decimal_t *value, MYSQL_TIME *ltime, + ulong fuzzydate) +{ + char buff[40]; + int length= sizeof(buff); + + decimal2string(value, buff, &length, 0, 0, 0); + return (str_to_datetime_with_warn(buff, length, ltime, fuzzydate) <= + MYSQL_TIMESTAMP_ERROR); +} + /* Convert a datetime from broken-down MYSQL_TIME representation to corresponding TIMESTAMP value. @@ -284,10 +311,11 @@ my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, my_bool *in_dst_time_ See str_to_time() for more info. */ bool -str_to_time_with_warn(const char *str, uint length, MYSQL_TIME *l_time) +str_to_time_with_warn(const char *str, uint length, MYSQL_TIME *l_time, + ulong fuzzydate) { int warning; - bool ret_val= str_to_time(str, length, l_time, &warning); + bool ret_val= str_to_time(str, length, l_time, fuzzydate, &warning); if (ret_val || warning) make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, str, length, MYSQL_TIMESTAMP_TIME, NullS); @@ -719,7 +747,8 @@ void make_datetime(const DATE_TIME_FORMAT *format __attribute__((unused)), } -void make_truncated_value_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, +void make_truncated_value_warning(THD *thd, + MYSQL_ERROR::enum_warning_level level, const char *str_val, uint str_length, timestamp_type time_type, const char *field_name) @@ -763,6 +792,7 @@ void make_truncated_value_warning(THD *thd, MYSQL_ERROR::enum_warning_level leve ER_TRUNCATED_WRONG_VALUE, warn_buff); } + /* Daynumber from year 0 to 9999-12-31 */ #define MAX_DAY_NUMBER 3652424L |