summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/field.cc185
-rw-r--r--sql/field.h4
-rw-r--r--sql/item.cc66
-rw-r--r--sql/item.h21
-rw-r--r--sql/item_cmpfunc.cc37
-rw-r--r--sql/item_cmpfunc.h9
-rw-r--r--sql/item_create.cc225
-rw-r--r--sql/item_create.h10
-rw-r--r--sql/item_func.cc90
-rw-r--r--sql/item_func.h22
-rw-r--r--sql/item_strfunc.cc764
-rw-r--r--sql/item_strfunc.h68
-rw-r--r--sql/item_timefunc.cc43
-rw-r--r--sql/item_timefunc.h1
-rw-r--r--sql/lex.h6
-rw-r--r--sql/my_decimal.cc47
-rw-r--r--sql/my_decimal.h63
-rw-r--r--sql/mysql_priv.h13
-rw-r--r--sql/share/errmsg.txt17
-rw-r--r--sql/sql_acl.cc3
-rw-r--r--sql/sql_base.cc34
-rw-r--r--sql/sql_string.h12
-rw-r--r--sql/sql_yacc.yy198
-rw-r--r--sql/time.cc40
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, &ltime, &warning))
+ if (str_to_time(from, len, &ltime,
+ 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, &ltime, 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(), &ltime))
+ if (!res ||
+ str_to_time_with_warn(res->ptr(), res->length(), &ltime,
+ 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(&ltime) &&
+ if (!get_time(&ltime) &&
!make_datetime(ltime.second_part ? TIME_MICROSECOND : TIME_ONLY,
&ltime, 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