diff options
Diffstat (limited to 'sql/field.cc')
-rw-r--r-- | sql/field.cc | 2755 |
1 files changed, 1128 insertions, 1627 deletions
diff --git a/sql/field.cc b/sql/field.cc index 61786557531..bf4cb3e4ff9 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. - +/* Copyright (c) 2000, 2011, 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 the Free Software Foundation; version 2 of the License. @@ -52,6 +52,17 @@ template class List<Create_field>; template class List_iterator<Create_field>; #endif +static const char *zero_timestamp="0000-00-00 00:00:00.000000"; + +/* number of bytes to store second_part part of the TIMESTAMP(N) */ +static uint sec_part_bytes[MAX_DATETIME_PRECISION+1]= { 0, 1, 1, 2, 2, 3, 3 }; + +/* number of bytes to store DATETIME(N) */ +static uint datetime_hires_bytes[MAX_DATETIME_PRECISION+1]= { 5, 6, 6, 7, 7, 7, 8 }; + +/* number of bytes to store TIME(N) */ +static uint time_hires_bytes[MAX_DATETIME_PRECISION+1]= { 3, 4, 4, 5, 5, 5, 6 }; + uchar Field_null::null[1]={1}; const char field_separator=','; @@ -76,7 +87,7 @@ const char field_separator=','; #define FIELDTYPE_TEAR_FROM (MYSQL_TYPE_BIT + 1) #define FIELDTYPE_TEAR_TO (MYSQL_TYPE_NEWDECIMAL - 1) #define FIELDTYPE_NUM (FIELDTYPE_TEAR_FROM + (255 - FIELDTYPE_TEAR_TO)) -inline int field_type2index (enum_field_types field_type) +static inline int field_type2index (enum_field_types field_type) { return (field_type < FIELDTYPE_TEAR_FROM ? field_type : @@ -1431,13 +1442,6 @@ int Field::store(const char *to, uint length, CHARSET_INFO *cs, should be overridden. The other functions are just convenience functions and hence should not be overridden. - The value of <code>low_byte_first</code> is dependent on how the - packed data is going to be used: for local use, e.g., temporary - store on disk or in memory, use the native format since that is - faster. For data that is going to be transfered to other machines - (e.g., when writing data to the binary log), data should always be - stored in little-endian format. - @note The default method for packing fields just copy the raw bytes of the record into the destination, but never more than <code>max_length</code> characters. @@ -1455,15 +1459,9 @@ int Field::store(const char *to, uint length, CHARSET_INFO *cs, is 1000. This information is sometimes needed to decide how to pack the data. - @param low_byte_first - @c TRUE if integers should be stored little-endian, @c FALSE if - native format should be used. Note that for little-endian machines, - the value of this flag is a moot point since the native format is - little-endian. */ uchar * -Field::pack(uchar *to, const uchar *from, uint max_length, - bool low_byte_first __attribute__((unused))) +Field::pack(uchar *to, const uchar *from, uint max_length) { uint32 length= pack_length(); set_if_smaller(length, max_length); @@ -1494,16 +1492,10 @@ Field::pack(uchar *to, const uchar *from, uint max_length, @param param_data Real type and original pack length of the field data - @param low_byte_first - If this flag is @c true, all composite entities (e.g., lengths) - should be unpacked in little-endian format; otherwise, the entities - are unpacked in native order. - @return New pointer into memory based on from + length of the data */ const uchar * -Field::unpack(uchar* to, const uchar *from, uint param_data, - bool low_byte_first __attribute__((unused))) +Field::unpack(uchar* to, const uchar *from, uint param_data) { uint length=pack_length(); int from_type= 0; @@ -1564,9 +1556,9 @@ void Field::make_field(Send_field *field) } else field->org_table_name= field->db_name= ""; - if (orig_table && orig_table->alias) + if (orig_table && orig_table->alias.ptr()) { - field->table_name= orig_table->alias; + field->table_name= orig_table->alias.ptr(); field->org_col_name= field_name; } else @@ -1606,17 +1598,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; @@ -1776,16 +1770,6 @@ bool Field::get_date(MYSQL_TIME *ltime,uint fuzzydate) return 0; } -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->charset(), res->ptr(), res->length(), ltime)) - return 1; - return 0; -} - /** This is called when storing a date in a string. @@ -1793,11 +1777,11 @@ bool Field::get_time(MYSQL_TIME *ltime) Needs to be changed if/when we want to support different time formats. */ -int Field::store_time(MYSQL_TIME *ltime, timestamp_type type_arg) +int Field::store_time_dec(MYSQL_TIME *ltime, uint dec) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; char buff[MAX_DATE_STRING_REP_LENGTH]; - uint length= (uint) my_TIME_to_str(ltime, buff); + uint length= (uint) my_TIME_to_str(ltime, buff, dec); /* Avoid conversion when field character set is ASCII compatible */ return store(buff, length, (charset()->state & MY_CS_NONASCII) ? &my_charset_latin1 : charset()); @@ -2766,10 +2750,10 @@ int Field_new_decimal::store_decimal(const my_decimal *decimal_value) } -int Field_new_decimal::store_time(MYSQL_TIME *ltime, timestamp_type t_type) +int Field_new_decimal::store_time_dec(MYSQL_TIME *ltime, uint dec) { - my_decimal decimal_value; - return store_value(date2my_decimal(ltime, &decimal_value)); + my_decimal decimal_value; + return store_value(date2my_decimal(ltime, &decimal_value)); } @@ -2920,13 +2904,10 @@ uint Field_new_decimal::is_equal(Create_field *new_field) @return New pointer into memory based on from + length of the data */ const uchar * -Field_new_decimal::unpack(uchar* to, - const uchar *from, - uint param_data, - bool low_byte_first) +Field_new_decimal::unpack(uchar* to, const uchar *from, uint param_data) { if (param_data == 0) - return Field::unpack(to, from, param_data, low_byte_first); + return Field::unpack(to, from, param_data); uint from_precision= (param_data & 0xff00) >> 8U; uint from_decimal= param_data & 0x00ff; @@ -2960,6 +2941,15 @@ Field_new_decimal::unpack(uchar* to, return from+len; } +int Field_num::store_time_dec(MYSQL_TIME *ltime, uint dec) +{ + longlong v= TIME_to_ulonglong(ltime); + if (ltime->neg == 0) + return store(v, true); + return store(-v, false); +} + + /**************************************************************************** ** tiny int ****************************************************************************/ @@ -3149,14 +3139,7 @@ int Field_short::store(const char *from,uint len,CHARSET_INFO *cs) error= get_int(cs, from, len, &rnd, UINT_MAX16, INT_MIN16, INT_MAX16); store_tmp= unsigned_flag ? (int) (ulonglong) rnd : (int) rnd; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - int2store(ptr, store_tmp); - } - else -#endif - shortstore(ptr, (short) store_tmp); + int2store(ptr, store_tmp); return error; } @@ -3201,14 +3184,7 @@ int Field_short::store(double nr) else res=(int16) (int) nr; } -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - int2store(ptr,res); - } - else -#endif - shortstore(ptr,res); + int2store(ptr,res); return error; } @@ -3256,14 +3232,7 @@ int Field_short::store(longlong nr, bool unsigned_val) else res=(int16) nr; } -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - int2store(ptr,res); - } - else -#endif - shortstore(ptr,res); + int2store(ptr,res); return error; } @@ -3272,12 +3241,7 @@ double Field_short::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; short j; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - j=sint2korr(ptr); - else -#endif - shortget(j,ptr); + j=sint2korr(ptr); return unsigned_flag ? (double) (unsigned short) j : (double) j; } @@ -3285,12 +3249,7 @@ longlong Field_short::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; short j; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - j=sint2korr(ptr); - else -#endif - shortget(j,ptr); + j=sint2korr(ptr); return unsigned_flag ? (longlong) (unsigned short) j : (longlong) j; } @@ -3305,12 +3264,7 @@ String *Field_short::val_str(String *val_buffer, val_buffer->alloc(mlength); char *to=(char*) val_buffer->ptr(); short j; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - j=sint2korr(ptr); - else -#endif - shortget(j,ptr); + j=sint2korr(ptr); if (unsigned_flag) length=(uint) cs->cset->long10_to_str(cs, to, mlength, 10, @@ -3334,18 +3288,8 @@ bool Field_short::send_binary(Protocol *protocol) int Field_short::cmp(const uchar *a_ptr, const uchar *b_ptr) { short a,b; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - a=sint2korr(a_ptr); - b=sint2korr(b_ptr); - } - else -#endif - { - shortget(a,a_ptr); - shortget(b,b_ptr); - } + a=sint2korr(a_ptr); + b=sint2korr(b_ptr); if (unsigned_flag) return ((unsigned short) a < (unsigned short) b) ? -1 : @@ -3355,24 +3299,11 @@ int Field_short::cmp(const uchar *a_ptr, const uchar *b_ptr) void Field_short::sort_string(uchar *to,uint length __attribute__((unused))) { -#ifdef WORDS_BIGENDIAN - if (!table->s->db_low_byte_first) - { - if (unsigned_flag) - to[0] = ptr[0]; - else - to[0] = (char) (ptr[0] ^ 128); /* Revers signbit */ - to[1] = ptr[1]; - } + if (unsigned_flag) + to[0] = ptr[1]; else -#endif - { - if (unsigned_flag) - to[0] = ptr[1]; - else - to[0] = (char) (ptr[1] ^ 128); /* Revers signbit */ - to[1] = ptr[0]; - } + to[0] = (char) (ptr[1] ^ 128); /* Revers signbit */ + to[1] = ptr[0]; } void Field_short::sql_type(String &res) const @@ -3588,14 +3519,7 @@ int Field_long::store(const char *from,uint len,CHARSET_INFO *cs) error= get_int(cs, from, len, &rnd, UINT_MAX32, INT_MIN32, INT_MAX32); store_tmp= unsigned_flag ? (long) (ulonglong) rnd : (long) rnd; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - int4store(ptr, store_tmp); - } - else -#endif - longstore(ptr, store_tmp); + int4store(ptr, store_tmp); return error; } @@ -3640,14 +3564,7 @@ int Field_long::store(double nr) if (error) set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - int4store(ptr,res); - } - else -#endif - longstore(ptr,res); + int4store(ptr,res); return error; } @@ -3693,14 +3610,7 @@ int Field_long::store(longlong nr, bool unsigned_val) if (error) set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - int4store(ptr,res); - } - else -#endif - longstore(ptr,res); + int4store(ptr,res); return error; } @@ -3709,12 +3619,7 @@ double Field_long::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; int32 j; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - j=sint4korr(ptr); - else -#endif - longget(j,ptr); + j=sint4korr(ptr); return unsigned_flag ? (double) (uint32) j : (double) j; } @@ -3724,12 +3629,7 @@ longlong Field_long::val_int(void) int32 j; /* See the comment in Field_long::store(long long) */ DBUG_ASSERT(table->in_use == current_thd); -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - j=sint4korr(ptr); - else -#endif - longget(j,ptr); + j=sint4korr(ptr); return unsigned_flag ? (longlong) (uint32) j : (longlong) j; } @@ -3743,12 +3643,7 @@ String *Field_long::val_str(String *val_buffer, val_buffer->alloc(mlength); char *to=(char*) val_buffer->ptr(); int32 j; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - j=sint4korr(ptr); - else -#endif - longget(j,ptr); + j=sint4korr(ptr); if (unsigned_flag) length=cs->cset->long10_to_str(cs,to,mlength, 10,(long) (uint32)j); @@ -3771,18 +3666,8 @@ bool Field_long::send_binary(Protocol *protocol) int Field_long::cmp(const uchar *a_ptr, const uchar *b_ptr) { int32 a,b; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - a=sint4korr(a_ptr); - b=sint4korr(b_ptr); - } - else -#endif - { - longget(a,a_ptr); - longget(b,b_ptr); - } + a=sint4korr(a_ptr); + b=sint4korr(b_ptr); if (unsigned_flag) return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0; return (a < b) ? -1 : (a > b) ? 1 : 0; @@ -3790,28 +3675,13 @@ int Field_long::cmp(const uchar *a_ptr, const uchar *b_ptr) void Field_long::sort_string(uchar *to,uint length __attribute__((unused))) { -#ifdef WORDS_BIGENDIAN - if (!table->s->db_low_byte_first) - { - if (unsigned_flag) - to[0] = ptr[0]; - else - to[0] = (char) (ptr[0] ^ 128); /* Revers signbit */ - to[1] = ptr[1]; - to[2] = ptr[2]; - to[3] = ptr[3]; - } + if (unsigned_flag) + to[0] = ptr[3]; else -#endif - { - if (unsigned_flag) - to[0] = ptr[3]; - else - to[0] = (char) (ptr[3] ^ 128); /* Revers signbit */ - to[1] = ptr[2]; - to[2] = ptr[1]; - to[3] = ptr[0]; - } + to[0] = (char) (ptr[3] ^ 128); /* Revers signbit */ + to[1] = ptr[2]; + to[2] = ptr[1]; + to[3] = ptr[0]; } @@ -3845,14 +3715,7 @@ int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs) error= 1; else error= 0; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - int8store(ptr,tmp); - } - else -#endif - longlongstore(ptr,tmp); + int8store(ptr,tmp); return error; } @@ -3860,51 +3723,15 @@ 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); -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - int8store(ptr,res); - } - else -#endif - longlongstore(ptr,res); + int8store(ptr,res); return error; } @@ -3928,14 +3755,7 @@ int Field_longlong::store(longlong nr, bool unsigned_val) } } -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - int8store(ptr,nr); - } - else -#endif - longlongstore(ptr,nr); + int8store(ptr,nr); return error; } @@ -3944,14 +3764,7 @@ double Field_longlong::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; longlong j; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - j=sint8korr(ptr); - } - else -#endif - longlongget(j,ptr); + j=sint8korr(ptr); /* The following is open coded to avoid a bug in gcc 3.3 */ if (unsigned_flag) { @@ -3966,12 +3779,7 @@ longlong Field_longlong::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; longlong j; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - j=sint8korr(ptr); - else -#endif - longlongget(j,ptr); + j=sint8korr(ptr); return j; } @@ -3985,12 +3793,7 @@ String *Field_longlong::val_str(String *val_buffer, val_buffer->alloc(mlength); char *to=(char*) val_buffer->ptr(); longlong j; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - j=sint8korr(ptr); - else -#endif - longlongget(j,ptr); + j=sint8korr(ptr); length=(uint) (cs->cset->longlong10_to_str)(cs,to,mlength, unsigned_flag ? 10 : -10, j); @@ -4012,18 +3815,8 @@ bool Field_longlong::send_binary(Protocol *protocol) int Field_longlong::cmp(const uchar *a_ptr, const uchar *b_ptr) { longlong a,b; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - a=sint8korr(a_ptr); - b=sint8korr(b_ptr); - } - else -#endif - { - longlongget(a,a_ptr); - longlongget(b,b_ptr); - } + a=sint8korr(a_ptr); + b=sint8korr(b_ptr); if (unsigned_flag) return ((ulonglong) a < (ulonglong) b) ? -1 : ((ulonglong) a > (ulonglong) b) ? 1 : 0; @@ -4032,36 +3825,17 @@ int Field_longlong::cmp(const uchar *a_ptr, const uchar *b_ptr) void Field_longlong::sort_string(uchar *to,uint length __attribute__((unused))) { -#ifdef WORDS_BIGENDIAN - if (!table->s->db_low_byte_first) - { - if (unsigned_flag) - to[0] = ptr[0]; - else - to[0] = (char) (ptr[0] ^ 128); /* Revers signbit */ - to[1] = ptr[1]; - to[2] = ptr[2]; - to[3] = ptr[3]; - to[4] = ptr[4]; - to[5] = ptr[5]; - to[6] = ptr[6]; - to[7] = ptr[7]; - } + if (unsigned_flag) + to[0] = ptr[7]; else -#endif - { - if (unsigned_flag) - to[0] = ptr[7]; - else - to[0] = (char) (ptr[7] ^ 128); /* Revers signbit */ - to[1] = ptr[6]; - to[2] = ptr[5]; - to[3] = ptr[4]; - to[4] = ptr[3]; - to[5] = ptr[2]; - to[6] = ptr[1]; - to[7] = ptr[0]; - } + to[0] = (char) (ptr[7] ^ 128); /* Revers signbit */ + to[1] = ptr[6]; + to[2] = ptr[5]; + to[3] = ptr[4]; + to[4] = ptr[3]; + to[5] = ptr[2]; + to[6] = ptr[1]; + to[7] = ptr[0]; } @@ -4078,43 +3852,6 @@ void Field_longlong::sql_type(String &res) const Floating-point numbers */ -uchar * -Field_real::pack(uchar *to, const uchar *from, - uint max_length, bool low_byte_first) -{ - DBUG_ENTER("Field_real::pack"); - DBUG_ASSERT(max_length >= pack_length()); -#ifdef WORDS_BIGENDIAN - if (low_byte_first != table->s->db_low_byte_first) - { - const uchar *dptr= from + pack_length(); - while (dptr-- > from) - *to++ = *dptr; - DBUG_RETURN(to); - } - else -#endif - DBUG_RETURN(Field::pack(to, from, max_length, low_byte_first)); -} - -const uchar * -Field_real::unpack(uchar *to, const uchar *from, - uint param_data, bool low_byte_first) -{ - DBUG_ENTER("Field_real::unpack"); -#ifdef WORDS_BIGENDIAN - if (low_byte_first != table->s->db_low_byte_first) - { - const uchar *dptr= from + pack_length(); - while (dptr-- > from) - *to++ = *dptr; - DBUG_RETURN(from + pack_length()); - } - else -#endif - DBUG_RETURN(Field::unpack(to, from, param_data, low_byte_first)); -} - /**************************************************************************** single precision float ****************************************************************************/ @@ -4139,17 +3876,21 @@ 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); - float j= (float)nr; - -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) + int error= truncate_double(&nr, field_length, + not_fixed ? NOT_FIXED_DEC : dec, + unsigned_flag, FLT_MAX); + if (error) { - float4store(ptr,j); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + if (error < 0) // Wrong double value + { + error= 1; + set_null(); + } } - else -#endif - memcpy(ptr, &j, sizeof(j)); + float j= (float)nr; + + float4store(ptr,j); return error; } @@ -4165,28 +3906,14 @@ double Field_float::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; float j; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - float4get(j,ptr); - } - else -#endif - memcpy(&j, ptr, sizeof(j)); + float4get(j,ptr); return ((double) j); } longlong Field_float::val_int(void) { float j; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - float4get(j,ptr); - } - else -#endif - memcpy(&j, ptr, sizeof(j)); + float4get(j,ptr); return (longlong) rint(j); } @@ -4197,14 +3924,7 @@ String *Field_float::val_str(String *val_buffer, ASSERT_COLUMN_MARKED_FOR_READ; DBUG_ASSERT(!zerofill || field_length <= MAX_FIELD_CHARLENGTH); float nr; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - float4get(nr,ptr); - } - else -#endif - memcpy(&nr, ptr, sizeof(nr)); + float4get(nr,ptr); uint to_length= 70; if (val_buffer->alloc(to_length)) @@ -4238,18 +3958,8 @@ String *Field_float::val_str(String *val_buffer, int Field_float::cmp(const uchar *a_ptr, const uchar *b_ptr) { float a,b; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - float4get(a,a_ptr); - float4get(b,b_ptr); - } - else -#endif - { - memcpy(&a, a_ptr, sizeof(float)); - memcpy(&b, b_ptr, sizeof(float)); - } + float4get(a,a_ptr); + float4get(b,b_ptr); return (a < b) ? -1 : (a > b) ? 1 : 0; } @@ -4258,14 +3968,7 @@ int Field_float::cmp(const uchar *a_ptr, const uchar *b_ptr) void Field_float::sort_string(uchar *to,uint length __attribute__((unused))) { float nr; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - float4get(nr,ptr); - } - else -#endif - memcpy(&nr, ptr, sizeof(float)); + float4get(nr,ptr); uchar *tmp= to; if (nr == (float) 0.0) @@ -4361,16 +4064,20 @@ 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); - -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) + int error= truncate_double(&nr, field_length, + not_fixed ? NOT_FIXED_DEC : dec, + unsigned_flag, DBL_MAX); + if (error) { - float8store(ptr,nr); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + if (error < 0) // Wrong double value + { + error= 1; + set_null(); + } } - else -#endif - doublestore(ptr,nr); + + float8store(ptr,nr); return error; } @@ -4385,28 +4092,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; @@ -4426,22 +4136,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) { @@ -4450,18 +4208,17 @@ int Field_real::store_decimal(const my_decimal *dm) return store(dbl); } +int Field_real::store_time_dec(MYSQL_TIME *ltime, uint dec) +{ + return store(TIME_to_double(ltime)); +} + + double Field_double::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; double j; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - float8get(j,ptr); - } - else -#endif - doubleget(j,ptr); + float8get(j,ptr); return j; } @@ -4470,33 +4227,13 @@ longlong Field_double::val_int(void) ASSERT_COLUMN_MARKED_FOR_READ; double j; longlong res; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - float8get(j,ptr); - } - 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); + bool error; + float8get(j,ptr); -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; - str= val_str(&tmp, 0); - ErrConvString err(str); + ErrConvDouble err(j); push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE, ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER", @@ -4514,20 +4251,22 @@ my_decimal *Field_real::val_decimal(my_decimal *decimal_value) } +bool Field_real::get_date(MYSQL_TIME *ltime,uint fuzzydate) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + double nr= val_real(); + return double_to_datetime_with_warn(nr, ltime, fuzzydate, field_name); +} + + String *Field_double::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { ASSERT_COLUMN_MARKED_FOR_READ; DBUG_ASSERT(!zerofill || field_length <= MAX_FIELD_CHARLENGTH); double nr; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - float8get(nr,ptr); - } - else -#endif - doubleget(nr,ptr); + float8get(nr,ptr); + uint to_length= DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE; if (val_buffer->alloc(to_length)) { @@ -4559,18 +4298,8 @@ bool Field_double::send_binary(Protocol *protocol) int Field_double::cmp(const uchar *a_ptr, const uchar *b_ptr) { double a,b; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - float8get(a,a_ptr); - float8get(b,b_ptr); - } - else -#endif - { - doubleget(a, a_ptr); - doubleget(b, b_ptr); - } + float8get(a,a_ptr); + float8get(b,b_ptr); return (a < b) ? -1 : (a > b) ? 1 : 0; } @@ -4582,14 +4311,7 @@ int Field_double::cmp(const uchar *a_ptr, const uchar *b_ptr) void Field_double::sort_string(uchar *to,uint length __attribute__((unused))) { double nr; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - float8get(nr,ptr); - } - else -#endif - doubleget(nr,ptr); + float8get(nr,ptr); change_double_for_sort(nr, to); } @@ -4677,12 +4399,12 @@ Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg, const char *field_name_arg, TABLE_SHARE *share, CHARSET_INFO *cs) - :Field_str(ptr_arg, MAX_DATETIME_WIDTH, null_ptr_arg, null_bit_arg, + :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, cs) { /* For 4.0 MYD and 4.0 InnoDB compatibility */ - flags|= ZEROFILL_FLAG | UNSIGNED_FLAG | BINARY_FLAG; - if (!share->timestamp_field && unireg_check != NONE) + flags|= UNSIGNED_FLAG | BINARY_FLAG; + if (unireg_check != NONE && !share->timestamp_field) { /* This timestamp has auto-update */ share->timestamp_field= this; @@ -4693,20 +4415,6 @@ Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg, } -Field_timestamp::Field_timestamp(bool maybe_null_arg, - const char *field_name_arg, - CHARSET_INFO *cs) - :Field_str((uchar*) 0, MAX_DATETIME_WIDTH, - maybe_null_arg ? (uchar*) "": 0, 0, - NONE, field_name_arg, cs) -{ - /* For 4.0 MYD and 4.0 InnoDB compatibility */ - flags|= ZEROFILL_FLAG | UNSIGNED_FLAG | BINARY_FLAG; - if (unireg_check != TIMESTAMP_DN_FIELD) - flags|= ON_UPDATE_NOW_FLAG; -} - - /** Get auto-set type for TIMESTAMP field. @@ -4741,175 +4449,150 @@ timestamp_auto_set_type Field_timestamp::get_auto_set_type() const } } +my_time_t Field_timestamp::get_timestamp(ulong *sec_part) const +{ + ASSERT_COLUMN_MARKED_FOR_READ; + *sec_part= 0; + return sint4korr(ptr); +} + -int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs) +int Field_timestamp::store_TIME_with_warning(THD *thd, MYSQL_TIME *l_time, + const ErrConv *str, + bool was_cut, + bool have_smth_to_conv) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; - MYSQL_TIME l_time; - my_time_t tmp= 0; - int error; - bool have_smth_to_conv; - my_bool in_dst_time_gap; - THD *thd= table ? table->in_use : current_thd; - - /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ - have_smth_to_conv= (str_to_datetime(cs, from, len, &l_time, - (thd->variables.sql_mode & - MODE_NO_ZERO_DATE) | - MODE_NO_ZERO_IN_DATE, &error) > - MYSQL_TIMESTAMP_ERROR); + uint error = 0; + my_time_t timestamp; - if (error || !have_smth_to_conv) + if (was_cut || !have_smth_to_conv) { error= 1; set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, - from, len, MYSQL_TIMESTAMP_DATETIME, 1); + str, MYSQL_TIMESTAMP_DATETIME, 1); } - /* Only convert a correct date (not a zero date) */ - if (have_smth_to_conv && l_time.month) + if (have_smth_to_conv && l_time->month) { - if (!(tmp= TIME_to_timestamp(thd, &l_time, &in_dst_time_gap))) - { - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_DATA_OUT_OF_RANGE, - from, len, MYSQL_TIMESTAMP_DATETIME, !error); - error= 1; - } - else if (in_dst_time_gap) + uint conversion_error; + timestamp= TIME_to_timestamp(thd, l_time, &conversion_error); + if (timestamp == 0 && l_time->second_part == 0) + conversion_error= ER_WARN_DATA_OUT_OF_RANGE; + if (conversion_error) { - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_INVALID_TIMESTAMP, - from, len, MYSQL_TIMESTAMP_DATETIME, !error); + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, conversion_error, + str, MYSQL_TIMESTAMP_DATETIME, !error); error= 1; } } - store_timestamp(tmp); + else + { + timestamp= 0; + l_time->second_part= 0; + } + store_TIME(timestamp, l_time->second_part); return error; } -int Field_timestamp::store(double nr) +int Field_timestamp::store_time_dec(MYSQL_TIME *ltime, uint dec) { - int error= 0; - if (nr < 0 || nr > 99991231235959.0) - { - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_DATA_OUT_OF_RANGE, - nr, MYSQL_TIMESTAMP_DATETIME); - nr= 0; // Avoid overflow on buff - error= 1; - } - error|= Field_timestamp::store((longlong) rint(nr), FALSE); - return error; + THD *thd= table->in_use; + int unused; + MYSQL_TIME l_time= *ltime; + ErrConvTime str(ltime); + bool valid= !check_date(&l_time, pack_time(&l_time) != 0, + (thd->variables.sql_mode & MODE_NO_ZERO_DATE) | + MODE_NO_ZERO_IN_DATE, &unused); + + return store_TIME_with_warning(thd, &l_time, &str, false, valid); } -int Field_timestamp::store(longlong nr, bool unsigned_val) +int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; MYSQL_TIME l_time; - my_time_t timestamp= 0; int error; - my_bool in_dst_time_gap; - THD *thd= table ? table->in_use : current_thd; + int have_smth_to_conv; + ErrConvString str(from, len, cs); + THD *thd= table->in_use; /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ - longlong tmp= number_to_datetime(nr, &l_time, (thd->variables.sql_mode & + have_smth_to_conv= (str_to_datetime(cs, from, len, &l_time, + (thd->variables.sql_mode & + MODE_NO_ZERO_DATE) | + MODE_NO_ZERO_IN_DATE, &error) > + MYSQL_TIMESTAMP_ERROR); + return store_TIME_with_warning(thd, &l_time, &str, error, have_smth_to_conv); +} + + +int Field_timestamp::store(double nr) +{ + MYSQL_TIME l_time; + int error; + ErrConvDouble str(nr); + THD *thd= table->in_use; + + longlong tmp= double_to_datetime(nr, &l_time, (thd->variables.sql_mode & MODE_NO_ZERO_DATE) | MODE_NO_ZERO_IN_DATE, &error); - if (tmp == LL(-1)) - { - error= 2; - } + return store_TIME_with_warning(thd, &l_time, &str, error, tmp != -1); +} - if (!error && tmp) - { - if (!(timestamp= TIME_to_timestamp(thd, &l_time, &in_dst_time_gap))) - { - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_DATA_OUT_OF_RANGE, - nr, MYSQL_TIMESTAMP_DATETIME, 1); - error= 1; - } - if (in_dst_time_gap) - { - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_INVALID_TIMESTAMP, - nr, MYSQL_TIMESTAMP_DATETIME, 1); - error= 1; - } - } else if (error) - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - WARN_DATA_TRUNCATED, - nr, MYSQL_TIMESTAMP_DATETIME, 1); - store_timestamp(timestamp); - return error; +int Field_timestamp::store(longlong nr, bool unsigned_val) +{ + MYSQL_TIME l_time; + int error; + ErrConvInteger str(nr); + THD *thd= table->in_use; + + /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ + longlong tmp= number_to_datetime(nr, 0, &l_time, (thd->variables.sql_mode & + MODE_NO_ZERO_DATE) | + MODE_NO_ZERO_IN_DATE, &error); + return store_TIME_with_warning(thd, &l_time, &str, error, tmp != LL(-1)); } + double Field_timestamp::val_real(void) { - ASSERT_COLUMN_MARKED_FOR_READ; return (double) Field_timestamp::val_int(); } + longlong Field_timestamp::val_int(void) { - ASSERT_COLUMN_MARKED_FOR_READ; - uint32 temp; - MYSQL_TIME time_tmp; - THD *thd= table ? table->in_use : current_thd; - - thd->time_zone_used= 1; -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) - temp=uint4korr(ptr); - else -#endif - longget(temp,ptr); + MYSQL_TIME ltime; + if (get_date(<ime, TIME_NO_ZERO_DATE)) + return 0; - if (temp == 0L) // No time - return(0); /* purecov: inspected */ - - thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, (my_time_t)temp); - - return time_tmp.year * LL(10000000000) + time_tmp.month * LL(100000000) + - time_tmp.day * 1000000L + time_tmp.hour * 10000L + - time_tmp.minute * 100 + time_tmp.second; + return ltime.year * 10000000000LL + ltime.month * 100000000LL + + ltime.day * 1000000L + ltime.hour * 10000L + + ltime.minute * 100 + ltime.second; } String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) { - ASSERT_COLUMN_MARKED_FOR_READ; + MYSQL_TIME ltime; uint32 temp, temp2; - MYSQL_TIME time_tmp; - THD *thd= table ? table->in_use : current_thd; char *to; val_buffer->alloc(field_length+1); to= (char*) val_buffer->ptr(); val_buffer->length(field_length); - thd->time_zone_used= 1; -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) - temp=uint4korr(ptr); - else -#endif - longget(temp,ptr); - - if (temp == 0L) + if (get_date(<ime, TIME_NO_ZERO_DATE)) { /* Zero time is "000000" */ - val_ptr->set(STRING_WITH_LEN("0000-00-00 00:00:00"), &my_charset_numeric); + val_ptr->set(zero_timestamp, field_length, &my_charset_numeric); return val_ptr; } val_buffer->set_charset(&my_charset_numeric); // Safety - - thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,(my_time_t)temp); - - temp= time_tmp.year % 100; + + temp= ltime.year % 100; if (temp < YY_PART_YEAR - 1) { *to++= '2'; @@ -4924,27 +4607,27 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) *to++= (char) ('0'+(char) (temp2)); *to++= (char) ('0'+(char) (temp)); *to++= '-'; - temp=time_tmp.month; + temp=ltime.month; temp2=temp/10; temp=temp-temp2*10; *to++= (char) ('0'+(char) (temp2)); *to++= (char) ('0'+(char) (temp)); *to++= '-'; - temp=time_tmp.day; + temp=ltime.day; temp2=temp/10; temp=temp-temp2*10; *to++= (char) ('0'+(char) (temp2)); *to++= (char) ('0'+(char) (temp)); *to++= ' '; - temp=time_tmp.hour; + temp=ltime.hour; temp2=temp/10; temp=temp-temp2*10; *to++= (char) ('0'+(char) (temp2)); *to++= (char) ('0'+(char) (temp)); *to++= ':'; - temp=time_tmp.minute; + temp=ltime.minute; temp2=temp/10; temp=temp-temp2*10; *to++= (char) ('0'+(char) (temp2)); *to++= (char) ('0'+(char) (temp)); *to++= ':'; - temp=time_tmp.second; + temp=ltime.second; temp2=temp/10; temp=temp-temp2*10; *to++= (char) ('0'+(char) (temp2)); *to++= (char) ('0'+(char) (temp)); @@ -4956,16 +4639,11 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) bool Field_timestamp::get_date(MYSQL_TIME *ltime, uint fuzzydate) { - long temp; - THD *thd= table ? table->in_use : current_thd; + THD *thd= table->in_use; thd->time_zone_used= 1; -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) - temp=uint4korr(ptr); - else -#endif - longget(temp,ptr); - if (temp == 0L) + ulong sec_part; + my_time_t temp= get_timestamp(&sec_part); + if (temp == 0 && sec_part == 0) { /* Zero time is "000000" */ if (fuzzydate & TIME_NO_ZERO_DATE) return 1; @@ -4974,61 +4652,35 @@ bool Field_timestamp::get_date(MYSQL_TIME *ltime, uint fuzzydate) else { thd->variables.time_zone->gmt_sec_to_TIME(ltime, (my_time_t)temp); + ltime->second_part= sec_part; } return 0; } -bool Field_timestamp::get_time(MYSQL_TIME *ltime) -{ - return Field_timestamp::get_date(ltime,0); -} - bool Field_timestamp::send_binary(Protocol *protocol) { - MYSQL_TIME tm; - Field_timestamp::get_date(&tm, 0); - return protocol->store(&tm); + MYSQL_TIME ltime; + Field_timestamp::get_date(<ime, 0); + return protocol->store(<ime, 0); } int Field_timestamp::cmp(const uchar *a_ptr, const uchar *b_ptr) { int32 a,b; -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) - { - a=sint4korr(a_ptr); - b=sint4korr(b_ptr); - } - else -#endif - { - longget(a,a_ptr); - longget(b,b_ptr); - } + a=sint4korr(a_ptr); + b=sint4korr(b_ptr); return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0; } void Field_timestamp::sort_string(uchar *to,uint length __attribute__((unused))) { -#ifdef WORDS_BIGENDIAN - if (!table || !table->s->db_low_byte_first) - { - to[0] = ptr[0]; - to[1] = ptr[1]; - to[2] = ptr[2]; - to[3] = ptr[3]; - } - else -#endif - { - to[0] = ptr[3]; - to[1] = ptr[2]; - to[2] = ptr[1]; - to[3] = ptr[0]; - } + to[0] = ptr[3]; + to[1] = ptr[2]; + to[2] = ptr[1]; + to[3] = ptr[0]; } @@ -5038,149 +4690,442 @@ void Field_timestamp::sql_type(String &res) const } -void Field_timestamp::set_time() +int Field_timestamp::set_time() { - THD *thd= table ? table->in_use : current_thd; - long tmp= (long) thd->query_start(); + THD *thd= table->in_use; set_notnull(); - store_timestamp(tmp); + store_TIME(thd->query_start(), 0); + return 0; } -/**************************************************************************** -** time type -** In string context: HH:MM:SS -** In number context: HHMMSS -** Stored as a 3 byte unsigned int -****************************************************************************/ +void Field_timestamp_hires::sql_type(String &res) const +{ + CHARSET_INFO *cs=res.charset(); + res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(), + "timestamp(%u)", dec)); +} -int Field_time::store(const char *from,uint len,CHARSET_INFO *cs) +#ifdef NOT_USED +static void store_native(ulonglong num, uchar *to, uint bytes) +{ + switch(bytes) { + case 1: *to= (uchar)num; break; + case 2: shortstore(to, (ushort)num); break; + case 3: int3store(to, num); /* Sic!*/ break; + case 4: longstore(to, (ulong)num); break; + case 8: longlongstore(to, num); break; + default: DBUG_ASSERT(0); + } +} + +static longlong read_native(const uchar *from, uint bytes) +{ + switch(bytes) { + case 1: return from[0]; + case 2: { uint16 tmp; shortget(tmp, from); return tmp; } + case 3: return uint3korr(from); + case 4: { uint32 tmp; longget(tmp, from); return tmp; } + case 8: { longlong tmp; longlongget(tmp, from); return tmp; } + default: DBUG_ASSERT(0); return 0; + } +} +#endif + +static void store_lowendian(ulonglong num, uchar *to, uint bytes) +{ + switch(bytes) { + case 1: *to= (uchar)num; break; + case 2: int2store(to, num); break; + case 3: int3store(to, num); break; + case 4: int4store(to, num); break; + case 8: int8store(to, num); break; + default: DBUG_ASSERT(0); + } +} + +static longlong read_lowendian(const uchar *from, uint bytes) +{ + switch(bytes) { + case 1: return from[0]; + case 2: return uint2korr(from); + case 3: return uint3korr(from); + case 4: return uint4korr(from); + case 8: return sint8korr(from); + default: DBUG_ASSERT(0); return 0; + } +} + +static void store_bigendian(ulonglong num, uchar *to, uint bytes) +{ + switch(bytes) { + case 1: mi_int1store(to, num); break; + case 2: mi_int2store(to, num); break; + case 3: mi_int3store(to, num); break; + case 4: mi_int4store(to, num); break; + case 5: mi_int5store(to, num); break; + case 6: mi_int6store(to, num); break; + case 7: mi_int7store(to, num); break; + case 8: mi_int8store(to, num); break; + default: DBUG_ASSERT(0); + } +} + +static longlong read_bigendian(const uchar *from, uint bytes) +{ + switch(bytes) { + case 1: return mi_uint1korr(from); + case 2: return mi_uint2korr(from); + case 3: return mi_uint3korr(from); + case 4: return mi_uint4korr(from); + case 5: return mi_uint5korr(from); + case 6: return mi_uint6korr(from); + case 7: return mi_uint7korr(from); + case 8: return mi_sint8korr(from); + default: DBUG_ASSERT(0); return 0; + } +} + +void Field_timestamp_hires::store_TIME(my_time_t timestamp, ulong sec_part) +{ + mi_int4store(ptr, timestamp); + store_bigendian(sec_part_shift(sec_part, dec), ptr+4, sec_part_bytes[dec]); +} + +my_time_t Field_timestamp_hires::get_timestamp(ulong *sec_part) const +{ + ASSERT_COLUMN_MARKED_FOR_READ; + *sec_part= (long)sec_part_unshift(read_bigendian(ptr+4, sec_part_bytes[dec]), dec); + return mi_uint4korr(ptr); +} + +double Field_timestamp_hires::val_real(void) { MYSQL_TIME ltime; - long tmp; - int error= 0; - int warning; + if (get_date(<ime, TIME_NO_ZERO_DATE)) + return 0; + + return ltime.year * 1e10 + ltime.month * 1e8 + + ltime.day * 1e6 + ltime.hour * 1e4 + + ltime.minute * 1e2 + ltime.second + ltime.second_part*1e-6; +} + +String *Field_timestamp_hires::val_str(String *val_buffer, String *val_ptr) +{ + String *tmp= Field_timestamp::val_str(val_buffer, val_ptr); + ulong sec_part= (ulong)read_bigendian(ptr+4, sec_part_bytes[dec]); + + if (tmp->ptr() == zero_timestamp) + return tmp; - if (str_to_time(cs, from, len, <ime, &warning)) + char *buf= const_cast<char*>(tmp->ptr() + MAX_DATETIME_WIDTH); + for (int i=dec; i>0; i--, sec_part/=10) + buf[i]= (char)(sec_part % 10) + '0'; + buf[0]= '.'; + buf[dec+1]= 0; + return tmp; +} + + +my_decimal *Field_timestamp_hires::val_decimal(my_decimal *d) +{ + MYSQL_TIME ltime; + get_date(<ime, 0); + longlong intg= TIME_to_ulonglong(<ime); + return seconds2my_decimal(ltime.neg, intg, ltime.second_part, d); +} + +int Field_timestamp_hires::store_decimal(const my_decimal *d) +{ + ulonglong nr; + ulong sec_part; + int error; + MYSQL_TIME ltime; + longlong tmp; + THD *thd= table->in_use; + ErrConvDecimal str(d); + + if (my_decimal2seconds(d, &nr, &sec_part)) { - tmp=0L; + tmp= -1; error= 2; - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, - from, len, MYSQL_TIMESTAMP_TIME, 1); } else - { - if (warning & MYSQL_TIME_WARN_TRUNCATED) - { - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - WARN_DATA_TRUNCATED, - from, len, MYSQL_TIMESTAMP_TIME, 1); - error= 1; - } - if (warning & MYSQL_TIME_WARN_OUT_OF_RANGE) - { - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_DATA_OUT_OF_RANGE, - from, len, MYSQL_TIMESTAMP_TIME, !error); - error= 1; - } - if (ltime.month) - ltime.day=0; - tmp=(ltime.day*24L+ltime.hour)*10000L+(ltime.minute*100+ltime.second); - } - - if (ltime.neg) - tmp= -tmp; - int3store(ptr,tmp); - return error; + tmp= number_to_datetime(nr, sec_part, <ime, TIME_NO_ZERO_IN_DATE | + (thd->variables.sql_mode & + MODE_NO_ZERO_DATE), &error); + + return store_TIME_with_warning(thd, <ime, &str, error, tmp != -1); } +int Field_timestamp_hires::set_time() +{ + THD *thd= table->in_use; + set_notnull(); + store_TIME(thd->query_start(), thd->query_start_sec_part()); + return 0; +} -int Field_time::store_time(MYSQL_TIME *ltime, timestamp_type time_type) +bool Field_timestamp_hires::send_binary(Protocol *protocol) { - long tmp= ((ltime->month ? 0 : ltime->day * 24L) + ltime->hour) * 10000L + - (ltime->minute * 100 + ltime->second); - if (ltime->neg) - tmp= -tmp; - return Field_time::store((longlong) tmp, FALSE); + MYSQL_TIME ltime; + Field_timestamp::get_date(<ime, 0); + return protocol->store(<ime, dec); } -int Field_time::store(double nr) +int Field_timestamp_hires::cmp(const uchar *a_ptr, const uchar *b_ptr) +{ + int32 a,b; + ulong a_sec_part, b_sec_part; + a= mi_uint4korr(a_ptr); + a_sec_part= (ulong)read_bigendian(a_ptr+4, sec_part_bytes[dec]); + b= mi_uint4korr(b_ptr); + b_sec_part= (ulong)read_bigendian(b_ptr+4, sec_part_bytes[dec]); + return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : + a_sec_part < b_sec_part ? -1 : a_sec_part > b_sec_part ? 1 : 0; +} + + +void Field_timestamp_hires::sort_string(uchar *to,uint length) +{ + DBUG_ASSERT(length == Field_timestamp_hires::pack_length()); + memcpy(to, ptr, length); +} + +uint32 Field_timestamp_hires::pack_length() const +{ + return 4 + sec_part_bytes[dec]; +} + +void Field_timestamp_hires::make_field(Send_field *field) +{ + Field::make_field(field); + field->decimals= dec; +} + +/* + Store string into a date/time field + + RETURN + 0 ok + 1 Value was cut during conversion + 2 value was out of range + 3 Datetime value that was cut (warning level NOTE) + This is used by opt_range.cc:get_mm_leaf(). +*/ +int Field_temporal::store_TIME_with_warning(MYSQL_TIME *ltime, + const ErrConv *str, + int was_cut, int have_smth_to_conv) { + MYSQL_ERROR::enum_warning_level trunc_level= MYSQL_ERROR::WARN_LEVEL_WARN; + int ret= 2; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; - long tmp; - int error= 0; - if (nr > (double)TIME_MAX_VALUE) + + if (was_cut == 0 && + have_smth_to_conv == 0 && + mysql_type_to_time_type(type()) != MYSQL_TIMESTAMP_TIME) // special case: zero date + was_cut= MYSQL_TIME_WARN_OUT_OF_RANGE; + else + if (!have_smth_to_conv) { - tmp= TIME_MAX_VALUE; - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_TIME); - error= 1; + bzero(ltime, sizeof(*ltime)); + was_cut= MYSQL_TIME_WARN_TRUNCATED; + ret= 1; } - else if (nr < (double)-TIME_MAX_VALUE) + else if (!(was_cut & MYSQL_TIME_WARN_TRUNCATED) && + mysql_type_to_time_type(type()) == MYSQL_TIMESTAMP_DATE && + (ltime->hour || ltime->minute || ltime->second || ltime->second_part)) { - tmp= -TIME_MAX_VALUE; - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_TIME); - error= 1; + trunc_level= MYSQL_ERROR::WARN_LEVEL_NOTE; + was_cut|= MYSQL_TIME_WARN_TRUNCATED; + ret= 3; } - else + else if (!(was_cut & MYSQL_TIME_WARN_TRUNCATED) && + mysql_type_to_time_type(type()) == MYSQL_TIMESTAMP_TIME && + (ltime->year || ltime->month)) { - tmp=(long) floor(fabs(nr)); // Remove fractions - if (nr < 0) - tmp= -tmp; - if (tmp % 100 > 59 || tmp/100 % 100 > 59) - { - tmp=0; - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_DATA_OUT_OF_RANGE, nr, - MYSQL_TIMESTAMP_TIME); - error= 1; - } + ltime->year= ltime->month= ltime->day= 0; + trunc_level= MYSQL_ERROR::WARN_LEVEL_NOTE; + was_cut|= MYSQL_TIME_WARN_TRUNCATED; + ret= 3; } - int3store(ptr,tmp); - return error; + + /* + error code logic: + MYSQL_TIME_WARN_TRUNCATED means that the value was not a date/time at all. + it will be stored as zero date/time. + MYSQL_TIME_WARN_OUT_OF_RANGE means that the value was a date/time, + that is, it was parsed as such, but the value was invalid. + + Also, MYSQL_TIME_WARN_TRUNCATED is used when storing a DATETIME in + a DATE field and non-zero time part is thrown away. + */ + if (was_cut & MYSQL_TIME_WARN_TRUNCATED) + set_datetime_warning(trunc_level, WARN_DATA_TRUNCATED, + str, mysql_type_to_time_type(type()), 1); + if (was_cut & MYSQL_TIME_WARN_OUT_OF_RANGE) + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, + str, mysql_type_to_time_type(type()), 1); + + store_TIME(ltime); + return was_cut ? ret : 0; } -int Field_time::store(longlong nr, bool unsigned_val) +int Field_temporal::store(const char *from,uint len,CHARSET_INFO *cs) +{ + MYSQL_TIME ltime; + int error; + enum enum_mysql_timestamp_type func_res; + THD *thd= table->in_use; + ErrConvString str(from, len, cs); + + func_res= str_to_datetime(cs, from, len, <ime, + (TIME_FUZZY_DATE | + (thd->variables.sql_mode & + (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | + MODE_INVALID_DATES))), + &error); + return store_TIME_with_warning(<ime, &str, error, func_res > MYSQL_TIMESTAMP_ERROR); +} + + +int Field_temporal::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; - long tmp; int error= 0; - if (nr < (longlong) -TIME_MAX_VALUE && !unsigned_val) - { - tmp= -TIME_MAX_VALUE; - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_DATA_OUT_OF_RANGE, nr, - MYSQL_TIMESTAMP_TIME, 1); - error= 1; - } - else if (nr > (longlong) TIME_MAX_VALUE || (nr < 0 && unsigned_val)) - { - tmp= TIME_MAX_VALUE; - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_DATA_OUT_OF_RANGE, nr, - MYSQL_TIMESTAMP_TIME, 1); - error= 1; - } - else + MYSQL_TIME ltime; + THD *thd= table->in_use; + ErrConvDouble str(nr); + + longlong tmp= double_to_datetime(nr, <ime, + (TIME_FUZZY_DATE | + (thd->variables.sql_mode & + (MODE_NO_ZERO_IN_DATE | + MODE_NO_ZERO_DATE | + MODE_INVALID_DATES))), &error); + return store_TIME_with_warning(<ime, &str, error, tmp != -1); +} + + +int Field_temporal::store(longlong nr, bool unsigned_val) +{ + int error; + MYSQL_TIME ltime; + longlong tmp; + THD *thd= table->in_use; + ErrConvInteger str(nr); + + tmp= number_to_datetime(nr, 0, <ime, (TIME_FUZZY_DATE | + (thd->variables.sql_mode & + (MODE_NO_ZERO_IN_DATE | + MODE_NO_ZERO_DATE | + MODE_INVALID_DATES))), &error); + + return store_TIME_with_warning(<ime, &str, error, tmp != -1); +} + + +int Field_temporal::store_time_dec(MYSQL_TIME *ltime, uint dec) +{ + int error = 0, have_smth_to_conv= 1; + MYSQL_TIME l_time= *ltime; + ErrConvTime str(ltime); + /* + We don't perform range checking here since values stored in TIME + structure always fit into DATETIME range. + */ + have_smth_to_conv= !check_date(&l_time, pack_time(&l_time) != 0, + (TIME_FUZZY_DATE | + (current_thd->variables.sql_mode & + (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | + MODE_INVALID_DATES))), &error); + return store_TIME_with_warning(&l_time, &str, error, have_smth_to_conv); +} + +my_decimal *Field_temporal::val_decimal(my_decimal *d) +{ + MYSQL_TIME ltime; + if (get_date(<ime, TIME_FUZZY_DATE)) { - tmp=(long) nr; - if (tmp % 100 > 59 || tmp/100 % 100 > 59) - { - tmp=0; - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_DATA_OUT_OF_RANGE, nr, - MYSQL_TIMESTAMP_TIME, 1); - error= 1; - } + bzero(<ime, sizeof(ltime)); + ltime.time_type= mysql_type_to_time_type(type()); } + longlong intg= TIME_to_ulonglong(<ime); + return seconds2my_decimal(ltime.neg, intg, ltime.second_part, d); +} + +/**************************************************************************** +** time type +** In string context: HH:MM:SS +** In number context: HHMMSS +** Stored as a 3 byte unsigned int +****************************************************************************/ + +void Field_time::store_TIME(MYSQL_TIME *ltime) +{ + long tmp= (ltime->day*24L+ltime->hour)*10000L + + (ltime->minute*100+ltime->second); + if (ltime->neg) + tmp= -tmp; int3store(ptr,tmp); - return error; +} + +int Field_time::store(const char *from,uint len,CHARSET_INFO *cs) +{ + MYSQL_TIME ltime; + ErrConvString str(from, len, cs); + int was_cut; + int have_smth_to_conv= + str_to_time(cs, from, len, <ime, + table->in_use->variables.sql_mode & + (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | + MODE_INVALID_DATES), + &was_cut) > MYSQL_TIMESTAMP_ERROR; + + return store_TIME_with_warning(<ime, &str, was_cut, have_smth_to_conv); +} + + +int Field_time::store_time_dec(MYSQL_TIME *ltime, uint dec) +{ + MYSQL_TIME l_time= *ltime; + ErrConvTime str(ltime); + int was_cut= 0; + + int have_smth_to_conv= !check_time_range(&l_time, decimals(), &was_cut); + return store_TIME_with_warning(&l_time, &str, was_cut, have_smth_to_conv); +} + + +int Field_time::store(double nr) +{ + MYSQL_TIME ltime; + ErrConvDouble str(nr); + int was_cut; + bool neg= nr < 0; + if (neg) + nr= -nr; + int have_smth_to_conv= !number_to_time(neg, (longlong)nr, + (ulong)((nr - floor(nr)) * TIME_SECOND_PART_FACTOR), + <ime, &was_cut); + + return store_TIME_with_warning(<ime, &str, was_cut, have_smth_to_conv); } +int Field_time::store(longlong nr, bool unsigned_val) +{ + MYSQL_TIME ltime; + ErrConvInteger str(nr); + int was_cut; + int have_smth_to_conv= !number_to_time(nr < 0, nr < 0 ? -nr : nr, + 0, <ime, &was_cut); + + return store_TIME_with_warning(<ime, &str, was_cut, have_smth_to_conv); +} + + double Field_time::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; @@ -5206,7 +5151,6 @@ String *Field_time::val_str(String *val_buffer, { ASSERT_COLUMN_MARKED_FOR_READ; MYSQL_TIME ltime; - val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH); long tmp=(long) sint3korr(ptr); ltime.neg= 0; if (tmp < 0) @@ -5214,12 +5158,19 @@ String *Field_time::val_str(String *val_buffer, tmp= -tmp; ltime.neg= 1; } + ltime.year= ltime.month= 0; ltime.day= (uint) 0; ltime.hour= (uint) (tmp/10000); ltime.minute= (uint) (tmp/100 % 100); ltime.second= (uint) (tmp % 100); - make_time((DATE_TIME_FORMAT*) 0, <ime, val_buffer); + ltime.second_part= 0; + + val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH); + uint length= (uint) my_time_to_str(<ime, + const_cast<char*>(val_buffer->ptr()), 0); + val_buffer->length(length); val_buffer->set_charset(&my_charset_numeric); + return val_buffer; } @@ -5233,8 +5184,8 @@ String *Field_time::val_str(String *val_buffer, bool Field_time::get_date(MYSQL_TIME *ltime, uint fuzzydate) { - THD *thd= table ? table->in_use : current_thd; - if (!(fuzzydate & TIME_FUZZY_DATE)) + THD *thd= table->in_use; + if (!(fuzzydate & (TIME_FUZZY_DATE|TIME_TIME_ONLY))) { push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, @@ -5242,12 +5193,6 @@ bool Field_time::get_date(MYSQL_TIME *ltime, uint fuzzydate) thd->warning_info->current_row_for_warning()); return 1; } - return Field_time::get_time(ltime); -} - - -bool Field_time::get_time(MYSQL_TIME *ltime) -{ long tmp=(long) sint3korr(ptr); ltime->neg=0; if (tmp < 0) @@ -5268,11 +5213,9 @@ bool Field_time::get_time(MYSQL_TIME *ltime) bool Field_time::send_binary(Protocol *protocol) { - MYSQL_TIME tm; - Field_time::get_time(&tm); - tm.day= tm.hour/24; // Move hours to days - tm.hour-= tm.day*24; - return protocol->store_time(&tm); + MYSQL_TIME ltime; + Field_time::get_date(<ime, TIME_TIME_ONLY); + return protocol->store_time(<ime, 0); } @@ -5296,6 +5239,121 @@ void Field_time::sql_type(String &res) const res.set_ascii(STRING_WITH_LEN("time")); } +int Field_time_hires::reset() +{ + store_bigendian(zero_point, ptr, Field_time_hires::pack_length()); + return 0; +} + + +void Field_time_hires::store_TIME(MYSQL_TIME *ltime) +{ + ulonglong packed= sec_part_shift(pack_time(ltime), dec) + zero_point; + store_bigendian(packed, ptr, Field_time_hires::pack_length()); +} + +int Field_time_hires::store_decimal(const my_decimal *d) +{ + ulonglong nr; + ulong sec_part; + ErrConvDecimal str(d); + MYSQL_TIME ltime; + int was_cut; + bool neg= my_decimal2seconds(d, &nr, &sec_part); + + int have_smth_to_conv= !number_to_time(neg, nr, sec_part, <ime, &was_cut); + + return store_TIME_with_warning(<ime, &str, was_cut, have_smth_to_conv); +} + +uint32 Field_time_hires::pack_length() const +{ + return time_hires_bytes[dec]; +} + +longlong Field_time_hires::val_int(void) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + MYSQL_TIME ltime; + Field_time_hires::get_date(<ime, TIME_TIME_ONLY); + longlong val= TIME_to_ulonglong_time(<ime); + return ltime.neg ? -val : val; +} + +double Field_time_hires::val_real(void) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + MYSQL_TIME ltime; + Field_time_hires::get_date(<ime, TIME_TIME_ONLY); + return TIME_to_double(<ime); +} + +String *Field_time_hires::val_str(String *str, + String *unused __attribute__((unused))) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + MYSQL_TIME ltime; + Field_time_hires::get_date(<ime, TIME_TIME_ONLY); + str->alloc(field_length+1); + str->length(my_time_to_str(<ime, (char*) str->ptr(), dec)); + str->set_charset(&my_charset_bin); + return str; +} + +bool Field_time_hires::get_date(MYSQL_TIME *ltime, uint fuzzydate) +{ + uint32 len= pack_length(); + longlong packed= read_bigendian(ptr, len); + + packed= sec_part_unshift(packed - zero_point, dec); + + unpack_time(packed, ltime); + /* + unpack_time() returns MYSQL_TIMESTAMP_DATETIME. + To get MYSQL_TIMESTAMP_TIME we need few adjustments + */ + ltime->time_type= MYSQL_TIMESTAMP_TIME; + ltime->hour+= (ltime->month*32+ltime->day)*24; + ltime->month= ltime->day= 0; + return fuzzydate & (TIME_FUZZY_DATE | TIME_TIME_ONLY) ? 0 : 1; +} + + +bool Field_time_hires::send_binary(Protocol *protocol) +{ + MYSQL_TIME ltime; + Field_time_hires::get_date(<ime, TIME_TIME_ONLY); + return protocol->store_time(<ime, dec); +} + + +int Field_time_hires::cmp(const uchar *a_ptr, const uchar *b_ptr) +{ + ulonglong a=read_bigendian(a_ptr, Field_time_hires::pack_length()); + ulonglong b=read_bigendian(b_ptr, Field_time_hires::pack_length()); + return (a < b) ? -1 : (a > b) ? 1 : 0; +} + +void Field_time_hires::sort_string(uchar *to,uint length __attribute__((unused))) +{ + DBUG_ASSERT(length == Field_time_hires::pack_length()); + memcpy(to, ptr, length); + to[0]^= 128; +} + +void Field_time_hires::sql_type(String &res) const +{ + CHARSET_INFO *cs=res.charset(); + res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(), + "time(%u)", dec)); +} + +void Field_time_hires::make_field(Send_field *field) +{ + Field::make_field(field); + field->decimals= dec; +} + /**************************************************************************** ** year type ** Save in a byte the year 0, 1901->2155 @@ -5371,6 +5429,17 @@ int Field_year::store(longlong nr, bool unsigned_val) } +int Field_year::store_time_dec(MYSQL_TIME *ltime, uint dec) +{ + ErrConvTime str(ltime); + if (Field_year::store(ltime->year, 0)) + return 1; + + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, + &str, ltime->time_type, 1); + return 0; +} + bool Field_year::send_binary(Protocol *protocol) { ASSERT_COLUMN_MARKED_FOR_READ; @@ -5411,6 +5480,15 @@ String *Field_year::val_str(String *val_buffer, } +bool Field_year::get_date(MYSQL_TIME *ltime,uint fuzzydate) +{ + int tmp= (int) ptr[0]; + if (tmp || field_length != 4) + tmp+= 1900; + return int_to_datetime_with_warn(tmp * 10000, ltime, fuzzydate, field_name); +} + + void Field_year::sql_type(String &res) const { CHARSET_INFO *cs=res.charset(); @@ -5426,102 +5504,12 @@ void Field_year::sql_type(String &res) const ** Stored as a 4 byte unsigned int ****************************************************************************/ -int Field_date::store(const char *from, uint len,CHARSET_INFO *cs) +void Field_date::store_TIME(MYSQL_TIME *ltime) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; - MYSQL_TIME l_time; - uint32 tmp; - int error; - THD *thd= table ? table->in_use : current_thd; - - if (str_to_datetime(cs, from, len, &l_time, TIME_FUZZY_DATE | - (thd->variables.sql_mode & - (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | - MODE_INVALID_DATES)), - &error) <= MYSQL_TIMESTAMP_ERROR) - { - tmp= 0; - error= 2; - } - else - tmp=(uint32) l_time.year*10000L + (uint32) (l_time.month*100+l_time.day); - - if (error) - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, - from, len, MYSQL_TIMESTAMP_DATE, 1); - -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) - { - int4store(ptr,tmp); - } - else -#endif - longstore(ptr,tmp); - return error; + uint tmp= ltime->year*10000L + ltime->month*100+ltime->day; + int4store(ptr,tmp); } - -int Field_date::store(double nr) -{ - longlong tmp; - if (nr >= 19000000000000.0 && nr <= 99991231235959.0) - nr=floor(nr/1000000.0); // Timestamp to date - if (nr < 0.0 || nr > 99991231.0) - { - tmp= LL(0); - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_DATA_OUT_OF_RANGE, - nr, MYSQL_TIMESTAMP_DATE); - } - else - tmp= (longlong) rint(nr); - - return Field_date::store(tmp, TRUE); -} - - -int Field_date::store(longlong nr, bool unsigned_val) -{ - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; - MYSQL_TIME not_used; - int error; - longlong initial_nr= nr; - THD *thd= table ? table->in_use : current_thd; - - nr= number_to_datetime(nr, ¬_used, (TIME_FUZZY_DATE | - (thd->variables.sql_mode & - (MODE_NO_ZERO_IN_DATE | - MODE_NO_ZERO_DATE | - MODE_INVALID_DATES))), &error); - - if (nr == LL(-1)) - { - nr= 0; - error= 2; - } - - if (nr >= 19000000000000.0 && nr <= 99991231235959.0) - nr= (longlong) floor(nr/1000000.0); // Timestamp to date - - if (error) - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - error == 2 ? ER_WARN_DATA_OUT_OF_RANGE : - WARN_DATA_TRUNCATED, initial_nr, - MYSQL_TIMESTAMP_DATETIME, 1); - -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) - { - int4store(ptr, nr); - } - else -#endif - longstore(ptr, nr); - return error; -} - - bool Field_date::send_binary(Protocol *protocol) { longlong tmp= Field_date::val_int(); @@ -5537,12 +5525,7 @@ double Field_date::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; int32 j; -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) - j=sint4korr(ptr); - else -#endif - longget(j,ptr); + j=sint4korr(ptr); return (double) (uint32) j; } @@ -5551,12 +5534,7 @@ longlong Field_date::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; int32 j; -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) - j=sint4korr(ptr); - else -#endif - longget(j,ptr); + j=sint4korr(ptr); return (longlong) (uint32) j; } @@ -5566,68 +5544,38 @@ String *Field_date::val_str(String *val_buffer, { ASSERT_COLUMN_MARKED_FOR_READ; MYSQL_TIME ltime; - val_buffer->alloc(field_length); int32 tmp; -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) - tmp=sint4korr(ptr); - else -#endif - longget(tmp,ptr); + tmp=sint4korr(ptr); ltime.neg= 0; ltime.year= (int) ((uint32) tmp/10000L % 10000); ltime.month= (int) ((uint32) tmp/100 % 100); ltime.day= (int) ((uint32) tmp % 100); - make_date((DATE_TIME_FORMAT *) 0, <ime, val_buffer); - val_buffer->set_charset(&my_charset_numeric); - return val_buffer; -} + val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH); + uint length= (uint) my_date_to_str(<ime, + const_cast<char*>(val_buffer->ptr())); + val_buffer->length(length); + val_buffer->set_charset(&my_charset_numeric); -bool Field_date::get_time(MYSQL_TIME *ltime) -{ - bzero((char *)ltime, sizeof(MYSQL_TIME)); - return 0; + return val_buffer; } int Field_date::cmp(const uchar *a_ptr, const uchar *b_ptr) { int32 a,b; -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) - { - a=sint4korr(a_ptr); - b=sint4korr(b_ptr); - } - else -#endif - { - longget(a,a_ptr); - longget(b,b_ptr); - } + a=sint4korr(a_ptr); + b=sint4korr(b_ptr); return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0; } void Field_date::sort_string(uchar *to,uint length __attribute__((unused))) { -#ifdef WORDS_BIGENDIAN - if (!table || !table->s->db_low_byte_first) - { - to[0] = ptr[0]; - to[1] = ptr[1]; - to[2] = ptr[2]; - to[3] = ptr[3]; - } - else -#endif - { - to[0] = ptr[3]; - to[1] = ptr[2]; - to[2] = ptr[1]; - to[3] = ptr[0]; - } + to[0] = ptr[3]; + to[1] = ptr[2]; + to[2] = ptr[1]; + to[3] = ptr[0]; } void Field_date::sql_type(String &res) const @@ -5642,153 +5590,10 @@ void Field_date::sql_type(String &res) const ** In number context: YYYYMMDD ****************************************************************************/ -/* - Store string into a date field - - SYNOPSIS - Field_newdate::store() - from Date string - len Length of date field - cs Character set (not used) - - RETURN - 0 ok - 1 Value was cut during conversion - 2 Wrong date string - 3 Datetime value that was cut (warning level NOTE) - This is used by opt_range.cc:get_mm_leaf(). Note that there is a - nearly-identical class Field_date doesn't ever return 3 from its - store function. -*/ - -int Field_newdate::store(const char *from,uint len,CHARSET_INFO *cs) -{ - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; - long tmp; - MYSQL_TIME l_time; - int error; - THD *thd= table ? table->in_use : current_thd; - enum enum_mysql_timestamp_type ret; - if ((ret= str_to_datetime(cs, from, len, &l_time, - (TIME_FUZZY_DATE | - (thd->variables.sql_mode & - (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | - MODE_INVALID_DATES))), - &error)) <= MYSQL_TIMESTAMP_ERROR) - { - tmp= 0; - error= 2; - } - else - { - tmp= l_time.day + l_time.month*32 + l_time.year*16*32; - if (!error && (ret != MYSQL_TIMESTAMP_DATE) && - (l_time.hour || l_time.minute || l_time.second || l_time.second_part)) - error= 3; // Datetime was cut (note) - } - - if (error) - set_datetime_warning(error == 3 ? MYSQL_ERROR::WARN_LEVEL_NOTE : - MYSQL_ERROR::WARN_LEVEL_WARN, - WARN_DATA_TRUNCATED, - from, len, MYSQL_TIMESTAMP_DATE, 1); - - int3store(ptr, tmp); - return error; -} - - -int Field_newdate::store(double nr) -{ - if (nr < 0.0 || nr > 99991231235959.0) - { - int3store(ptr,(int32) 0); - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - WARN_DATA_TRUNCATED, nr, MYSQL_TIMESTAMP_DATE); - return 1; - } - return Field_newdate::store((longlong) rint(nr), FALSE); -} - - -int Field_newdate::store(longlong nr, bool unsigned_val) +void Field_newdate::store_TIME(MYSQL_TIME *ltime) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; - MYSQL_TIME l_time; - longlong tmp; - int error; - THD *thd= table ? table->in_use : current_thd; - if (number_to_datetime(nr, &l_time, - (TIME_FUZZY_DATE | - (thd->variables.sql_mode & - (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | - MODE_INVALID_DATES))), - &error) == LL(-1)) - { - tmp= 0L; - error= 2; - } - else - tmp= l_time.day + l_time.month*32 + l_time.year*16*32; - - if (!error && l_time.time_type != MYSQL_TIMESTAMP_DATE && - (l_time.hour || l_time.minute || l_time.second || l_time.second_part)) - error= 3; - - if (error) - set_datetime_warning(error == 3 ? MYSQL_ERROR::WARN_LEVEL_NOTE : - MYSQL_ERROR::WARN_LEVEL_WARN, - error == 2 ? - ER_WARN_DATA_OUT_OF_RANGE : WARN_DATA_TRUNCATED, - nr,MYSQL_TIMESTAMP_DATE, 1); - + uint tmp= ltime->year*16*32 + ltime->month*32+ltime->day; int3store(ptr,tmp); - return error; -} - - -int Field_newdate::store_time(MYSQL_TIME *ltime,timestamp_type time_type) -{ - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; - long tmp; - int error= 0; - if (time_type == MYSQL_TIMESTAMP_DATE || - time_type == MYSQL_TIMESTAMP_DATETIME) - { - tmp=ltime->year*16*32+ltime->month*32+ltime->day; - if (check_date(ltime, tmp != 0, - (TIME_FUZZY_DATE | - (current_thd->variables.sql_mode & - (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | - MODE_INVALID_DATES))), &error)) - { - char buff[MAX_DATE_STRING_REP_LENGTH]; - String str(buff, sizeof(buff), &my_charset_latin1); - tmp= 0; - make_date((DATE_TIME_FORMAT *) 0, ltime, &str); - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, - str.ptr(), str.length(), MYSQL_TIMESTAMP_DATE, 1); - } - if (!error && ltime->time_type != MYSQL_TIMESTAMP_DATE && - (ltime->hour || ltime->minute || ltime->second || ltime->second_part)) - { - char buff[MAX_DATE_STRING_REP_LENGTH]; - String str(buff, sizeof(buff), &my_charset_latin1); - make_datetime((DATE_TIME_FORMAT *) 0, ltime, &str); - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_NOTE, - WARN_DATA_TRUNCATED, - str.ptr(), str.length(), MYSQL_TIMESTAMP_DATE, 1); - error= 3; - } - } - else - { - tmp=0; - error= 1; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); - } - int3store(ptr,tmp); - return error; } @@ -5854,14 +5659,11 @@ bool Field_newdate::get_date(MYSQL_TIME *ltime,uint fuzzydate) ltime->year= (tmp >> 9); ltime->time_type= MYSQL_TIMESTAMP_DATE; ltime->hour= ltime->minute= ltime->second= ltime->second_part= ltime->neg= 0; - return ((!(fuzzydate & TIME_FUZZY_DATE) && (!ltime->month || !ltime->day)) ? - 1 : 0); -} - - -bool Field_newdate::get_time(MYSQL_TIME *ltime) -{ - return Field_newdate::get_date(ltime,0); + if (!tmp) + return fuzzydate & TIME_NO_ZERO_DATE; + if (!ltime->month || !ltime->day) + return !(fuzzydate & TIME_FUZZY_DATE); + return 0; } @@ -5895,150 +5697,20 @@ void Field_newdate::sql_type(String &res) const ** Stored as a 8 byte unsigned int. Should sometimes be change to a 6 byte int. ****************************************************************************/ -int Field_datetime::store(const char *from,uint len,CHARSET_INFO *cs) -{ - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; - MYSQL_TIME time_tmp; - int error; - ulonglong tmp= 0; - enum enum_mysql_timestamp_type func_res; - THD *thd= table ? table->in_use : current_thd; - - func_res= str_to_datetime(cs, from, len, &time_tmp, - (TIME_FUZZY_DATE | - (thd->variables.sql_mode & - (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | - MODE_INVALID_DATES))), - &error); - if ((int) func_res > (int) MYSQL_TIMESTAMP_ERROR) - tmp= TIME_to_ulonglong_datetime(&time_tmp); - else - error= 1; // Fix if invalid zero date - - if (error) - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_DATA_OUT_OF_RANGE, - from, len, MYSQL_TIMESTAMP_DATETIME, 1); - -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) - { - int8store(ptr,tmp); - } - else -#endif - longlongstore(ptr,tmp); - return error; -} - - -int Field_datetime::store(double nr) -{ - int error= 0; - if (nr < 0.0 || nr > 99991231235959.0) - { - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_DATA_OUT_OF_RANGE, - nr, MYSQL_TIMESTAMP_DATETIME); - nr= 0.0; - error= 1; - } - error|= Field_datetime::store((longlong) rint(nr), FALSE); - return error; -} - - -int Field_datetime::store(longlong nr, bool unsigned_val) -{ - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; - MYSQL_TIME not_used; - int error; - longlong initial_nr= nr; - THD *thd= table ? table->in_use : current_thd; - - nr= number_to_datetime(nr, ¬_used, (TIME_FUZZY_DATE | - (thd->variables.sql_mode & - (MODE_NO_ZERO_IN_DATE | - MODE_NO_ZERO_DATE | - MODE_INVALID_DATES))), &error); - - if (nr == LL(-1)) - { - nr= 0; - error= 2; - } - - if (error) - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - error == 2 ? ER_WARN_DATA_OUT_OF_RANGE : - WARN_DATA_TRUNCATED, initial_nr, - MYSQL_TIMESTAMP_DATETIME, 1); - -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) - { - int8store(ptr,nr); - } - else -#endif - longlongstore(ptr,nr); - return error; -} - - -int Field_datetime::store_time(MYSQL_TIME *ltime,timestamp_type time_type) +void Field_datetime::store_TIME(MYSQL_TIME *ltime) { - ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; - longlong tmp; - int error= 0; - /* - We don't perform range checking here since values stored in TIME - structure always fit into DATETIME range. - */ - if (time_type == MYSQL_TIMESTAMP_DATE || - time_type == MYSQL_TIMESTAMP_DATETIME) - { - tmp=((ltime->year*10000L+ltime->month*100+ltime->day)*LL(1000000)+ - (ltime->hour*10000L+ltime->minute*100+ltime->second)); - if (check_date(ltime, tmp != 0, - (TIME_FUZZY_DATE | - (current_thd->variables.sql_mode & - (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | - MODE_INVALID_DATES))), &error)) - { - char buff[MAX_DATE_STRING_REP_LENGTH]; - String str(buff, sizeof(buff), &my_charset_latin1); - tmp= 0; - make_datetime((DATE_TIME_FORMAT *) 0, ltime, &str); - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, - str.ptr(), str.length(), MYSQL_TIMESTAMP_DATETIME,1); - } - } - else - { - tmp=0; - error= 1; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); - } -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) - { - int8store(ptr,tmp); - } - else -#endif - longlongstore(ptr,tmp); - return error; + ulonglong tmp= TIME_to_ulonglong_datetime(ltime); + int8store(ptr,tmp); } bool Field_datetime::send_binary(Protocol *protocol) { MYSQL_TIME tm; Field_datetime::get_date(&tm, TIME_FUZZY_DATE); - return protocol->store(&tm); + return protocol->store(&tm, 0); } - - + + double Field_datetime::val_real(void) { return (double) Field_datetime::val_int(); @@ -6048,12 +5720,7 @@ longlong Field_datetime::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; longlong j; -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) - j=sint8korr(ptr); - else -#endif - longlongget(j,ptr); + j=sint8korr(ptr); return j; } @@ -6061,20 +5728,16 @@ longlong Field_datetime::val_int(void) String *Field_datetime::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { - ASSERT_COLUMN_MARKED_FOR_READ; val_buffer->alloc(field_length); val_buffer->length(field_length); + + ASSERT_COLUMN_MARKED_FOR_READ; ulonglong tmp; long part1,part2; char *pos; int part3; -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) - tmp=sint8korr(ptr); - else -#endif - longlongget(tmp,ptr); + tmp= Field_datetime::val_int(); /* Avoid problem with slow longlong arithmetic and sprintf @@ -6124,65 +5787,148 @@ bool Field_datetime::get_date(MYSQL_TIME *ltime, uint fuzzydate) ltime->day= (int) (part1%100); ltime->month= (int) (part1/100%100); ltime->year= (int) (part1/10000); - return (!(fuzzydate & TIME_FUZZY_DATE) && (!ltime->month || !ltime->day)) ? 1 : 0; -} - -bool Field_datetime::get_time(MYSQL_TIME *ltime) -{ - return Field_datetime::get_date(ltime,0); + if (!tmp) + return fuzzydate & TIME_NO_ZERO_DATE; + if (!ltime->month || !ltime->day) + return !(fuzzydate & TIME_FUZZY_DATE); + return 0; } int Field_datetime::cmp(const uchar *a_ptr, const uchar *b_ptr) { longlong a,b; -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) - { - a=sint8korr(a_ptr); - b=sint8korr(b_ptr); - } - else -#endif - { - longlongget(a,a_ptr); - longlongget(b,b_ptr); - } + a=sint8korr(a_ptr); + b=sint8korr(b_ptr); return ((ulonglong) a < (ulonglong) b) ? -1 : ((ulonglong) a > (ulonglong) b) ? 1 : 0; } void Field_datetime::sort_string(uchar *to,uint length __attribute__((unused))) { -#ifdef WORDS_BIGENDIAN - if (!table || !table->s->db_low_byte_first) + to[0] = ptr[7]; + to[1] = ptr[6]; + to[2] = ptr[5]; + to[3] = ptr[4]; + to[4] = ptr[3]; + to[5] = ptr[2]; + to[6] = ptr[1]; + to[7] = ptr[0]; +} + + +void Field_datetime::sql_type(String &res) const +{ + res.set_ascii(STRING_WITH_LEN("datetime")); +} + +void Field_datetime_hires::store_TIME(MYSQL_TIME *ltime) +{ + ulonglong packed= sec_part_shift(pack_time(ltime), dec); + store_bigendian(packed, ptr, Field_datetime_hires::pack_length()); +} + +int Field_datetime_hires::store_decimal(const my_decimal *d) +{ + ulonglong nr; + ulong sec_part; + int error; + MYSQL_TIME ltime; + longlong tmp; + THD *thd= table->in_use; + ErrConvDecimal str(d); + + if (my_decimal2seconds(d, &nr, &sec_part)) { - to[0] = ptr[0]; - to[1] = ptr[1]; - to[2] = ptr[2]; - to[3] = ptr[3]; - to[4] = ptr[4]; - to[5] = ptr[5]; - to[6] = ptr[6]; - to[7] = ptr[7]; + tmp= -1; + error= 2; } else -#endif - { - to[0] = ptr[7]; - to[1] = ptr[6]; - to[2] = ptr[5]; - to[3] = ptr[4]; - to[4] = ptr[3]; - to[5] = ptr[2]; - to[6] = ptr[1]; - to[7] = ptr[0]; - } + tmp= number_to_datetime(nr, sec_part, <ime, (TIME_FUZZY_DATE | + (thd->variables.sql_mode & + (MODE_NO_ZERO_IN_DATE | + MODE_NO_ZERO_DATE | + MODE_INVALID_DATES))), &error); + + return store_TIME_with_warning(<ime, &str, error, tmp != -1); } +bool Field_datetime_hires::send_binary(Protocol *protocol) +{ + MYSQL_TIME ltime; + Field_datetime_hires::get_date(<ime, TIME_FUZZY_DATE); + return protocol->store(<ime, dec); +} -void Field_datetime::sql_type(String &res) const + +double Field_datetime_hires::val_real(void) { - res.set_ascii(STRING_WITH_LEN("datetime")); + MYSQL_TIME ltime; + Field_datetime_hires::get_date(<ime, TIME_FUZZY_DATE); + return TIME_to_double(<ime); +} + +longlong Field_datetime_hires::val_int(void) +{ + MYSQL_TIME ltime; + Field_datetime_hires::get_date(<ime, TIME_FUZZY_DATE); + return TIME_to_ulonglong_datetime(<ime); +} + + +String *Field_datetime_hires::val_str(String *str, + String *unused __attribute__((unused))) +{ + MYSQL_TIME ltime; + Field_datetime_hires::get_date(<ime, TIME_FUZZY_DATE); + str->alloc(field_length+1); + str->length(field_length); + my_datetime_to_str(<ime, (char*) str->ptr(), dec); + str->set_charset(&my_charset_bin); + return str; +} + +bool Field_datetime_hires::get_date(MYSQL_TIME *ltime, uint fuzzydate) +{ + ulonglong packed= read_bigendian(ptr, Field_datetime_hires::pack_length()); + unpack_time(sec_part_unshift(packed, dec), ltime); + if (!packed) + return fuzzydate & TIME_NO_ZERO_DATE; + if (!ltime->month || !ltime->day) + return !(fuzzydate & TIME_FUZZY_DATE); + return 0; +} + +uint32 Field_datetime_hires::pack_length() const +{ + return datetime_hires_bytes[dec]; +} + +int Field_datetime_hires::cmp(const uchar *a_ptr, const uchar *b_ptr) +{ + ulonglong a=read_bigendian(a_ptr, Field_datetime_hires::pack_length()); + ulonglong b=read_bigendian(b_ptr, Field_datetime_hires::pack_length()); + return a < b ? -1 : a > b ? 1 : 0; +} + +void Field_datetime_hires::sort_string(uchar *to, + uint length __attribute__((unused))) +{ + DBUG_ASSERT(length == Field_datetime_hires::pack_length()); + memcpy(to, ptr, length); +} + + +void Field_datetime_hires::sql_type(String &res) const +{ + CHARSET_INFO *cs=res.charset(); + res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(), + "datetime(%u)", dec)); +} + +void Field_datetime_hires::make_field(Send_field *field) +{ + Field::make_field(field); + field->decimals= dec; } /**************************************************************************** @@ -6564,9 +6310,7 @@ void Field_string::sql_type(String &res) const } -uchar *Field_string::pack(uchar *to, const uchar *from, - uint max_length, - bool low_byte_first __attribute__((unused))) +uchar *Field_string::pack(uchar *to, const uchar *from, uint max_length) { uint length= min(field_length,max_length); uint local_char_length= max_length/field_charset->mbmaxlen; @@ -6622,10 +6366,7 @@ uchar *Field_string::pack(uchar *to, const uchar *from, @return New pointer into memory based on from + length of the data */ const uchar * -Field_string::unpack(uchar *to, - const uchar *from, - uint param_data, - bool low_byte_first __attribute__((unused))) +Field_string::unpack(uchar *to, const uchar *from, uint param_data) { uint from_length, length; @@ -7039,9 +6780,7 @@ uint32 Field_varstring::data_length() Here the number of length bytes are depending on the given max_length */ -uchar *Field_varstring::pack(uchar *to, const uchar *from, - uint max_length, - bool low_byte_first __attribute__((unused))) +uchar *Field_varstring::pack(uchar *to, const uchar *from, uint max_length) { uint length= length_bytes == 1 ? (uint) *from : uint2korr(from); set_if_smaller(max_length, field_length); @@ -7076,9 +6815,7 @@ uchar *Field_varstring::pack(uchar *to, const uchar *from, @return New pointer into memory based on from + length of the data */ const uchar * -Field_varstring::unpack(uchar *to, const uchar *from, - uint param_data, - bool low_byte_first __attribute__((unused))) +Field_varstring::unpack(uchar *to, const uchar *from, uint param_data) { uint length; uint l_bytes= (param_data && (param_data < field_length)) ? @@ -7252,103 +6989,15 @@ Field_blob::Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, } -void Field_blob::store_length(uchar *i_ptr, - uint i_packlength, - uint32 i_number, - bool low_byte_first) +void Field_blob::store_length(uchar *i_ptr, uint i_packlength, uint32 i_number) { - switch (i_packlength) { - case 1: - i_ptr[0]= (uchar) i_number; - break; - case 2: -#ifdef WORDS_BIGENDIAN - if (low_byte_first) - { - int2store(i_ptr,(unsigned short) i_number); - } - else -#endif - shortstore(i_ptr,(unsigned short) i_number); - break; - case 3: - int3store(i_ptr,i_number); - break; - case 4: -#ifdef WORDS_BIGENDIAN - if (low_byte_first) - { - int4store(i_ptr,i_number); - } - else -#endif - longstore(i_ptr,i_number); - } + store_lowendian(i_number, i_ptr, i_packlength); } -uint32 Field_blob::get_length(const uchar *pos, uint packlength_arg, bool low_byte_first) +uint32 Field_blob::get_length(const uchar *pos, uint packlength_arg) { - switch (packlength_arg) { - case 1: - return (uint32) pos[0]; - case 2: - { - uint16 tmp; -#ifdef WORDS_BIGENDIAN - if (low_byte_first) - tmp=sint2korr(pos); - else -#endif - shortget(tmp,pos); - return (uint32) tmp; - } - case 3: - return (uint32) uint3korr(pos); - case 4: - { - uint32 tmp; -#ifdef WORDS_BIGENDIAN - if (low_byte_first) - tmp=uint4korr(pos); - else -#endif - longget(tmp,pos); - return (uint32) tmp; - } - } - /* When expanding this, see also MAX_FIELD_BLOBLENGTH. */ - return 0; // Impossible -} - - -/** - Put a blob length field into a record buffer. - - Depending on the maximum length of a blob, its length field is - put into 1 to 4 bytes. This is a property of the blob object, - described by 'packlength'. - - @param pos Pointer into the record buffer. - @param length The length value to put. -*/ - -void Field_blob::put_length(uchar *pos, uint32 length) -{ - switch (packlength) { - case 1: - *pos= (char) length; - break; - case 2: - int2store(pos, length); - break; - case 3: - int3store(pos, length); - break; - case 4: - int4store(pos, length); - break; - } + return (uint32)read_lowendian(pos, packlength_arg); } @@ -7689,20 +7338,7 @@ void Field_blob::sort_string(uchar *to,uint length) length-= packlength; pos= to+length; - switch (packlength) { - case 1: - *pos= (char) blob_length; - break; - case 2: - mi_int2store(pos, blob_length); - break; - case 3: - mi_int3store(pos, blob_length); - break; - case 4: - mi_int4store(pos, blob_length); - break; - } + store_bigendian(blob_length, pos, packlength); } memcpy(&blob, ptr+packlength, sizeof(char*)); @@ -7732,8 +7368,7 @@ void Field_blob::sql_type(String &res) const } } -uchar *Field_blob::pack(uchar *to, const uchar *from, - uint max_length, bool low_byte_first) +uchar *Field_blob::pack(uchar *to, const uchar *from, uint max_length) { uchar *save= ptr; ptr= (uchar*) from; @@ -7744,7 +7379,7 @@ uchar *Field_blob::pack(uchar *to, const uchar *from, length given is smaller than the actual length of the blob, we just store the initial bytes of the blob. */ - store_length(to, packlength, min(length, max_length), low_byte_first); + store_length(to, packlength, min(length, max_length)); /* Store the actual blob data, which will occupy 'length' bytes. @@ -7776,18 +7411,14 @@ uchar *Field_blob::pack(uchar *to, const uchar *from, @return New pointer into memory based on from + length of the data */ -const uchar *Field_blob::unpack(uchar *to, - const uchar *from, - uint param_data, - bool low_byte_first) +const uchar *Field_blob::unpack(uchar *to, const uchar *from, uint param_data) { DBUG_ENTER("Field_blob::unpack"); - DBUG_PRINT("enter", ("to: 0x%lx; from: 0x%lx;" - " param_data: %u; low_byte_first: %d", - (ulong) to, (ulong) from, param_data, low_byte_first)); + DBUG_PRINT("enter", ("to: 0x%lx; from: 0x%lx; param_data: %u", + (ulong) to, (ulong) from, param_data)); uint const master_packlength= param_data > 0 ? param_data & 0xFF : packlength; - uint32 const length= get_length(from, master_packlength, low_byte_first); + uint32 const length= get_length(from, master_packlength); DBUG_DUMP("packed", from, length + master_packlength); bitmap_set_bit(table->write_set, field_index); store(reinterpret_cast<const char*>(from) + master_packlength, @@ -7796,6 +7427,7 @@ const uchar *Field_blob::unpack(uchar *to, DBUG_RETURN(from + master_packlength + length); } + uint Field_blob::packed_col_length(const uchar *data_ptr, uint length) { if (length > 255) @@ -7933,39 +7565,7 @@ enum ha_base_keytype Field_enum::key_type() const void Field_enum::store_type(ulonglong value) { - switch (packlength) { - case 1: ptr[0]= (uchar) value; break; - case 2: -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - int2store(ptr,(unsigned short) value); - } - else -#endif - shortstore(ptr,(unsigned short) value); - break; - case 3: int3store(ptr,(long) value); break; - case 4: -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - int4store(ptr,value); - } - else -#endif - longstore(ptr,(long) value); - break; - case 8: -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - int8store(ptr,value); - } - else -#endif - longlongstore(ptr,value); break; - } + store_lowendian(value, ptr, packlength); } @@ -8051,46 +7651,7 @@ double Field_enum::val_real(void) longlong Field_enum::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; - switch (packlength) { - case 1: - return (longlong) ptr[0]; - case 2: - { - uint16 tmp; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - tmp=sint2korr(ptr); - else -#endif - shortget(tmp,ptr); - return (longlong) tmp; - } - case 3: - return (longlong) uint3korr(ptr); - case 4: - { - uint32 tmp; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - tmp=uint4korr(ptr); - else -#endif - longget(tmp,ptr); - return (longlong) tmp; - } - case 8: - { - longlong tmp; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - tmp=sint8korr(ptr); - else -#endif - longlongget(tmp,ptr); - return tmp; - } - } - return 0; // impossible + return read_lowendian(ptr, packlength); } @@ -8399,51 +7960,20 @@ uint Field_enum::is_equal(Create_field *new_field) } -uchar *Field_enum::pack(uchar *to, const uchar *from, - uint max_length, bool low_byte_first) +uchar *Field_enum::pack(uchar *to, const uchar *from, uint max_length) { DBUG_ENTER("Field_enum::pack"); DBUG_PRINT("debug", ("packlength: %d", packlength)); DBUG_DUMP("from", from, packlength); - - switch (packlength) - { - case 1: - *to = *from; - DBUG_RETURN(to + 1); - case 2: DBUG_RETURN(pack_int16(to, from, low_byte_first)); - case 3: DBUG_RETURN(pack_int24(to, from, low_byte_first)); - case 4: DBUG_RETURN(pack_int32(to, from, low_byte_first)); - case 8: DBUG_RETURN(pack_int64(to, from, low_byte_first)); - default: - DBUG_ASSERT(0); - } - MY_ASSERT_UNREACHABLE(); - DBUG_RETURN(NULL); + DBUG_RETURN(pack_int(to, from, packlength)); } -const uchar *Field_enum::unpack(uchar *to, const uchar *from, - uint param_data, bool low_byte_first) +const uchar *Field_enum::unpack(uchar *to, const uchar *from, uint param_data) { DBUG_ENTER("Field_enum::unpack"); DBUG_PRINT("debug", ("packlength: %d", packlength)); DBUG_DUMP("from", from, packlength); - - switch (packlength) - { - case 1: - *to = *from; - DBUG_RETURN(from + 1); - - case 2: DBUG_RETURN(unpack_int16(to, from, low_byte_first)); - case 3: DBUG_RETURN(unpack_int24(to, from, low_byte_first)); - case 4: DBUG_RETURN(unpack_int32(to, from, low_byte_first)); - case 8: DBUG_RETURN(unpack_int64(to, from, low_byte_first)); - default: - DBUG_ASSERT(0); - } - MY_ASSERT_UNREACHABLE(); - DBUG_RETURN(NULL); + DBUG_RETURN(unpack_int(to, from, packlength)); } @@ -8892,8 +8422,7 @@ void Field_bit::sql_type(String &res) const uchar * -Field_bit::pack(uchar *to, const uchar *from, uint max_length, - bool low_byte_first __attribute__((unused))) +Field_bit::pack(uchar *to, const uchar *from, uint max_length) { DBUG_ASSERT(max_length > 0); uint length; @@ -8940,8 +8469,7 @@ Field_bit::pack(uchar *to, const uchar *from, uint max_length, @return New pointer into memory based on from + length of the data */ const uchar * -Field_bit::unpack(uchar *to, const uchar *from, uint param_data, - bool low_byte_first __attribute__((unused))) +Field_bit::unpack(uchar *to, const uchar *from, uint param_data) { DBUG_ENTER("Field_bit::unpack"); DBUG_PRINT("enter", ("to: %p, from: %p, param_data: 0x%x", @@ -9492,28 +9020,14 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, } break; case MYSQL_TYPE_TIMESTAMP: - if (fld_length == NULL) - { - length= MAX_DATETIME_WIDTH; - } - else if (length != MAX_DATETIME_WIDTH) + if (length > MAX_DATETIME_PRECISION) { - /* - We support only even TIMESTAMP lengths less or equal than 14 - and 19 as length of 4.1 compatible representation. Silently - shrink it to MAX_DATETIME_COMPRESSED_WIDTH. - */ - DBUG_ASSERT(MAX_DATETIME_COMPRESSED_WIDTH < UINT_MAX); - if (length != UINT_MAX) /* avoid overflow; is safe because of min() */ - length= ((length+1)/2)*2; - length= min(length, MAX_DATETIME_COMPRESSED_WIDTH); + my_error(ER_TOO_BIG_PRECISION, MYF(0), length, fld_name, + MAX_DATETIME_PRECISION); + DBUG_RETURN(TRUE); } - flags|= ZEROFILL_FLAG | UNSIGNED_FLAG; - /* - Since we silently rewrite down to MAX_DATETIME_COMPRESSED_WIDTH bytes, - the parser should not raise errors unless bizzarely large. - */ - max_field_charlength= UINT_MAX; + length+= MAX_DATETIME_WIDTH + (length ? 1 : 0); + flags|= UNSIGNED_FLAG; if (fld_default_value) { @@ -9561,10 +9075,22 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, length= MAX_DATE_WIDTH; break; case MYSQL_TYPE_TIME: - length= 10; + if (length > MAX_DATETIME_PRECISION) + { + my_error(ER_TOO_BIG_PRECISION, MYF(0), length, fld_name, + MAX_DATETIME_PRECISION); + DBUG_RETURN(TRUE); + } + length+= MIN_TIME_WIDTH + (length ? 1 : 0); break; case MYSQL_TYPE_DATETIME: - length= MAX_DATETIME_WIDTH; + if (length > MAX_DATETIME_PRECISION) + { + my_error(ER_TOO_BIG_PRECISION, MYF(0), length, fld_name, + MAX_DATETIME_PRECISION); + DBUG_RETURN(TRUE); + } + length+= MAX_DATETIME_WIDTH + (length ? 1 : 0); break; case MYSQL_TYPE_SET: { @@ -9684,14 +9210,22 @@ uint32 calc_pack_length(enum_field_types type,uint32 length) case MYSQL_TYPE_TINY : return 1; case MYSQL_TYPE_SHORT : return 2; case MYSQL_TYPE_INT24: - case MYSQL_TYPE_NEWDATE: - case MYSQL_TYPE_TIME: return 3; + case MYSQL_TYPE_NEWDATE: return 3; + case MYSQL_TYPE_TIME: return length > MIN_TIME_WIDTH + ? time_hires_bytes[length - 1 - MIN_TIME_WIDTH] + : 3; case MYSQL_TYPE_TIMESTAMP: + return length > MAX_DATETIME_WIDTH + ? 4 + sec_part_bytes[length - 1 - MAX_DATETIME_WIDTH] + : 4; case MYSQL_TYPE_DATE: case MYSQL_TYPE_LONG : return 4; case MYSQL_TYPE_FLOAT : return sizeof(float); case MYSQL_TYPE_DOUBLE: return sizeof(double); case MYSQL_TYPE_DATETIME: + return length > MAX_DATETIME_WIDTH + ? datetime_hires_bytes[length - 1 - MAX_DATETIME_WIDTH] + : 8; case MYSQL_TYPE_LONGLONG: return 8; /* Don't crash if no longlong */ case MYSQL_TYPE_NULL : return 0; case MYSQL_TYPE_TINY_BLOB: return 1+portable_sizeof_char_ptr; @@ -9722,6 +9256,7 @@ uint pack_length_to_packflag(uint type) return 0; // This shouldn't happen } + Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length, uchar *null_pos, uchar null_bit, uint pack_flag, @@ -9871,9 +9406,12 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length, f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); case MYSQL_TYPE_TIMESTAMP: - return new Field_timestamp(ptr,field_length, null_pos, null_bit, - unireg_check, field_name, share, - field_charset); + { + uint dec= field_length > MAX_DATETIME_WIDTH ? + field_length - MAX_DATETIME_WIDTH - 1: 0; + return new_Field_timestamp(ptr, null_pos, null_bit, unireg_check, + field_name, share, dec, field_charset); + } case MYSQL_TYPE_YEAR: return new Field_year(ptr,field_length,null_pos,null_bit, unireg_check, field_name); @@ -9884,11 +9422,19 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length, return new Field_newdate(ptr,null_pos,null_bit, unireg_check, field_name, field_charset); case MYSQL_TYPE_TIME: - return new Field_time(ptr,null_pos,null_bit, - unireg_check, field_name, field_charset); + { + uint dec= field_length > MIN_TIME_WIDTH ? + field_length - MIN_TIME_WIDTH - 1: 0; + return new_Field_time(ptr, null_pos, null_bit, unireg_check, + field_name, dec, field_charset); + } case MYSQL_TYPE_DATETIME: - return new Field_datetime(ptr,null_pos,null_bit, - unireg_check, field_name, field_charset); + { + uint dec= field_length > MAX_DATETIME_WIDTH ? + field_length - MAX_DATETIME_WIDTH - 1: 0; + return new_Field_datetime(ptr, null_pos, null_bit, unireg_check, + field_name, dec, field_charset); + } case MYSQL_TYPE_NULL: return new Field_null(ptr, field_length, unireg_check, field_name, field_charset); @@ -9997,6 +9543,7 @@ Create_field::Create_field(Field *old_field,Field *orig_field) } } + /** maximum possible character length for blob. @@ -10086,14 +9633,9 @@ uint32 Field_blob::max_display_length() if count_cuted_fields == CHECK_FIELD_IGNORE then we ignore notes. This allows us to avoid notes in optimisation, like convert_constant_item(). - - @retval - 1 if count_cuted_fields == CHECK_FIELD_IGNORE and error level is not NOTE - @retval - 0 otherwise */ -bool +void Field::set_warning(MYSQL_ERROR::enum_warning_level level, uint code, int cuted_increment) { @@ -10107,9 +9649,7 @@ Field::set_warning(MYSQL_ERROR::enum_warning_level level, uint code, thd->cuted_fields+= cuted_increment; push_warning_printf(thd, level, code, ER(code), field_name, thd->warning_info->current_row_for_warning()); - return 0; } - return level >= MYSQL_ERROR::WARN_LEVEL_WARN; } @@ -10119,7 +9659,6 @@ Field::set_warning(MYSQL_ERROR::enum_warning_level level, uint code, @param level level of message (Note/Warning/Error) @param code error code of message to be produced @param str string value which we tried to save - @param str_length length of string which we tried to save @param ts_type type of datetime value (datetime/date/time) @param cuted_increment whenever we should increase cut fields count or not @@ -10127,80 +9666,42 @@ Field::set_warning(MYSQL_ERROR::enum_warning_level level, uint code, This function will always produce some warning but won't increase cut fields counter if count_cuted_fields ==FIELD_CHECK_IGNORE for current thread. -*/ - -void -Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, uint code, - const char *str, uint str_length, - timestamp_type ts_type, int cuted_increment) -{ - THD *thd= table ? table->in_use : current_thd; - if ((thd->really_abort_on_warning() && - level >= MYSQL_ERROR::WARN_LEVEL_WARN) || - set_warning(level, code, cuted_increment)) - make_truncated_value_warning(thd, level, str, str_length, ts_type, - field_name); -} - - -/** - Produce warning or note about integer datetime value saved into field. - @param level level of message (Note/Warning/Error) - @param code error code of message to be produced - @param nr numeric value which we tried to save - @param ts_type type of datetime value (datetime/date/time) - @param cuted_increment whenever we should increase cut fields count or not + See also bug#2336 - @note - This function will always produce some warning but won't increase cut - fields counter if count_cuted_fields == FIELD_CHECK_IGNORE for current - thread. */ -void -Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, uint code, - longlong nr, timestamp_type ts_type, - int cuted_increment) +void Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, + uint code, const ErrConv *str, + timestamp_type ts_type, int cuted_increment) { - THD *thd= table ? table->in_use : current_thd; - if (thd->really_abort_on_warning() || - set_warning(level, code, cuted_increment)) - { - char str_nr[22]; - char *str_end= longlong10_to_str(nr, str_nr, -10); - make_truncated_value_warning(thd, level, str_nr, (uint) (str_end - str_nr), - ts_type, field_name); - } + THD *thd= table->in_use; + if (thd->really_abort_on_warning() && level >= MYSQL_ERROR::WARN_LEVEL_WARN) + make_truncated_value_warning(thd, level, str, ts_type, field_name); + else + set_warning(level, code, cuted_increment); } -/** - Produce warning or note about double datetime data saved into field. - - @param level level of message (Note/Warning/Error) - @param code error code of message to be produced - @param nr double value which we tried to save - @param ts_type type of datetime value (datetime/date/time) - - @note - This function will always produce some warning but won't increase cut - fields counter if count_cuted_fields == FIELD_CHECK_IGNORE for current - thread. +/* + @brief + Return possible keys for a field + + @details + Return bit map of keys over this field which can be used by the range + optimizer. For a field of a generic table such keys are all keys that starts + from this field. For a field of a materialized derived table/view such keys + are all keys in which this field takes a part. This is less restrictive as + keys for a materialized derived table/view are generated on the fly from + present fields, thus the case when a field for the beginning of a key is + absent is impossible. + + @return map of possible keys */ -void -Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, uint code, - double nr, timestamp_type ts_type) +key_map Field::get_possible_keys() { - THD *thd= table ? table->in_use : current_thd; - if (thd->really_abort_on_warning() || - set_warning(level, code, 1)) - { - /* DBL_DIG is enough to print '-[digits].E+###' */ - char str_nr[DBL_DIG + 8]; - uint str_len= sprintf(str_nr, "%g", nr); - make_truncated_value_warning(thd, level, str_nr, str_len, ts_type, - field_name); - } + DBUG_ASSERT(table->pos_in_table_list); + return (table->pos_in_table_list->is_materialized_derived() ? + part_of_key : key_start); } |