diff options
author | Michael Widenius <monty@askmonty.org> | 2011-05-28 05:11:32 +0300 |
---|---|---|
committer | Michael Widenius <monty@askmonty.org> | 2011-05-28 05:11:32 +0300 |
commit | f197991f4102ed8ac66b7b57071f24f1d3b86aea (patch) | |
tree | b4590b80e7d50b664d8e6ff6a62978cb2402f0a6 /sql | |
parent | de44b51e151a00a00d0e396dc57ced3682d24d78 (diff) | |
parent | 306ed65302e14f303fdc33cfa9d19016fb319440 (diff) | |
download | mariadb-git-f197991f4102ed8ac66b7b57071f24f1d3b86aea.tar.gz |
Merge with 5.1-microseconds
A lot of small fixes and new test cases.
client/mysqlbinlog.cc:
Cast removed
client/mysqltest.cc:
Added missing DBUG_RETURN
include/my_pthread.h:
set_timespec_time_nsec() now only takes one argument
mysql-test/t/date_formats.test:
Remove --disable_ps_protocl as now also ps supports microseconds
mysys/my_uuid.c:
Changed to use my_interval_timer() instead of my_getsystime()
mysys/waiting_threads.c:
Changed to use my_hrtime()
sql/field.h:
Added bool special_const_compare() for fields that may convert values before compare (like year)
sql/field_conv.cc:
Added test to get optimal copying of identical temporal values.
sql/item.cc:
Return that item_int is equal if it's positive, even if unsigned flag is different.
Fixed Item_cache_str::save_in_field() to have identical null check as other similar functions
Added proper NULL check to Item_cache_int::save_in_field()
sql/item_cmpfunc.cc:
Don't call convert_constant_item() if there is nothing that is worth converting.
Simplified test when years should be converted
sql/item_sum.cc:
Mark cache values in Item_sum_hybrid as not constants to ensure they are not replaced by other cache values in compare_datetime()
sql/item_timefunc.cc:
Changed sec_to_time() to take a my_decimal argument to ensure we don't loose any sub seconds.
Added Item_temporal_func::get_time() (This simplifies some things)
sql/mysql_priv.h:
Added Lazy_string_decimal()
sql/mysqld.cc:
Added my_decimal constants max_seconds_for_time_type, time_second_part_factor
sql/table.cc:
Changed expr_arena to be of type CONVENTIONAL_EXECUTION to ensure that we don't loose any items that are created by fix_fields()
sql/tztime.cc:
TIME_to_gmt_sec() now sets *in_dst_time_gap in case of errors
This is needed to be able to detect if timestamp is 0
storage/maria/lockman.c:
Changed from my_getsystime() to set_timespec_time_nsec()
storage/maria/ma_loghandler.c:
Changed from my_getsystime() to my_hrtime()
storage/maria/ma_recovery.c:
Changed from my_getsystime() to mmicrosecond_interval_timer()
storage/maria/unittest/trnman-t.c:
Changed from my_getsystime() to mmicrosecond_interval_timer()
storage/xtradb/handler/ha_innodb.cc:
Added support for new time,datetime and timestamp
unittest/mysys/thr_template.c:
my_getsystime() -> my_interval_timer()
unittest/mysys/waiting_threads-t.c:
my_getsystime() -> my_interval_timer()
Diffstat (limited to 'sql')
62 files changed, 3342 insertions, 4344 deletions
diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 959f6ec06c0..6e2aa013d88 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -927,7 +927,7 @@ Event_queue_element::compute_next_execution_time() goto ret; } - time_now= (my_time_t) current_thd->query_start(); + time_now= current_thd->query_start(); DBUG_PRINT("info",("NOW: [%lu]", (ulong) time_now)); @@ -1130,7 +1130,7 @@ err: void Event_queue_element::mark_last_executed(THD *thd) { - last_executed= (my_time_t) thd->query_start(); + last_executed= thd->query_start(); last_executed_changed= TRUE; execution_count++; @@ -1190,7 +1190,7 @@ append_datetime(String *buf, Time_zone *time_zone, my_time_t secs, */ MYSQL_TIME time; time_zone->gmt_sec_to_TIME(&time, secs); - buf->append(dtime_buff, my_datetime_to_str(&time, dtime_buff)); + buf->append(dtime_buff, my_datetime_to_str(&time, dtime_buff, 0)); buf->append(STRING_WITH_LEN("'")); } diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index 2a2fc6bdd3d..7d82126cbe5 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -232,6 +232,9 @@ mysql_event_fill_row(THD *thd, rs|= fields[ET_FIELD_STATUS]->store((longlong)et->status, TRUE); rs|= fields[ET_FIELD_ORIGINATOR]->store((longlong)et->originator, TRUE); + if (!is_update) + rs|= fields[ET_FIELD_CREATED]->set_time(); + /* Change the SQL_MODE only if body was present in an ALTER EVENT and of course always during CREATE EVENT. @@ -318,7 +321,7 @@ mysql_event_fill_row(THD *thd, */ } - ((Field_timestamp *)fields[ET_FIELD_MODIFIED])->set_time(); + rs|= fields[ET_FIELD_MODIFIED]->set_time(); if (et->comment.str) { @@ -674,8 +677,6 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data, goto end; } - ((Field_timestamp *)table->field[ET_FIELD_CREATED])->set_time(); - /* mysql_event_fill_row() calls my_error() in case of error so no need to handle it here diff --git a/sql/event_parse_data.cc b/sql/event_parse_data.cc index 86905b38627..4555da44f18 100644 --- a/sql/event_parse_data.cc +++ b/sql/event_parse_data.cc @@ -111,7 +111,7 @@ Event_parse_data::init_name(THD *thd, sp_name *spn) void Event_parse_data::check_if_in_the_past(THD *thd, my_time_t ltime_utc) { - if (ltime_utc >= (my_time_t) thd->query_start()) + if (ltime_utc >= thd->query_start()) return; /* @@ -198,7 +198,7 @@ Event_parse_data::check_dates(THD *thd, int previous_on_completion) int Event_parse_data::init_execute_at(THD *thd) { - my_bool not_used; + uint not_used; MYSQL_TIME ltime; my_time_t ltime_utc; @@ -215,7 +215,7 @@ Event_parse_data::init_execute_at(THD *thd) (starts_null && ends_null))); DBUG_ASSERT(starts_null && ends_null); - if ((not_used= item_execute_at->get_date(<ime, TIME_NO_ZERO_DATE))) + if (item_execute_at->get_date(<ime, TIME_NO_ZERO_DATE)) goto wrong_value; ltime_utc= TIME_to_timestamp(thd,<ime,¬_used); @@ -368,7 +368,7 @@ wrong_value: int Event_parse_data::init_starts(THD *thd) { - my_bool not_used; + uint not_used; MYSQL_TIME ltime; my_time_t ltime_utc; @@ -379,7 +379,7 @@ Event_parse_data::init_starts(THD *thd) if (item_starts->fix_fields(thd, &item_starts)) goto wrong_value; - if ((not_used= item_starts->get_date(<ime, TIME_NO_ZERO_DATE))) + if (item_starts->get_date(<ime, TIME_NO_ZERO_DATE)) goto wrong_value; ltime_utc= TIME_to_timestamp(thd, <ime, ¬_used); @@ -422,7 +422,7 @@ wrong_value: int Event_parse_data::init_ends(THD *thd) { - my_bool not_used; + uint not_used; MYSQL_TIME ltime; my_time_t ltime_utc; @@ -434,7 +434,7 @@ Event_parse_data::init_ends(THD *thd) goto error_bad_params; DBUG_PRINT("info", ("convert to TIME")); - if ((not_used= item_ends->get_date(<ime, TIME_NO_ZERO_DATE))) + if (item_ends->get_date(<ime, TIME_NO_ZERO_DATE)) goto error_bad_params; ltime_utc= TIME_to_timestamp(thd, <ime, ¬_used); diff --git a/sql/event_queue.cc b/sql/event_queue.cc index c551cf74095..6016048aa9c 100644 --- a/sql/event_queue.cc +++ b/sql/event_queue.cc @@ -530,9 +530,10 @@ Event_queue::empty_queue() */ void -Event_queue::dbug_dump_queue(time_t now) +Event_queue::dbug_dump_queue(my_time_t when) { #ifndef DBUG_OFF + my_time_t now= when; Event_queue_element *et; uint i; DBUG_ENTER("Event_queue::dbug_dump_queue"); @@ -618,9 +619,9 @@ Event_queue::get_top_for_execution_if_time(THD *thd, time or until signaled. Release LOCK_queue while waiting. */ struct timespec top_time; - set_timespec(top_time, next_activation_at - thd->query_start()); - cond_wait(thd, &top_time, queue_wait_msg, SCHED_FUNC, __LINE__); + set_timespec_time_nsec(top_time, next_activation_at*1000000000ULL); + cond_wait(thd, &top_time, queue_wait_msg, SCHED_FUNC, __LINE__); continue; } diff --git a/sql/event_queue.h b/sql/event_queue.h index 2870ecb4d0b..e1f814adf20 100644 --- a/sql/event_queue.h +++ b/sql/event_queue.h @@ -98,7 +98,7 @@ private: void - dbug_dump_queue(time_t now); + dbug_dump_queue(my_time_t now); /* LOCK_event_queue is the mutex which protects the access to the queue. */ pthread_mutex_t LOCK_event_queue; diff --git a/sql/field.cc b/sql/field.cc index c9851b8af50..7976c8b0e3c 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2010, Oracle and/or its affiliates. - + Copyright (c) 2009-2011 Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. @@ -47,6 +47,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=','; @@ -1408,13 +1419,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. @@ -1432,15 +1436,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); @@ -1471,16 +1469,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; @@ -1750,19 +1742,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->ptr(), res->length(), ltime, - table->in_use->variables.sql_mode & - (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | - MODE_INVALID_DATES))) - return 1; - return 0; -} - /** This is called when storing a date in a string. @@ -1774,7 +1753,7 @@ int Field::store_time(MYSQL_TIME *ltime, timestamp_type type_arg) { 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, decimals()); return store(buff, length, &my_charset_bin); } @@ -2935,13 +2914,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; @@ -3163,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; } @@ -3215,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; } @@ -3270,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; } @@ -3286,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; } @@ -3299,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; } @@ -3319,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, @@ -3347,18 +3287,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 : @@ -3368,24 +3298,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 @@ -3600,14 +3517,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; } @@ -3652,14 +3562,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; } @@ -3705,14 +3608,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; } @@ -3721,12 +3617,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; } @@ -3736,12 +3627,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; } @@ -3755,12 +3641,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); @@ -3782,18 +3663,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; @@ -3801,28 +3672,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]; } @@ -3856,14 +3712,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; } @@ -3879,14 +3728,7 @@ int Field_longlong::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) - { - int8store(ptr,res); - } - else -#endif - longlongstore(ptr,res); + int8store(ptr,res); return error; } @@ -3910,14 +3752,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; } @@ -3926,14 +3761,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) { @@ -3948,12 +3776,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; } @@ -3967,12 +3790,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); @@ -3993,18 +3811,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; @@ -4013,36 +3821,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]; } @@ -4059,43 +3848,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 ****************************************************************************/ @@ -4134,14 +3886,7 @@ int Field_float::store(double nr) } float j= (float)nr; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - float4store(ptr,j); - } - else -#endif - memcpy_fixed(ptr,(uchar*) &j,sizeof(j)); + float4store(ptr,j); return error; } @@ -4157,28 +3902,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_fixed((uchar*) &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_fixed((uchar*) &j,ptr,sizeof(j)); + float4get(j,ptr); return (longlong) rint(j); } @@ -4188,14 +3919,7 @@ String *Field_float::val_str(String *val_buffer, { ASSERT_COLUMN_MARKED_FOR_READ; float nr; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - float4get(nr,ptr); - } - else -#endif - memcpy_fixed((uchar*) &nr,ptr,sizeof(nr)); + float4get(nr,ptr); uint to_length=max(field_length,70); val_buffer->alloc(to_length); @@ -4270,18 +3994,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_fixed(&a,a_ptr,sizeof(float)); - memcpy_fixed(&b,b_ptr,sizeof(float)); - } + float4get(a,a_ptr); + float4get(b,b_ptr); return (a < b) ? -1 : (a > b) ? 1 : 0; } @@ -4290,14 +4004,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_fixed(&nr,ptr,sizeof(float)); + float4get(nr,ptr); uchar *tmp= to; if (nr == (float) 0.0) @@ -4406,14 +4113,7 @@ int Field_double::store(double nr) } } -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - float8store(ptr,nr); - } - else -#endif - doublestore(ptr,nr); + float8store(ptr,nr); return error; } @@ -4544,14 +4244,7 @@ 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; } @@ -4561,14 +4254,7 @@ longlong Field_double::val_int(void) double j; longlong res; bool error; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { - float8get(j,ptr); - } - else -#endif - doubleget(j,ptr); + float8get(j,ptr); res= double_to_longlong(j, 0, &error); if (error) @@ -4598,14 +4284,7 @@ String *Field_double::val_str(String *val_buffer, { ASSERT_COLUMN_MARKED_FOR_READ; 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; val_buffer->alloc(to_length); @@ -4686,18 +4365,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; } @@ -4709,14 +4378,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); } @@ -4804,12 +4466,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; - if (!share->timestamp_field && unireg_check != NONE) + flags|= UNSIGNED_FLAG; + if (unireg_check != NONE && !share->timestamp_field) { /* This timestamp has auto-update */ share->timestamp_field= this; @@ -4820,20 +4482,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; - if (unireg_check != TIMESTAMP_DN_FIELD) - flags|= ON_UPDATE_NOW_FLAG; -} - - /** Get auto-set type for TIMESTAMP field. @@ -4868,136 +4516,139 @@ 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 Lazy_string *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; + uint error = 0; + my_time_t timestamp; - /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ - have_smth_to_conv= (str_to_datetime(from, len, &l_time, - (thd->variables.sql_mode & - MODE_NO_ZERO_DATE) | - MODE_NO_ZERO_IN_DATE, &error) > - MYSQL_TIMESTAMP_ERROR); - - 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 (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_time(MYSQL_TIME *ltime,timestamp_type time_type) +{ + THD *thd= table->in_use; + int unused; + MYSQL_TIME l_time= *ltime; + Lazy_string_time 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(const char *from,uint len,CHARSET_INFO *cs) +{ + MYSQL_TIME l_time; + int error; + int have_smth_to_conv; + Lazy_string_str str(from, len); + THD *thd= table->in_use; + + /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ + have_smth_to_conv= (str_to_datetime(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) { - 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; + MYSQL_TIME l_time; + int error; + Lazy_string_double str(nr); + THD *thd= table->in_use; + longlong tmp; + + /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ + if (nr < 0 || nr > LONGLONG_MAX) + nr= LONGLONG_MAX; + tmp= number_to_datetime((longlong) floor(nr), + &l_time, (thd->variables.sql_mode & + MODE_NO_ZERO_DATE) | + MODE_NO_ZERO_IN_DATE, &error); + l_time.second_part= (ulong)((nr-floor(nr))*TIME_SECOND_PART_FACTOR); + return store_TIME_with_warning(thd, &l_time, &str, error, tmp != -1); } int Field_timestamp::store(longlong nr, bool unsigned_val) { - 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; + Lazy_string_num 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, &l_time, (thd->variables.sql_mode & MODE_NO_ZERO_DATE) | MODE_NO_ZERO_IN_DATE, &error); - if (tmp == LL(-1)) - { - error= 2; - } - - 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; + 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 *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); + ulong sec_part; + my_time_t temp= get_timestamp(&sec_part); - if (temp == 0L) // No time - return(0); /* purecov: inspected */ + /* + Field_timestamp() and Field_timestamp_hres() shares this code. + This is why are also testing sec_part below. + */ + + if (temp == 0 && sec_part == 0) + return(0); thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, (my_time_t)temp); @@ -5009,10 +4660,9 @@ longlong Field_timestamp::val_int(void) String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) { - ASSERT_COLUMN_MARKED_FOR_READ; - uint32 temp, temp2; + uint32 temp2; + THD *thd= table->in_use; MYSQL_TIME time_tmp; - THD *thd= table ? table->in_use : current_thd; char *to; val_buffer->alloc(field_length+1); @@ -5020,20 +4670,16 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_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); + ulong sec_part; + my_time_t temp= get_timestamp(&sec_part); - if (temp == 0L) + if (temp == 0 && sec_part == 0) { /* Zero time is "000000" */ - val_ptr->set(STRING_WITH_LEN("0000-00-00 00:00:00"), &my_charset_bin); + val_ptr->set(zero_timestamp, field_length, &my_charset_bin); return val_ptr; } val_buffer->set_charset(&my_charset_bin); // Safety - + thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,(my_time_t)temp); temp= time_tmp.year % 100; @@ -5082,16 +4728,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; @@ -5100,61 +4741,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]; } @@ -5164,152 +4779,423 @@ 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) { - MYSQL_TIME ltime; - long tmp; - int error= 0; - int warning; + 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); + } +} - if (str_to_time(from, len, <ime, - table->in_use->variables.sql_mode & - (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | - MODE_INVALID_DATES), &warning)) - { - tmp=0L; - error= 2; - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, - from, len, MYSQL_TIMESTAMP_TIME, 1); +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; } - 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); +} +#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 time_tmp; + THD *thd= table->in_use; + + thd->time_zone_used= 1; + ulong sec_part; + my_time_t temp= get_timestamp(&sec_part); + + if (temp == 0 && sec_part == 0) + return(0); - if (ltime.neg) - tmp= -tmp; - int3store(ptr,tmp); - return error; + thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, (my_time_t)temp); + + return time_tmp.year * 1e10 + time_tmp.month * 1e8 + + time_tmp.day * 1e6 + time_tmp.hour * 1e4 + + time_tmp.minute * 1e2 + time_tmp.second + sec_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; -int Field_time::store_time(MYSQL_TIME *ltime, timestamp_type time_type) + 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; +} + + +int Field_timestamp_hires::store_decimal(const my_decimal *d) { - 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); + char buff[DECIMAL_MAX_STR_LENGTH+1]; + String str(buff, sizeof(buff), &my_charset_bin); + my_decimal2string(E_DEC_FATAL_ERROR, d, + MAX_DATETIME_COMPRESSED_WIDTH + MAX_DATETIME_PRECISION, + 6, '0', &str); + return store(str.ptr(), str.length(), str.charset()); } +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(double nr) +bool Field_timestamp_hires::send_binary(Protocol *protocol) +{ + MYSQL_TIME ltime; + Field_timestamp::get_date(<ime, 0); + return protocol->store(<ime, dec); +} + + +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 Lazy_string *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 && + temporal_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) && + temporal_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) && + temporal_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, temporal_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, temporal_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; + Lazy_string_str str(from, len); + + func_res= str_to_datetime(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)) + MYSQL_TIME ltime; + THD *thd= table->in_use; + Lazy_string_double str(nr); + + if (nr < 0 || nr > LONGLONG_MAX) + nr= LONGLONG_MAX; + longlong tmp= number_to_datetime((longlong) floor(nr), <ime, + (TIME_FUZZY_DATE | + (thd->variables.sql_mode & + (MODE_NO_ZERO_IN_DATE | + MODE_NO_ZERO_DATE | + MODE_INVALID_DATES))), &error); + ltime.second_part= (ulong)((nr-floor(nr))*TIME_SECOND_PART_FACTOR); + 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; + Lazy_string_num str(nr); + + tmp= number_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_time(MYSQL_TIME *ltime,timestamp_type time_type) +{ + int error = 0, have_smth_to_conv= 1; + MYSQL_TIME l_time= *ltime; + Lazy_string_time str(ltime); + /* + 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= TIME_MAX_VALUE; - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_DATA_OUT_OF_RANGE, nr, - MYSQL_TIMESTAMP_TIME, 1); - error= 1; + 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); } else { - 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; - } + error= 1; + have_smth_to_conv= 0; } + return store_TIME_with_warning(&l_time, &str, error, have_smth_to_conv); +} + +/**************************************************************************** +** 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; + Lazy_string_str str(from, len); + int was_cut; + int have_smth_to_conv= + str_to_time(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(MYSQL_TIME *ltime, timestamp_type time_type) +{ + MYSQL_TIME l_time= *ltime; + Lazy_string_time 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; + Lazy_string_double str(nr); + int was_cut; + int have_smth_to_conv= !number_to_time(nr, <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; + Lazy_string_num str(nr); + int was_cut; + int have_smth_to_conv= !number_to_time((double)nr, <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; @@ -5335,7 +5221,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) @@ -5343,6 +5228,7 @@ 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); @@ -5362,8 +5248,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, @@ -5371,12 +5257,6 @@ bool Field_time::get_date(MYSQL_TIME *ltime, uint fuzzydate) thd->row_count); 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) @@ -5397,11 +5277,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); } @@ -5425,6 +5303,94 @@ void Field_time::sql_type(String &res) const res.set_ascii(STRING_WITH_LEN("time")); } +void Field_time_hires::store_TIME(MYSQL_TIME *ltime) +{ + ulonglong packed= sec_part_shift(pack_time(ltime), dec); + store_bigendian(packed, ptr, Field_time_hires::pack_length()); +} + +uint32 Field_time_hires::pack_length() const +{ + return time_hires_bytes[dec]; +} + +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); + + /* sign extension */ + longlong mask= 1LL << (len*8 - 1); + if (packed & mask) + packed|= ~(mask-1); + + unpack_time(sec_part_unshift(packed, dec), ltime); + /* + unpack_time() returns MYSQL_TIMESTAMP_DATETIME. + To get MYSQL_TIMESTAMP_TIME we few some 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 @@ -5554,102 +5520,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) -{ - 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(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; -} - - -int Field_date::store(double nr) +void Field_date::store_TIME(MYSQL_TIME *ltime) { - 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; + uint tmp= ltime->year*10000L + ltime->month*100+ltime->day; + int4store(ptr,tmp); } - bool Field_date::send_binary(Protocol *protocol) { longlong tmp= Field_date::val_int(); @@ -5665,12 +5541,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; } @@ -5679,12 +5550,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; } @@ -5694,14 +5560,8 @@ 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); @@ -5711,50 +5571,21 @@ String *Field_date::val_str(String *val_buffer, } -bool Field_date::get_time(MYSQL_TIME *ltime) -{ - bzero((char *)ltime, sizeof(MYSQL_TIME)); - return 0; -} - - 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 @@ -5769,153 +5600,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(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; } @@ -5980,14 +5668,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; } @@ -6021,150 +5706,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) +void Field_datetime::store_TIME(MYSQL_TIME *ltime) { - 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(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) -{ - 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(); @@ -6174,12 +5729,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; } @@ -6187,20 +5737,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 @@ -6249,59 +5795,32 @@ 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) != 0; + 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[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]; - } - 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]; - } + 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]; } @@ -6310,6 +5829,101 @@ 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, pack_length()); +} + +int Field_datetime_hires::store_decimal(const my_decimal *d) +{ + char buff[DECIMAL_MAX_STR_LENGTH+1]; + String str(buff, sizeof(buff), &my_charset_bin); + my_decimal2string(E_DEC_FATAL_ERROR, d, + MAX_DATETIME_COMPRESSED_WIDTH + MAX_DATETIME_PRECISION, + 6, '0', &str); + return store(str.ptr(), str.length(), str.charset()); +} + +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); +} + + +double Field_datetime_hires::val_real(void) +{ + 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, 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, pack_length()); + ulonglong b=read_bigendian(b_ptr, 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 == 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; +} + /**************************************************************************** ** string type ** A string may be varchar or binary @@ -6765,9 +6379,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; @@ -6809,10 +6421,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; @@ -7274,9 +6883,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); @@ -7296,8 +6903,7 @@ uchar *Field_varstring::pack(uchar *to, const uchar *from, uchar * -Field_varstring::pack_key(uchar *to, const uchar *key, uint max_length, - bool low_byte_first __attribute__((unused))) +Field_varstring::pack_key(uchar *to, const uchar *key, uint max_length) { uint length= length_bytes == 1 ? (uint) *key : uint2korr(key); uint local_char_length= ((field_charset->mbmaxlen > 1) ? @@ -7334,8 +6940,7 @@ Field_varstring::pack_key(uchar *to, const uchar *key, uint max_length, */ const uchar * -Field_varstring::unpack_key(uchar *to, const uchar *key, uint max_length, - bool low_byte_first __attribute__((unused))) +Field_varstring::unpack_key(uchar *to, const uchar *key, uint max_length) { /* get length of the blob key */ uint32 length= *key++; @@ -7362,9 +6967,8 @@ Field_varstring::unpack_key(uchar *to, const uchar *key, uint max_length, end of key storage */ -uchar * -Field_varstring::pack_key_from_key_image(uchar *to, const uchar *from, uint max_length, - bool low_byte_first __attribute__((unused))) +uchar * Field_varstring::pack_key_from_key_image(uchar *to, const uchar *from, + uint max_length) { /* Key length is always stored as 2 bytes */ uint length= uint2korr(from); @@ -7395,9 +6999,7 @@ Field_varstring::pack_key_from_key_image(uchar *to, const uchar *from, uint max_ @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)) ? @@ -7623,103 +7225,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); } @@ -8058,20 +7572,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_fixed(&blob,ptr+packlength,sizeof(char*)); @@ -8101,14 +7602,11 @@ 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) { DBUG_ENTER("Field_blob::pack"); - DBUG_PRINT("enter", ("to: 0x%lx; from: 0x%lx;" - " max_length: %u; low_byte_first: %d", - (ulong) to, (ulong) from, - max_length, low_byte_first)); + DBUG_PRINT("enter", ("to: 0x%lx; from: 0x%lx; max_length: %u", + (ulong) to, (ulong) from, max_length)); DBUG_DUMP("record", from, table->s->reclength); uchar *save= ptr; ptr= (uchar*) from; @@ -8119,7 +7617,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. @@ -8152,18 +7650,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, @@ -8220,8 +7714,7 @@ int Field_blob::pack_cmp(const uchar *b, uint key_length_arg, /** Create a packed key that will be used for storage from a MySQL row. */ uchar * -Field_blob::pack_key(uchar *to, const uchar *from, uint max_length, - bool low_byte_first __attribute__((unused))) +Field_blob::pack_key(uchar *to, const uchar *from, uint max_length) { uchar *save= ptr; ptr= (uchar*) from; @@ -8265,8 +7758,7 @@ Field_blob::pack_key(uchar *to, const uchar *from, uint max_length, */ const uchar * -Field_blob::unpack_key(uchar *to, const uchar *from, uint max_length, - bool low_byte_first __attribute__((unused))) +Field_blob::unpack_key(uchar *to, const uchar *from, uint max_length) { /* get length of the blob key */ uint32 length= *from++; @@ -8274,7 +7766,7 @@ Field_blob::unpack_key(uchar *to, const uchar *from, uint max_length, length+= *from++ << 8; /* put the length into the record buffer */ - put_length(to, length); + store_length(to, packlength, length); /* put the address of the blob buffer or NULL */ if (length) @@ -8289,9 +7781,8 @@ Field_blob::unpack_key(uchar *to, const uchar *from, uint max_length, /** Create a packed key that will be used for storage from a MySQL key. */ -uchar * -Field_blob::pack_key_from_key_image(uchar *to, const uchar *from, uint max_length, - bool low_byte_first __attribute__((unused))) +uchar *Field_blob::pack_key_from_key_image(uchar *to, const uchar *from, + uint max_length) { uint length=uint2korr(from); if (length > max_length) @@ -8442,39 +7933,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); } @@ -8560,46 +8019,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); } @@ -9352,8 +8772,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; @@ -9400,8 +8819,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) { uint const from_len= (param_data >> 8U) & 0x00ff; uint const from_bit_len= param_data & 0x00ff; @@ -9858,28 +9276,14 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, } break; case MYSQL_TYPE_TIMESTAMP: - if (fld_length == NULL) + if (length > MAX_DATETIME_PRECISION) { - length= MAX_DATETIME_WIDTH; - } - else if (length != MAX_DATETIME_WIDTH) - { - /* - 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) { @@ -9927,10 +9331,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: { @@ -10050,14 +9466,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; @@ -10229,9 +9653,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); @@ -10242,11 +9669,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); @@ -10427,7 +9862,7 @@ Field::set_warning(MYSQL_ERROR::enum_warning_level level, uint code, If this field was created only for type conversion purposes it will have table == NULL. */ - THD *thd= table ? table->in_use : current_thd; + THD *thd= table->in_use; if (thd->count_cuted_fields) { thd->cuted_fields+= cuted_increment; @@ -10445,7 +9880,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 @@ -10453,80 +9887,18 @@ 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. + + See also bug#2336 */ -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) + +void Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, + uint code, const Lazy_string *str, + timestamp_type ts_type, int cuted_increment) { - THD *thd= table ? table->in_use : current_thd; + THD *thd= table->in_use; 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 - - @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) -{ - 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); - } -} - - -/** - 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. -*/ - -void -Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, uint code, - double nr, timestamp_type ts_type) -{ - 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= my_sprintf(str_nr, (str_nr, "%g", nr)); - make_truncated_value_warning(thd, level, str_nr, str_len, ts_type, - field_name); - } + make_truncated_value_warning(thd, level, str, ts_type, field_name); } diff --git a/sql/field.h b/sql/field.h index 3e3dcc10970..f5dd5d5ea3f 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1,4 +1,5 @@ /* Copyright (c) 2000, 2010 Oracle and/or its affiliates. + Copyright (c) 2009-2011 Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,7 +26,6 @@ #endif #define NOT_FIXED_DEC 31 -#define DATETIME_DEC 6 const uint32 max_field_size= (uint32) 4294967295U; class Send_field; @@ -232,7 +232,6 @@ public: virtual bool str_needs_quotes() { return FALSE; } virtual Item_result result_type () const=0; virtual Item_result cmp_type () const { return result_type(); } - virtual Item_result cast_to_int_type () const { return result_type(); } static bool type_can_have_key_part(enum_field_types); static enum_field_types field_type_merge(enum_field_types, enum_field_types); static Item_result result_merge_type(enum_field_types); @@ -387,14 +386,7 @@ public: virtual void make_field(Send_field *); virtual void sort_string(uchar *buff,uint length)=0; virtual bool optimize_range(uint idx, uint part); - /* - This should be true for fields which, when compared with constant - items, can be casted to longlong. In this case we will at 'fix_fields' - stage cast the constant items to longlongs and at the execution stage - use field->val_int() for comparison. Used to optimize clauses like - 'a_column BETWEEN date_const, date_const'. - */ - virtual bool can_be_compared_as_longlong() const { return FALSE; } + virtual bool special_const_compare() const { return FALSE; } virtual void free() {} virtual Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type); @@ -478,44 +470,39 @@ public: } virtual bool send_binary(Protocol *protocol); - virtual uchar *pack(uchar *to, const uchar *from, - uint max_length, bool low_byte_first); + virtual uchar *pack(uchar *to, const uchar *from, uint max_length); /** @overload Field::pack(uchar*, const uchar*, uint, bool) */ uchar *pack(uchar *to, const uchar *from) { DBUG_ENTER("Field::pack"); - uchar *result= this->pack(to, from, UINT_MAX, table->s->db_low_byte_first); + uchar *result= this->pack(to, from, UINT_MAX); DBUG_RETURN(result); } - virtual const uchar *unpack(uchar* to, const uchar *from, - uint param_data, bool low_byte_first); + virtual const uchar *unpack(uchar* to, const uchar *from, uint param_data); /** @overload Field::unpack(uchar*, const uchar*, uint, bool) */ const uchar *unpack(uchar* to, const uchar *from) { DBUG_ENTER("Field::unpack"); - const uchar *result= unpack(to, from, 0U, table->s->db_low_byte_first); + const uchar *result= unpack(to, from, 0); DBUG_RETURN(result); } - virtual uchar *pack_key(uchar* to, const uchar *from, - uint max_length, bool low_byte_first) + virtual uchar *pack_key(uchar* to, const uchar *from, uint max_length) { - return pack(to, from, max_length, low_byte_first); + return pack(to, from, max_length); } - virtual uchar *pack_key_from_key_image(uchar* to, const uchar *from, - uint max_length, bool low_byte_first) + virtual uchar *pack_key_from_key_image(uchar* to, const uchar *from, uint max_length) { - return pack(to, from, max_length, low_byte_first); + return pack(to, from, max_length); } - virtual const uchar *unpack_key(uchar* to, const uchar *from, - uint max_length, bool low_byte_first) + virtual const uchar *unpack_key(uchar* to, const uchar *from, uint max_length) { - return unpack(to, from, max_length, low_byte_first); + return unpack(to, from, max_length); } virtual uint packed_col_length(const uchar *to, uint length) { return length;} @@ -535,7 +522,7 @@ public: void copy_from_tmp(int offset); uint fill_cache_field(struct st_cache_field *copy); virtual bool get_date(MYSQL_TIME *ltime,uint fuzzydate); - virtual bool get_time(MYSQL_TIME *ltime); + bool get_time(MYSQL_TIME *ltime) { return get_date(ltime, TIME_TIME_ONLY); } virtual CHARSET_INFO *charset(void) const { return &my_charset_bin; } virtual CHARSET_INFO *sort_charset(void) const { return charset(); } virtual bool has_charset(void) const { return FALSE; } @@ -543,16 +530,12 @@ public: virtual enum Derivation derivation(void) const { return DERIVATION_IMPLICIT; } virtual void set_derivation(enum Derivation derivation_arg) { } + virtual int set_time() { return 1; } bool set_warning(MYSQL_ERROR::enum_warning_level, unsigned int code, int cuted_increment); void set_datetime_warning(MYSQL_ERROR::enum_warning_level, uint code, - const char *str, uint str_len, - timestamp_type ts_type, int cuted_increment); - void set_datetime_warning(MYSQL_ERROR::enum_warning_level, uint code, - longlong nr, timestamp_type ts_type, + const Lazy_string *str, timestamp_type ts_type, int cuted_increment); - void set_datetime_warning(MYSQL_ERROR::enum_warning_level, const uint code, - double nr, timestamp_type ts_type); inline bool check_overflow(int op_result) { return (op_result == E_DEC_OVERFLOW); @@ -648,70 +631,44 @@ protected: /* Helper function to pack()/unpack() int32 values */ - static void handle_int32(uchar *to, const uchar *from, - bool low_byte_first_from, bool low_byte_first_to) + static void handle_int32(uchar *to, const uchar *from) { int32 val; -#ifdef WORDS_BIGENDIAN - if (low_byte_first_from) - val = sint4korr(from); - else -#endif - longget(val, from); - -#ifdef WORDS_BIGENDIAN - if (low_byte_first_to) - int4store(to, val); - else -#endif - longstore(to, val); + val = sint4korr(from); + int4store(to, val); } /* Helper function to pack()/unpack() int64 values */ - static void handle_int64(uchar* to, const uchar *from, - bool low_byte_first_from, bool low_byte_first_to) + static void handle_int64(uchar* to, const uchar *from) { int64 val; -#ifdef WORDS_BIGENDIAN - if (low_byte_first_from) - val = sint8korr(from); - else -#endif - longlongget(val, from); - -#ifdef WORDS_BIGENDIAN - if (low_byte_first_to) - int8store(to, val); - else -#endif - longlongstore(to, val); + val = sint8korr(from); + int8store(to, val); } - uchar *pack_int32(uchar *to, const uchar *from, bool low_byte_first_to) + uchar *pack_int32(uchar *to, const uchar *from) { - handle_int32(to, from, table->s->db_low_byte_first, low_byte_first_to); + handle_int32(to, from); return to + sizeof(int32); } - const uchar *unpack_int32(uchar* to, const uchar *from, - bool low_byte_first_from) + const uchar *unpack_int32(uchar* to, const uchar *from) { - handle_int32(to, from, low_byte_first_from, table->s->db_low_byte_first); + handle_int32(to, from); return from + sizeof(int32); } - uchar *pack_int64(uchar* to, const uchar *from, bool low_byte_first_to) + uchar *pack_int64(uchar* to, const uchar *from) { - handle_int64(to, from, table->s->db_low_byte_first, low_byte_first_to); + handle_int64(to, from); return to + sizeof(int64); } - const uchar *unpack_int64(uchar* to, const uchar *from, - bool low_byte_first_from) + const uchar *unpack_int64(uchar* to, const uchar *from) { - handle_int64(to, from, low_byte_first_from, table->s->db_low_byte_first); + handle_int64(to, from); return from + sizeof(int64); } @@ -779,7 +736,6 @@ public: bool eq_cmp_as_binary() { return test(flags & BINARY_FLAG); } }; - /* base class for Field_string, Field_varstring and Field_blob */ class Field_longstr :public Field_str @@ -816,10 +772,6 @@ public: my_decimal *val_decimal(my_decimal *); uint32 max_display_length() { return field_length; } uint size_of() const { return sizeof(*this); } - virtual const uchar *unpack(uchar* to, const uchar *from, - uint param_data, bool low_byte_first); - virtual uchar *pack(uchar* to, const uchar *from, - uint max_length, bool low_byte_first); }; @@ -848,15 +800,13 @@ public: void overflow(bool negative); bool zero_pack() const { return 0; } void sql_type(String &str) const; - virtual const uchar *unpack(uchar* to, const uchar *from, - uint param_data, bool low_byte_first) + virtual const uchar *unpack(uchar* to, const uchar *from, uint param_data) { - return Field::unpack(to, from, param_data, low_byte_first); + return Field::unpack(to, from, param_data); } - virtual uchar *pack(uchar* to, const uchar *from, - uint max_length, bool low_byte_first) + virtual uchar *pack(uchar* to, const uchar *from, uint max_length) { - return Field::pack(to, from, max_length, low_byte_first); + return Field::pack(to, from, max_length); } }; @@ -909,8 +859,7 @@ public: int compatible_field_size(uint field_metadata, const Relay_log_info *rli, uint16 mflags); uint is_equal(Create_field *new_field); - virtual const uchar *unpack(uchar* to, const uchar *from, - uint param_data, bool low_byte_first); + virtual const uchar *unpack(uchar* to, const uchar *from, uint param_data); static Field *create_from_item (Item *); }; @@ -943,15 +892,13 @@ public: void sql_type(String &str) const; uint32 max_display_length() { return 4; } - virtual uchar *pack(uchar* to, const uchar *from, - uint max_length, bool low_byte_first) + virtual uchar *pack(uchar* to, const uchar *from, uint max_length) { *to= *from; return to + 1; } - virtual const uchar *unpack(uchar* to, const uchar *from, - uint param_data, bool low_byte_first) + virtual const uchar *unpack(uchar* to, const uchar *from, uint param_data) { *to= *from; return from + 1; @@ -992,43 +939,19 @@ public: void sql_type(String &str) const; uint32 max_display_length() { return 6; } - virtual uchar *pack(uchar* to, const uchar *from, - uint max_length, bool low_byte_first) + virtual uchar *pack(uchar* to, const uchar *from, uint max_length) { int16 val; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - val = sint2korr(from); - else -#endif - shortget(val, from); - -#ifdef WORDS_BIGENDIAN - if (low_byte_first) - int2store(to, val); - else -#endif - shortstore(to, val); + val = sint2korr(from); + int2store(to, val); return to + sizeof(val); } - virtual const uchar *unpack(uchar* to, const uchar *from, - uint param_data, bool low_byte_first) + virtual const uchar *unpack(uchar* to, const uchar *from, uint param_data) { int16 val; -#ifdef WORDS_BIGENDIAN - if (low_byte_first) - val = sint2korr(from); - else -#endif - shortget(val, from); - -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - int2store(to, val); - else -#endif - shortstore(to, val); + val = sint2korr(from); + int2store(to, val); return from + sizeof(val); } }; @@ -1061,16 +984,14 @@ public: void sql_type(String &str) const; uint32 max_display_length() { return 8; } - virtual uchar *pack(uchar* to, const uchar *from, - uint max_length, bool low_byte_first) + virtual uchar *pack(uchar* to, const uchar *from, uint max_length) { - return Field::pack(to, from, max_length, low_byte_first); + return Field::pack(to, from, max_length); } - virtual const uchar *unpack(uchar* to, const uchar *from, - uint param_data, bool low_byte_first) + virtual const uchar *unpack(uchar* to, const uchar *from, uint param_data) { - return Field::unpack(to, from, param_data, low_byte_first); + return Field::unpack(to, from, param_data); } }; @@ -1108,21 +1029,18 @@ public: void sql_type(String &str) const; uint32 max_display_length() { return MY_INT32_NUM_DECIMAL_DIGITS; } virtual uchar *pack(uchar* to, const uchar *from, - uint max_length __attribute__((unused)), - bool low_byte_first) + uint max_length __attribute__((unused))) { - return pack_int32(to, from, low_byte_first); + return pack_int32(to, from); } virtual const uchar *unpack(uchar* to, const uchar *from, - uint param_data __attribute__((unused)), - bool low_byte_first) + uint param_data __attribute__((unused))) { - return unpack_int32(to, from, low_byte_first); + return unpack_int32(to, from); } }; -#ifdef HAVE_LONG_LONG class Field_longlong :public Field_num { public: Field_longlong(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, @@ -1159,22 +1077,18 @@ public: void sort_string(uchar *buff,uint length); uint32 pack_length() const { return 8; } void sql_type(String &str) const; - bool can_be_compared_as_longlong() const { return TRUE; } uint32 max_display_length() { return 20; } virtual uchar *pack(uchar* to, const uchar *from, - uint max_length __attribute__((unused)), - bool low_byte_first) + uint max_length __attribute__((unused))) { - return pack_int64(to, from, low_byte_first); + return pack_int64(to, from); } virtual const uchar *unpack(uchar* to, const uchar *from, - uint param_data __attribute__((unused)), - bool low_byte_first) + uint param_data __attribute__((unused))) { - return unpack_int64(to, from, low_byte_first); + return unpack_int64(to, from); } }; -#endif class Field_float :public Field_real { @@ -1281,10 +1195,13 @@ public: void sql_type(String &str) const; uint size_of() const { return sizeof(*this); } uint32 max_display_length() { return 4; } + void move_field_offset(my_ptrdiff_t ptr_diff) {} }; class Field_timestamp :public Field_str { + int store_TIME_with_warning(THD *, MYSQL_TIME *, const Lazy_string *, + bool, bool); public: Field_timestamp(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, @@ -1294,11 +1211,11 @@ public: CHARSET_INFO *cs); enum_field_types type() const { return MYSQL_TYPE_TIMESTAMP;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; } - enum Item_result cmp_type () const { return INT_RESULT; } + enum Item_result cmp_type () const { return TIME_RESULT; } int store(const char *to,uint length,CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); - int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; } + int store_time(MYSQL_TIME *ltime, timestamp_type type); double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -1307,9 +1224,9 @@ public: void sort_string(uchar *buff,uint length); uint32 pack_length() const { return 4; } void sql_type(String &str) const; - bool can_be_compared_as_longlong() const { return TRUE; } bool zero_pack() const { return 0; } - void set_time(); + uint decimals() const { return 0; } + virtual int set_time(); virtual void set_default() { if (table->timestamp_field == this && @@ -1319,42 +1236,63 @@ public: Field::set_default(); } /* Get TIMESTAMP field value as seconds since begging of Unix Epoch */ - inline long get_timestamp(bool *null_value) + virtual my_time_t get_timestamp(ulong *sec_part) const; + virtual void store_TIME(my_time_t timestamp, ulong sec_part) { - if ((*null_value= is_null())) - return 0; -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) - return sint4korr(ptr); -#endif - long tmp; - longget(tmp,ptr); - return tmp; - } - inline void store_timestamp(my_time_t timestamp) - { -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) - { - int4store(ptr,timestamp); - } - else -#endif - longstore(ptr,(uint32) timestamp); + int4store(ptr,timestamp); } bool get_date(MYSQL_TIME *ltime,uint fuzzydate); - bool get_time(MYSQL_TIME *ltime); timestamp_auto_set_type get_auto_set_type() const; uchar *pack(uchar *to, const uchar *from, - uint max_length __attribute__((unused)), bool low_byte_first) + uint max_length __attribute__((unused))) { - return pack_int32(to, from, low_byte_first); + return pack_int32(to, from); } const uchar *unpack(uchar* to, const uchar *from, - uint param_data __attribute__((unused)), - bool low_byte_first) + uint param_data __attribute__((unused))) + { + return unpack_int32(to, from); + } +}; + + +class Field_timestamp_hires :public Field_timestamp { + uint dec; +public: + Field_timestamp_hires(uchar *ptr_arg, + uchar *null_ptr_arg, uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + TABLE_SHARE *share, uint dec_arg, CHARSET_INFO *cs) : + Field_timestamp(ptr_arg, MAX_DATETIME_WIDTH + dec_arg + 1, null_ptr_arg, + null_bit_arg, unireg_check_arg, field_name_arg, share, cs), + dec(dec_arg) + { + DBUG_ASSERT(dec); + DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS); + } + void sql_type(String &str) const; + my_time_t get_timestamp(ulong *sec_part) const; + void store_TIME(my_time_t timestamp, ulong sec_part); + int store_decimal(const my_decimal *d); + double val_real(void); + String *val_str(String*,String *); + bool send_binary(Protocol *protocol); + int cmp(const uchar *,const uchar *); + void sort_string(uchar *buff,uint length); + uint decimals() const { return dec; } + int set_time(); + enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } + void make_field(Send_field *field); + uint32 pack_length() const; + uchar *pack(uchar *to, const uchar *from, uint max_length) + { return Field::pack(to, from, max_length); } + const uchar *unpack(uchar* to, const uchar *from, uint param_data) + { return Field::unpack(to, from, param_data); } + uint size_of() const { return sizeof(*this); } + bool eq_def(Field *field) { - return unpack_int32(to, from, low_byte_first); + return (Field_str::eq_def(field) && + dec == ((Field_timestamp_hires*) field)->dec); } }; @@ -1375,76 +1313,85 @@ public: longlong val_int(void); String *val_str(String*,String *); bool send_binary(Protocol *protocol); + uint32 max_display_length() { return field_length; } + /* 99 should compare with 1999 */ + bool special_const_compare() const { return TRUE; } void sql_type(String &str) const; - bool can_be_compared_as_longlong() const { return TRUE; } }; -class Field_date :public Field_str { +class Field_temporal: public Field_str { +protected: + int store_TIME_with_warning(MYSQL_TIME *ltime, const Lazy_string *str, + int was_cut, int have_smth_to_conv); + virtual void store_TIME(MYSQL_TIME *ltime) = 0; + virtual timestamp_type temporal_type() = 0; +public: + Field_temporal(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, utype unireg_check_arg, + const char *field_name_arg, CHARSET_INFO *charset_arg) + :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, + field_name_arg, charset_arg) { } + enum Item_result cmp_type () const { return TIME_RESULT; } + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int store_time(MYSQL_TIME *ltime, timestamp_type type); + bool special_const_compare() const { return TRUE; } +}; + +class Field_date :public Field_temporal { + void store_TIME(MYSQL_TIME *ltime); + timestamp_type temporal_type() + { return MYSQL_TIMESTAMP_DATE; } public: Field_date(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, CHARSET_INFO *cs) - :Field_str(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, cs) + :Field_temporal(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, cs) {} - Field_date(bool maybe_null_arg, const char *field_name_arg, - CHARSET_INFO *cs) - :Field_str((uchar*) 0, MAX_DATE_WIDTH, maybe_null_arg ? (uchar*) "": 0,0, - NONE, field_name_arg, cs) {} enum_field_types type() const { return MYSQL_TYPE_DATE;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; } - enum Item_result cmp_type () const { return INT_RESULT; } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; } double val_real(void); longlong val_int(void); String *val_str(String*,String *); - bool get_time(MYSQL_TIME *ltime); + uint decimals() const { return 0; } bool send_binary(Protocol *protocol); int cmp(const uchar *,const uchar *); void sort_string(uchar *buff,uint length); uint32 pack_length() const { return 4; } void sql_type(String &str) const; - bool can_be_compared_as_longlong() const { return TRUE; } bool zero_pack() const { return 1; } uchar *pack(uchar* to, const uchar *from, - uint max_length __attribute__((unused)), bool low_byte_first) + uint max_length __attribute__((unused))) { - return pack_int32(to, from, low_byte_first); + return pack_int32(to, from); } const uchar *unpack(uchar* to, const uchar *from, - uint param_data __attribute__((unused)), - bool low_byte_first) + uint param_data __attribute__((unused))) { - return unpack_int32(to, from, low_byte_first); + return unpack_int32(to, from); } }; -class Field_newdate :public Field_str { +class Field_newdate :public Field_temporal { + void store_TIME(MYSQL_TIME *ltime); + timestamp_type temporal_type() { return MYSQL_TIMESTAMP_DATE; } public: Field_newdate(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, CHARSET_INFO *cs) - :Field_str(ptr_arg, 10, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, cs) + :Field_temporal(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, cs) {} - Field_newdate(bool maybe_null_arg, const char *field_name_arg, - CHARSET_INFO *cs) - :Field_str((uchar*) 0,10, maybe_null_arg ? (uchar*) "": 0,0, - NONE, field_name_arg, cs) {} enum_field_types type() const { return MYSQL_TYPE_DATE;} enum_field_types real_type() const { return MYSQL_TYPE_NEWDATE; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_UINT24; } - enum Item_result cmp_type () const { return INT_RESULT; } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - int store_time(MYSQL_TIME *ltime, timestamp_type type); int reset(void) { ptr[0]=ptr[1]=ptr[2]=0; return 0; } + uint decimals() const { return 0; } double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -1453,75 +1400,89 @@ public: void sort_string(uchar *buff,uint length); uint32 pack_length() const { return 3; } void sql_type(String &str) const; - bool can_be_compared_as_longlong() const { return TRUE; } bool zero_pack() const { return 1; } bool get_date(MYSQL_TIME *ltime,uint fuzzydate); - bool get_time(MYSQL_TIME *ltime); }; -class Field_time :public Field_str { +class Field_time :public Field_temporal { + void store_TIME(MYSQL_TIME *ltime); + timestamp_type temporal_type() + { return MYSQL_TIMESTAMP_TIME; } public: - Field_time(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - CHARSET_INFO *cs) - :Field_str(ptr_arg, 8, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, cs) + Field_time(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg, + uchar null_bit_arg, enum utype unireg_check_arg, + const char *field_name_arg, CHARSET_INFO *cs) + :Field_temporal(ptr_arg, length_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, cs) {} - Field_time(bool maybe_null_arg, const char *field_name_arg, - CHARSET_INFO *cs) - :Field_str((uchar*) 0,8, maybe_null_arg ? (uchar*) "": 0,0, - NONE, field_name_arg, cs) {} enum_field_types type() const { return MYSQL_TYPE_TIME;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_INT24; } - enum Item_result cmp_type () const { return INT_RESULT; } int store_time(MYSQL_TIME *ltime, timestamp_type type); int store(const char *to,uint length,CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); - int reset(void) { ptr[0]=ptr[1]=ptr[2]=0; return 0; } + uint decimals() const { return 0; } double val_real(void); longlong val_int(void); String *val_str(String*,String *); bool get_date(MYSQL_TIME *ltime, uint fuzzydate); bool send_binary(Protocol *protocol); - bool get_time(MYSQL_TIME *ltime); int cmp(const uchar *,const uchar *); void sort_string(uchar *buff,uint length); uint32 pack_length() const { return 3; } void sql_type(String &str) const; - bool can_be_compared_as_longlong() const { return TRUE; } bool zero_pack() const { return 1; } }; +class Field_time_hires :public Field_time { + uint dec; + void store_TIME(MYSQL_TIME *ltime); +public: + Field_time_hires(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + uint dec_arg, CHARSET_INFO *cs) + :Field_time(ptr_arg, MIN_TIME_WIDTH + dec_arg + 1, null_ptr_arg, + null_bit_arg, unireg_check_arg, field_name_arg, cs), + dec(dec_arg) + { + DBUG_ASSERT(dec); + DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS); + } + enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } + uint decimals() const { return dec; } + longlong val_int(void) { return (longlong)floor(val_real()); } + double val_real(void); + String *val_str(String*,String *); + bool get_date(MYSQL_TIME *ltime, uint fuzzydate); + bool send_binary(Protocol *protocol); + int cmp(const uchar *,const uchar *); + void sort_string(uchar *buff,uint length); + uint32 pack_length() const; + void sql_type(String &str) const; + void make_field(Send_field *); + uint size_of() const { return sizeof(*this); } + bool eq_def(Field *field) + { + return (Field_str::eq_def(field) && + dec == ((Field_time_hires*) field)->dec); + } +}; -class Field_datetime :public Field_str { +class Field_datetime :public Field_temporal { + void store_TIME(MYSQL_TIME *ltime); + timestamp_type temporal_type() + { return MYSQL_TIMESTAMP_DATETIME; } public: - Field_datetime(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - CHARSET_INFO *cs) - :Field_str(ptr_arg, MAX_DATETIME_WIDTH, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, cs) + Field_datetime(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg, + uchar null_bit_arg, enum utype unireg_check_arg, + const char *field_name_arg, CHARSET_INFO *cs) + :Field_temporal(ptr_arg, length_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, cs) {} - Field_datetime(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) {} enum_field_types type() const { return MYSQL_TYPE_DATETIME;} -#ifdef HAVE_LONG_LONG enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONGLONG; } -#endif - enum Item_result cmp_type () const { return INT_RESULT; } - uint decimals() const { return DATETIME_DEC; } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - int store_time(MYSQL_TIME *ltime, timestamp_type type); - int reset(void) - { - ptr[0]=ptr[1]=ptr[2]=ptr[3]=ptr[4]=ptr[5]=ptr[6]=ptr[7]=0; - return 0; - } + uint decimals() const { return 0; } double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -1530,23 +1491,102 @@ public: void sort_string(uchar *buff,uint length); uint32 pack_length() const { return 8; } void sql_type(String &str) const; - bool can_be_compared_as_longlong() const { return TRUE; } bool zero_pack() const { return 1; } bool get_date(MYSQL_TIME *ltime,uint fuzzydate); - bool get_time(MYSQL_TIME *ltime); uchar *pack(uchar* to, const uchar *from, - uint max_length __attribute__((unused)), bool low_byte_first) + uint max_length __attribute__((unused))) { - return pack_int64(to, from, low_byte_first); + return pack_int64(to, from); } const uchar *unpack(uchar* to, const uchar *from, - uint param_data __attribute__((unused)), - bool low_byte_first) + uint param_data __attribute__((unused))) + { + return unpack_int64(to, from); + } +}; + + +class Field_datetime_hires :public Field_datetime { + void store_TIME(MYSQL_TIME *ltime); + uint dec; +public: + Field_datetime_hires(uchar *ptr_arg, uchar *null_ptr_arg, + uchar null_bit_arg, enum utype unireg_check_arg, + const char *field_name_arg, uint dec_arg, + CHARSET_INFO *cs) + :Field_datetime(ptr_arg, MAX_DATETIME_WIDTH + dec_arg + 1, + null_ptr_arg, null_bit_arg, unireg_check_arg, + field_name_arg, cs), dec(dec_arg) + { + DBUG_ASSERT(dec); + DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS); + } + enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } + int store_decimal(const my_decimal *d); + uint decimals() const { return dec; } + void make_field(Send_field *field); + double val_real(void); + longlong val_int(void); + String *val_str(String*,String *); + bool send_binary(Protocol *protocol); + int cmp(const uchar *,const uchar *); + void sort_string(uchar *buff,uint length); + uint32 pack_length() const; + void sql_type(String &str) const; + bool get_date(MYSQL_TIME *ltime,uint fuzzydate); + uchar *pack(uchar *to, const uchar *from, uint max_length) + { return Field::pack(to, from, max_length); } + const uchar *unpack(uchar* to, const uchar *from, uint param_data) + { return Field::unpack(to, from, param_data); } + uint size_of() const { return sizeof(*this); } + bool eq_def(Field *field) { - return unpack_int64(to, from, low_byte_first); + return (Field_str::eq_def(field) && + dec == ((Field_datetime_hires*) field)->dec); } }; +static inline Field_timestamp * +new_Field_timestamp(uchar *ptr, uchar *null_ptr, uchar null_bit, + enum Field::utype unireg_check, const char *field_name, + TABLE_SHARE *share, uint dec, CHARSET_INFO *cs) +{ + if (dec==0) + return new Field_timestamp(ptr, MAX_DATETIME_WIDTH, null_ptr, null_bit, + unireg_check, field_name, share, cs); + if (dec == NOT_FIXED_DEC) + dec= MAX_DATETIME_PRECISION; + return new Field_timestamp_hires(ptr, null_ptr, null_bit, unireg_check, + field_name, share, dec, cs); +} + +static inline Field_time * +new_Field_time(uchar *ptr, uchar *null_ptr, uchar null_bit, + enum Field::utype unireg_check, const char *field_name, + uint dec, CHARSET_INFO *cs) +{ + if (dec == 0) + return new Field_time(ptr, MIN_TIME_WIDTH, null_ptr, null_bit, + unireg_check, field_name, cs); + if (dec == NOT_FIXED_DEC) + dec= MAX_DATETIME_PRECISION; + return new Field_time_hires(ptr, null_ptr, null_bit, + unireg_check, field_name, dec, cs); +} + +static inline Field_datetime * +new_Field_datetime(uchar *ptr, uchar *null_ptr, uchar null_bit, + enum Field::utype unireg_check, + const char *field_name, uint dec, CHARSET_INFO *cs) +{ + if (dec == 0) + return new Field_datetime(ptr, MAX_DATETIME_WIDTH, null_ptr, null_bit, + unireg_check, field_name, cs); + if (dec == NOT_FIXED_DEC) + dec= MAX_DATETIME_PRECISION; + return new Field_datetime_hires(ptr, null_ptr, null_bit, + unireg_check, field_name, dec, cs); +} class Field_string :public Field_longstr { public: @@ -1592,9 +1632,8 @@ public: void sort_string(uchar *buff,uint length); void sql_type(String &str) const; virtual uchar *pack(uchar *to, const uchar *from, - uint max_length, bool low_byte_first); - virtual const uchar *unpack(uchar* to, const uchar *from, - uint param_data, bool low_byte_first); + uint max_length); + virtual const uchar *unpack(uchar* to, const uchar *from, uint param_data); uint pack_length_from_metadata(uint field_metadata) { DBUG_PRINT("debug", ("field_metadata: 0x%04x", field_metadata)); @@ -1679,15 +1718,11 @@ public: uint get_key_image(uchar *buff,uint length, imagetype type); void set_key_image(const uchar *buff,uint length); void sql_type(String &str) const; - virtual uchar *pack(uchar *to, const uchar *from, - uint max_length, bool low_byte_first); - uchar *pack_key(uchar *to, const uchar *from, uint max_length, bool low_byte_first); - uchar *pack_key_from_key_image(uchar* to, const uchar *from, - uint max_length, bool low_byte_first); - virtual const uchar *unpack(uchar* to, const uchar *from, - uint param_data, bool low_byte_first); - const uchar *unpack_key(uchar* to, const uchar *from, - uint max_length, bool low_byte_first); + virtual uchar *pack(uchar *to, const uchar *from, uint max_length); + uchar *pack_key(uchar *to, const uchar *from, uint max_length); + uchar *pack_key_from_key_image(uchar* to, const uchar *from, uint max_length); + virtual const uchar *unpack(uchar* to, const uchar *from, uint param_data); + const uchar *unpack_key(uchar* to, const uchar *from, uint max_length); int pack_cmp(const uchar *a, const uchar *b, uint key_length, bool insert_or_update); int pack_cmp(const uchar *b, uint key_length,bool insert_or_update); @@ -1795,14 +1830,7 @@ public: int reset(void) { bzero(ptr, packlength+sizeof(uchar*)); return 0; } void reset_fields() { bzero((uchar*) &value,sizeof(value)); } uint32 get_field_buffer_size(void) { return value.alloced_length(); } -#ifndef WORDS_BIGENDIAN - static -#endif - void store_length(uchar *i_ptr, uint i_packlength, uint32 i_number, bool low_byte_first); - void store_length(uchar *i_ptr, uint i_packlength, uint32 i_number) - { - store_length(i_ptr, i_packlength, i_number, table->s->db_low_byte_first); - } + void store_length(uchar *i_ptr, uint i_packlength, uint32 i_number); inline void store_length(uint32 number) { store_length(ptr, packlength, number); @@ -1816,38 +1844,37 @@ public: @returns The length in the row plus the size of the data. */ - uint32 get_packed_size(const uchar *ptr_arg, bool low_byte_first) - {return packlength + get_length(ptr_arg, packlength, low_byte_first);} + uint32 get_packed_size(const uchar *ptr_arg) + {return packlength + get_length(ptr_arg, packlength);} inline uint32 get_length(uint row_offset= 0) - { return get_length(ptr+row_offset, this->packlength, table->s->db_low_byte_first); } - uint32 get_length(const uchar *ptr, uint packlength, bool low_byte_first); + { return get_length(ptr+row_offset, this->packlength); } + uint32 get_length(const uchar *ptr, uint packlength); uint32 get_length(const uchar *ptr_arg) - { return get_length(ptr_arg, this->packlength, table->s->db_low_byte_first); } - void put_length(uchar *pos, uint32 length); + { return get_length(ptr_arg, this->packlength); } inline void get_ptr(uchar **str) - { - memcpy_fixed((uchar*) str,ptr+packlength,sizeof(uchar*)); - } + { + memcpy_fixed((uchar*) str,ptr+packlength,sizeof(uchar*)); + } inline void get_ptr(uchar **str, uint row_offset) - { - memcpy_fixed((uchar*) str,ptr+packlength+row_offset,sizeof(char*)); - } + { + memcpy_fixed((uchar*) str,ptr+packlength+row_offset,sizeof(char*)); + } inline void set_ptr(uchar *length, uchar *data) - { - memcpy(ptr,length,packlength); - memcpy_fixed(ptr+packlength,&data,sizeof(char*)); - } + { + memcpy(ptr,length,packlength); + memcpy_fixed(ptr+packlength,&data,sizeof(char*)); + } void set_ptr_offset(my_ptrdiff_t ptr_diff, uint32 length, uchar *data) - { - uchar *ptr_ofs= ADD_TO_PTR(ptr,ptr_diff,uchar*); - store_length(ptr_ofs, packlength, length); - memcpy_fixed(ptr_ofs+packlength,&data,sizeof(char*)); - } + { + uchar *ptr_ofs= ADD_TO_PTR(ptr,ptr_diff,uchar*); + store_length(ptr_ofs, packlength, length); + memcpy_fixed(ptr_ofs+packlength,&data,sizeof(char*)); + } inline void set_ptr(uint32 length, uchar *data) - { - set_ptr_offset(0, length, data); - } + { + set_ptr_offset(0, length, data); + } uint get_key_image(uchar *buff,uint length, imagetype type); void set_key_image(const uchar *buff,uint length); void sql_type(String &str) const; @@ -1864,21 +1891,17 @@ public: memcpy_fixed(ptr+packlength,&tmp,sizeof(char*)); return 0; } - virtual uchar *pack(uchar *to, const uchar *from, - uint max_length, bool low_byte_first); - uchar *pack_key(uchar *to, const uchar *from, - uint max_length, bool low_byte_first); - uchar *pack_key_from_key_image(uchar* to, const uchar *from, - uint max_length, bool low_byte_first); - virtual const uchar *unpack(uchar *to, const uchar *from, - uint param_data, bool low_byte_first); - const uchar *unpack_key(uchar* to, const uchar *from, - uint max_length, bool low_byte_first); + virtual uchar *pack(uchar *to, const uchar *from, uint max_length); + uchar *pack_key(uchar *to, const uchar *from, uint max_length); + uchar *pack_key_from_key_image(uchar* to, const uchar *from, uint max_length); + virtual const uchar *unpack(uchar *to, const uchar *from, uint param_data); + const uchar *unpack_key(uchar* to, const uchar *from, uint max_length); int pack_cmp(const uchar *a, const uchar *b, uint key_length, bool insert_or_update); int pack_cmp(const uchar *b, uint key_length,bool insert_or_update); uint packed_col_length(const uchar *col_ptr, uint length); uint max_packed_col_length(uint max_length); + /* Set if we should convert constant item's with convert_const_item */ void free() { value.free(); } inline void clear_temporary() { bzero((uchar*) &value,sizeof(value)); } friend int field_conv(Field *to,Field *from); @@ -1945,7 +1968,6 @@ public: Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type); enum_field_types type() const { return MYSQL_TYPE_STRING; } enum Item_result cmp_type () const { return INT_RESULT; } - enum Item_result cast_to_int_type () const { return INT_RESULT; } enum ha_base_keytype key_type() const; int store(const char *to,uint length,CHARSET_INFO *charset); int store(double nr); @@ -2075,10 +2097,8 @@ public: int compatible_field_size(uint field_metadata, const Relay_log_info *rli, uint16 mflags); void sql_type(String &str) const; - virtual uchar *pack(uchar *to, const uchar *from, - uint max_length, bool low_byte_first); - virtual const uchar *unpack(uchar *to, const uchar *from, - uint param_data, bool low_byte_first); + virtual uchar *pack(uchar *to, const uchar *from, uint max_length); + virtual const uchar *unpack(uchar *to, const uchar *from, uint param_data); virtual void set_default(); Field *new_key_field(MEM_ROOT *root, struct st_table *new_table, diff --git a/sql/field_conv.cc b/sql/field_conv.cc index 3e52e67f2d9..2a597194a5c 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -365,6 +365,14 @@ static void do_field_decimal(Copy_field *copy) } +static void do_field_temporal(Copy_field *copy) +{ + MYSQL_TIME ltime; + copy->from_field->get_date(<ime, TIME_FUZZY_DATE); + copy->to_field->store_time(<ime, ltime.time_type); +} + + /** string copy for single byte characters set when to string is shorter than from string. @@ -561,7 +569,7 @@ void Copy_field::set(uchar *to,Field *from) /* To do: - If 'save\ is set to true and the 'from' is a blob field, do_copy is set to + If 'save' is set to true and the 'from' is a blob field, do_copy is set to do_save_blob rather than do_conv_blob. The only differences between them appears to be: @@ -638,13 +646,11 @@ void Copy_field::set(Field *to,Field *from,bool save) Copy_field::Copy_func * Copy_field::get_copy_func(Field *to,Field *from) { - bool compatible_db_low_byte_first= (to->table->s->db_low_byte_first == - from->table->s->db_low_byte_first); if (to->flags & BLOB_FLAG) { if (!(from->flags & BLOB_FLAG) || from->charset() != to->charset()) return do_conv_blob; - if (from_length != to_length || !compatible_db_low_byte_first) + if (from_length != to_length) { // Correct pointer to point at char pointer to_ptr+= to_length - to->table->s->blob_ptr_size; @@ -659,6 +665,19 @@ Copy_field::get_copy_func(Field *to,Field *from) return do_field_int; if (to->result_type() == DECIMAL_RESULT) return do_field_decimal; + if (to->cmp_type() == TIME_RESULT) // TODO; Optimize this + { + /* If types are not 100 % identical then convert trough get_date() */ + if (!to->eq_def(from) || + (((to->table->in_use->variables.sql_mode & + (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | + MODE_INVALID_DATES))) && + (to->type() == MYSQL_TYPE_DATE || + to->type() == MYSQL_TYPE_DATETIME))) + return do_field_temporal; + /* Do binary copy */ + } + // Check if identical fields if (from->result_type() == STRING_RESULT) { @@ -675,12 +694,7 @@ Copy_field::get_copy_func(Field *to,Field *from) If we are copying date or datetime's we have to check the dates if we don't allow 'all' dates. */ - if (to->real_type() != from->real_type() || - !compatible_db_low_byte_first || - ((to->table->in_use->variables.sql_mode & - (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES)) && - (to->type() == MYSQL_TYPE_DATE || - to->type() == MYSQL_TYPE_DATETIME))) + if (to->real_type() != from->real_type()) { if (from->real_type() == MYSQL_TYPE_ENUM || from->real_type() == MYSQL_TYPE_SET) @@ -727,8 +741,7 @@ Copy_field::get_copy_func(Field *to,Field *from) } } else if (to->real_type() != from->real_type() || - to_length != from_length || - !compatible_db_low_byte_first) + to_length != from_length) { if (to->real_type() == MYSQL_TYPE_DECIMAL || to->result_type() == STRING_RESULT) @@ -739,7 +752,7 @@ Copy_field::get_copy_func(Field *to,Field *from) } else { - if (!to->eq_def(from) || !compatible_db_low_byte_first) + if (!to->eq_def(from)) { if (to->real_type() == MYSQL_TYPE_DECIMAL) return do_field_string; @@ -772,14 +785,13 @@ int field_conv(Field *to,Field *from) { if (to->pack_length() == from->pack_length() && !(to->flags & UNSIGNED_FLAG && !(from->flags & UNSIGNED_FLAG)) && + to->decimals() == from->decimals() && to->real_type() != MYSQL_TYPE_ENUM && to->real_type() != MYSQL_TYPE_SET && to->real_type() != MYSQL_TYPE_BIT && (to->real_type() != MYSQL_TYPE_NEWDECIMAL || - ((to->field_length == from->field_length && - (((Field_num*)to)->dec == ((Field_num*)from)->dec)))) && + to->field_length == from->field_length) && from->charset() == to->charset() && - to->table->s->db_low_byte_first == from->table->s->db_low_byte_first && (!(to->table->in_use->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES)) || (to->type() != MYSQL_TYPE_DATE && diff --git a/sql/filesort.cc b/sql/filesort.cc index 3f69083fc8d..10991d1dcfb 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -873,25 +873,28 @@ static void make_sortkey(register SORTPARAM *param, break; } case INT_RESULT: + case TIME_RESULT: { - longlong value= item->val_int_result(); + longlong value; + if (sort_field->result_type == INT_RESULT) + value= item->val_int_result(); + else + { + MYSQL_TIME buf; + if (item->get_date_result(&buf, TIME_FUZZY_DATE | TIME_INVALID_DATES)) + DBUG_ASSERT(maybe_null && item->null_value); + else + value= pack_time(&buf); + } if (maybe_null) { - *to++=1; /* purecov: inspected */ if (item->null_value) { - if (maybe_null) - bzero((char*) to-1,sort_field->length+1); - else - { - DBUG_PRINT("warning", - ("Got null on something that shouldn't be null")); - bzero((char*) to,sort_field->length); - } + bzero((char*) to++, sort_field->length+1); break; } + *to++=1; /* purecov: inspected */ } -#if SIZEOF_LONG_LONG > 4 to[7]= (uchar) value; to[6]= (uchar) (value >> 8); to[5]= (uchar) (value >> 16); @@ -903,15 +906,6 @@ static void make_sortkey(register SORTPARAM *param, to[0]= (uchar) (value >> 56); else to[0]= (uchar) (value >> 56) ^ 128; /* Reverse signbit */ -#else - to[3]= (uchar) value; - to[2]= (uchar) (value >> 8); - to[1]= (uchar) (value >> 16); - if (item->unsigned_flag) /* Fix sign */ - to[0]= (uchar) (value >> 24); - else - to[0]= (uchar) (value >> 24) ^ 128; /* Reverse signbit */ -#endif break; } case DECIMAL_RESULT: @@ -921,8 +915,7 @@ static void make_sortkey(register SORTPARAM *param, { if (item->null_value) { - bzero((char*)to, sort_field->length+1); - to++; + bzero((char*) to++, sort_field->length+1); break; } *to++=1; @@ -1566,9 +1559,7 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length, } else { - sortorder->result_type= sortorder->item->result_type(); - if (sortorder->item->result_as_longlong()) - sortorder->result_type= INT_RESULT; + sortorder->result_type= sortorder->item->cmp_type(); switch (sortorder->result_type) { case STRING_RESULT: sortorder->length=sortorder->item->max_length; @@ -1586,12 +1577,9 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length, sortorder->length+= sortorder->suffix_length; } break; + case TIME_RESULT: case INT_RESULT: -#if SIZEOF_LONG_LONG > 4 sortorder->length=8; // Size of intern longlong -#else - sortorder->length=4; -#endif break; case DECIMAL_RESULT: sortorder->length= @@ -1666,6 +1654,7 @@ get_addon_fields(THD *thd, Field **ptabfield, uint sortlength, uint *plength) Actually we need only the fields referred in the result set. And for some of them it makes sense to use the values directly from sorted fields. + But beware the case when item->cmp_type() != item->result_type() */ *plength= 0; diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 6cd735ddd4b..6a72623ce5d 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -268,7 +268,6 @@ void ha_partition::init_handler_variables() m_extra_prepare_for_update= FALSE; m_extra_cache_part_id= NO_CURRENT_PART_ID; m_handler_status= handler_not_initialized; - m_low_byte_first= 1; m_part_field_array= NULL; m_ordered_rec_buffer= NULL; m_top_entry= NO_CURRENT_PART_ID; @@ -417,18 +416,11 @@ bool ha_partition::initialize_partition(MEM_ROOT *mem_root) Verify that all partitions have the same table_flags. */ check_table_flags= m_file[0]->ha_table_flags(); - m_low_byte_first= m_file[0]->low_byte_first(); m_pkey_is_clustered= TRUE; file_array= m_file; do { file= *file_array; - if (m_low_byte_first != file->low_byte_first()) - { - // Cannot have handlers with different endian - my_error(ER_MIX_HANDLER_ERROR, MYF(0)); - DBUG_RETURN(1); - } if (!file->primary_key_is_clustered()) m_pkey_is_clustered= FALSE; if (check_table_flags != file->ha_table_flags()) diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 49aa7103e1c..2d82ec721b5 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -110,7 +110,6 @@ private: for this since the MySQL Server sometimes allocating the handler object without freeing them. */ - ulong m_low_byte_first; enum enum_handler_status { handler_not_initialized= 0, @@ -907,12 +906,6 @@ public: virtual uint max_supported_key_part_length() const; /* - All handlers in a partitioned table must have the same low_byte_first - */ - virtual bool low_byte_first() const - { return m_low_byte_first; } - - /* The extra record buffer length is the maximum needed by all handlers. The minimum record length is the maximum of all involved handlers. */ diff --git a/sql/handler.h b/sql/handler.h index 919e063e1cf..4707aabbd52 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -2234,7 +2234,6 @@ public: virtual uint max_supported_key_part_length() const { return 255; } virtual uint min_record_length(uint options) const { return 1; } - virtual bool low_byte_first() const { return 1; } virtual uint checksum() const { return 0; } virtual bool is_crashed() const { return 0; } virtual bool auto_repair() const { return 0; } diff --git a/sql/item.cc b/sql/item.cc index 6ee656b9147..b73008fad9f 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -197,10 +197,12 @@ bool Item::val_bool() case STRING_RESULT: return val_real() != 0.0; case ROW_RESULT: - default: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); return 0; // Wrong (but safe) } + return 0; // Wrong (but safe) } @@ -471,6 +473,33 @@ void Item::print_item_w_name(String *str, enum_query_type query_type) } +void Item::print_value(String *str) +{ + char buff[MAX_FIELD_WIDTH]; + String *ptr, tmp(buff,sizeof(buff),str->charset()); + ptr= val_str(&tmp); + if (!ptr) + str->append("NULL"); + else + { + switch (result_type()) { + case STRING_RESULT: + append_unescaped(str, ptr->ptr(), ptr->length()); + break; + case DECIMAL_RESULT: + case REAL_RESULT: + case INT_RESULT: + str->append(*ptr); + break; + case ROW_RESULT: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: + DBUG_ASSERT(0); + } + } +} + + void Item::cleanup() { DBUG_ENTER("Item::cleanup"); @@ -515,6 +544,45 @@ void Item::rename(char *new_name) name= new_name; } +Item_result Item::cmp_type() const +{ + switch (field_type()) { + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + return DECIMAL_RESULT; + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_BIT: + return INT_RESULT; + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + return REAL_RESULT; + case MYSQL_TYPE_NULL: + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_GEOMETRY: + return STRING_RESULT; + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_NEWDATE: + return TIME_RESULT; + }; + DBUG_ASSERT(0); + return (Item_result)-1; +} /** Traverse item tree possibly transforming it (replacing items). @@ -989,7 +1057,17 @@ bool Item_string::eq(const Item *item, bool binary_cmp) const bool Item::get_date(MYSQL_TIME *ltime,uint fuzzydate) { - switch (result_type()) { + Item_result res_type; + + if (field_type() == MYSQL_TYPE_TIME) + fuzzydate|= TIME_TIME_ONLY; + + /* This function only supports time with strings */ + res_type= result_type(); + if (fuzzydate & TIME_TIME_ONLY) + res_type= STRING_RESULT; + + switch (res_type) { case INT_RESULT: { int was_cut; @@ -998,11 +1076,9 @@ bool Item::get_date(MYSQL_TIME *ltime,uint fuzzydate) goto err; if (number_to_datetime(value, ltime, fuzzydate, &was_cut) == LL(-1)) { - char buff[22], *end; - end= longlong10_to_str(value, buff, -10); + Lazy_string_num str(value); make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, - buff, (int) (end-buff), MYSQL_TIMESTAMP_NONE, - NullS); + &str, MYSQL_TIMESTAMP_NONE, NullS); null_value= 1; goto err; } @@ -1058,28 +1134,36 @@ err: } /** - Get time of first argument.\ - - As a extra convenience the time structure is reset on error! + Get time of first argument. */ bool Item::get_time(MYSQL_TIME *ltime) { char buff[40]; String tmp(buff,sizeof(buff),&my_charset_bin),*res; + + if (cmp_type() == TIME_RESULT) + { + /* + This is true for functions like Item_date_typecast() which + doesn't have a explicit get_time() function. + */ + return (get_date(ltime, TIME_TIME_ONLY | TIME_FUZZY_DATE | + sql_mode_for_dates())); + } + if (!(res=val_str(&tmp)) || str_to_time_with_warn(res->ptr(), res->length(), ltime, - TIME_FUZZY_DATE | - (current_thd->variables.sql_mode & - (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | - MODE_INVALID_DATES)))) + TIME_FUZZY_DATE | sql_mode_for_dates())) { - bzero((char*) ltime,sizeof(*ltime)); + bzero((char*) ltime,sizeof(*ltime)); // Safety + null_value= 1; return 1; } return 0; } + CHARSET_INFO *Item::default_charset() { return current_thd->variables.collation_connection; @@ -1103,6 +1187,7 @@ int Item::save_in_field_no_warnings(Field *field, bool no_conversions) my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set); ulong sql_mode= thd->variables.sql_mode; thd->variables.sql_mode&= ~(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE); + thd->variables.sql_mode|= MODE_INVALID_DATES; thd->count_cuted_fields= CHECK_FIELD_IGNORE; res= save_in_field(field, no_conversions); thd->count_cuted_fields= tmp; @@ -2212,16 +2297,6 @@ bool Item_field::get_date_result(MYSQL_TIME *ltime,uint fuzzydate) return 0; } -bool Item_field::get_time(MYSQL_TIME *ltime) -{ - if ((null_value=field->is_null()) || field->get_time(ltime)) - { - bzero((char*) ltime,sizeof(*ltime)); - return 1; - } - return 0; -} - void Item_field::save_result(Field *to) { save_field_in_field(result_field, &null_value, to, TRUE); @@ -2270,10 +2345,12 @@ bool Item_field::val_bool_result() case STRING_RESULT: return result_field->val_real() != 0.0; case ROW_RESULT: - default: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); return 0; // Shut up compiler } + return 0; } @@ -2854,19 +2931,19 @@ void Item_param::set_time(MYSQL_TIME *tm, timestamp_type time_type, if (value.time.year > 9999 || value.time.month > 12 || value.time.day > 31 || (time_type != MYSQL_TIMESTAMP_TIME && value.time.hour > 23) || - value.time.minute > 59 || value.time.second > 59) + value.time.minute > 59 || value.time.second > 59 || + value.time.second_part > TIME_MAX_SECOND_PART) { - char buff[MAX_DATE_STRING_REP_LENGTH]; - uint length= my_TIME_to_str(&value.time, buff); + Lazy_string_time str(&value.time); make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, - buff, length, time_type, 0); + &str, time_type, 0); set_zero_time(&value.time, MYSQL_TIMESTAMP_ERROR); } state= TIME_VALUE; maybe_null= 0; max_length= max_length_arg; - decimals= 0; + decimals= tm->second_part > 0 ? TIME_SECOND_PART_DIGITS : 0; DBUG_VOID_RETURN; } @@ -2953,10 +3030,12 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry) case REAL_RESULT: set_double(*(double*)entry->value); item_type= Item::REAL_ITEM; + param_type= MYSQL_TYPE_DOUBLE; break; case INT_RESULT: set_int(*(longlong*)entry->value, MY_INT64_NUM_DECIMAL_DIGITS); item_type= Item::INT_ITEM; + param_type= MYSQL_TYPE_LONGLONG; break; case STRING_RESULT: { @@ -2979,6 +3058,7 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry) charset of connection, so we have to set it later. */ item_type= Item::STRING_ITEM; + param_type= MYSQL_TYPE_VARCHAR; if (set_str((const char *)entry->value, entry->length)) DBUG_RETURN(1); @@ -2994,9 +3074,12 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry) my_decimal_precision_to_length_no_truncation(ent_value->precision(), decimals, unsigned_flag); item_type= Item::DECIMAL_ITEM; + param_type= MYSQL_TYPE_NEWDECIMAL; break; } - default: + case ROW_RESULT: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); set_null(); } @@ -3074,21 +3157,6 @@ int Item_param::save_in_field(Field *field, bool no_conversions) } -bool Item_param::get_time(MYSQL_TIME *res) -{ - if (state == TIME_VALUE) - { - *res= value.time; - return 0; - } - /* - If parameter value isn't supplied assertion will fire in val_str() - which is called from Item::get_time(). - */ - return Item::get_time(res); -} - - bool Item_param::get_date(MYSQL_TIME *res, uint fuzzydate) { if (state == TIME_VALUE) @@ -3218,7 +3286,8 @@ String *Item_param::val_str(String* str) { if (str->reserve(MAX_DATE_STRING_REP_LENGTH)) break; - str->length((uint) my_TIME_to_str(&value.time, (char*) str->ptr())); + str->length((uint) my_TIME_to_str(&value.time, (char*) str->ptr(), + decimals)); str->set_charset(&my_charset_bin); return str; } @@ -3270,7 +3339,7 @@ const String *Item_param::query_val_str(String* str) const buf= str->c_ptr_quick(); ptr= buf; *ptr++= '\''; - ptr+= (uint) my_TIME_to_str(&value.time, ptr); + ptr+= (uint) my_TIME_to_str(&value.time, ptr, decimals); *ptr++= '\''; str->length((uint32) (ptr - buf)); break; @@ -3464,6 +3533,7 @@ Item_param::set_param_type_and_swap_value(Item_param *src) /**************************************************************************** Item_copy ****************************************************************************/ + Item_copy *Item_copy::create (Item *item) { switch (item->result_type()) @@ -3477,7 +3547,9 @@ Item_copy *Item_copy::create (Item *item) new Item_copy_uint (item) : new Item_copy_int (item); case DECIMAL_RESULT: return new Item_copy_decimal (item); - default: + case TIME_RESULT: + case ROW_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT (0); } /* should not happen */ @@ -3550,8 +3622,7 @@ void Item_copy_int::copy() null_value=item->null_value; } -static int save_int_value_in_field (Field *field, longlong nr, - bool null_value, bool unsigned_flag); +static int save_int_value_in_field (Field *, longlong, bool, bool); int Item_copy_int::save_in_field(Field *field, bool no_conversions) { @@ -4844,12 +4915,8 @@ Item *Item_field::equal_fields_propagator(uchar *arg) item= this; else if (field && (field->flags & ZEROFILL_FLAG) && IS_NUM(field->type())) { - /* - We don't need to zero-fill timestamp columns here because they will be - first converted to a string (in date/time format) and compared as such if - compared with another string. - */ - if (item && field->type() != FIELD_TYPE_TIMESTAMP && cmp_context != INT_RESULT) + if (item && field->type() != FIELD_TYPE_TIMESTAMP && + cmp_context != INT_RESULT) convert_zerofill_number_to_string(&item, (Field_num *)field); else item= this; @@ -4972,25 +5039,12 @@ enum_field_types Item::field_type() const case DECIMAL_RESULT: return MYSQL_TYPE_NEWDECIMAL; case REAL_RESULT: return MYSQL_TYPE_DOUBLE; case ROW_RESULT: - default: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); return MYSQL_TYPE_VARCHAR; } -} - - -bool Item::is_datetime() -{ - switch (field_type()) - { - case MYSQL_TYPE_DATE: - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIMESTAMP: - return TRUE; - default: - break; - } - return FALSE; + return MYSQL_TYPE_VARCHAR; } @@ -5170,16 +5224,19 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table, bool fixed_length) break; case MYSQL_TYPE_NEWDATE: case MYSQL_TYPE_DATE: - field= new Field_newdate(maybe_null, name, &my_charset_bin); + field= new Field_newdate(0, null_ptr, 0, Field::NONE, name, &my_charset_bin); break; case MYSQL_TYPE_TIME: - field= new Field_time(maybe_null, name, &my_charset_bin); + field= new_Field_time(0, null_ptr, 0, Field::NONE, name, + decimals, &my_charset_bin); break; case MYSQL_TYPE_TIMESTAMP: - field= new Field_timestamp(maybe_null, name, &my_charset_bin); + field= new_Field_timestamp(0, null_ptr, 0, + Field::NONE, name, 0, decimals, &my_charset_bin); break; case MYSQL_TYPE_DATETIME: - field= new Field_datetime(maybe_null, name, &my_charset_bin); + field= new_Field_datetime(0, null_ptr, 0, Field::NONE, name, + decimals, &my_charset_bin); break; case MYSQL_TYPE_YEAR: field= new Field_year((uchar*) 0, max_length, null_ptr, 0, Field::NONE, @@ -5416,12 +5473,6 @@ int Item_string::save_in_field(Field *field, bool no_conversions) } -int Item_uint::save_in_field(Field *field, bool no_conversions) -{ - /* Item_int::save_in_field handles both signed and unsigned. */ - return Item_int::save_in_field(field, no_conversions); -} - static int save_int_value_in_field (Field *field, longlong nr, bool null_value, bool unsigned_flag) { @@ -5438,6 +5489,22 @@ int Item_int::save_in_field(Field *field, bool no_conversions) } +void Item_datetime::set(longlong packed) +{ + unpack_time(packed, <ime); +} + +int Item_datetime::save_in_field(Field *field, bool no_conversions) +{ + field->set_notnull(); + return field->store_time(<ime, ltime.time_type); +} + +longlong Item_datetime::val_int() +{ + return TIME_to_ulonglong(<ime); +} + int Item_decimal::save_in_field(Field *field, bool no_conversions) { field->set_notnull(); @@ -5455,7 +5522,9 @@ bool Item_int::eq(const Item *arg, bool binary_cmp) const a basic constant. */ Item *item= (Item*) arg; - return item->val_int() == value && item->unsigned_flag == unsigned_flag; + return (item->val_int() == value && + ((longlong) value >= 0 || + (item->unsigned_flag == unsigned_flag))); } return FALSE; } @@ -5869,7 +5938,7 @@ bool Item::send(Protocol *protocol, String *buffer) if (f_type == MYSQL_TYPE_DATE) return protocol->store_date(&tm); else - result= protocol->store(&tm); + result= protocol->store(&tm, decimals); } break; } @@ -5878,7 +5947,7 @@ bool Item::send(Protocol *protocol, String *buffer) MYSQL_TIME tm; get_time(&tm); if (!null_value) - result= protocol->store_time(&tm); + result= protocol->store_time(&tm, decimals); break; } } @@ -5959,17 +6028,7 @@ void Item_field::print(String *str, enum_query_type query_type) { if (field && field->table->const_table) { - char buff[MAX_FIELD_WIDTH]; - String tmp(buff,sizeof(buff),str->charset()); - field->val_str(&tmp); - if (field->is_null()) - str->append("NULL"); - else - { - str->append('\''); - str->append(tmp); - str->append('\''); - } + print_value(str); return; } Item_ident::print(str, query_type); @@ -6583,7 +6642,8 @@ bool Item_ref::val_bool_result() case STRING_RESULT: return result_field->val_real() != 0.0; case ROW_RESULT: - default: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); } } @@ -7841,6 +7901,8 @@ Item_result item_cmp_type(Item_result a,Item_result b) return INT_RESULT; else if (a == ROW_RESULT || b == ROW_RESULT) return ROW_RESULT; + else if (a == TIME_RESULT || b == TIME_RESULT) + return TIME_RESULT; if ((a == INT_RESULT || a == DECIMAL_RESULT) && (b == INT_RESULT || b == DECIMAL_RESULT)) return DECIMAL_RESULT; @@ -7854,11 +7916,20 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) Item *new_item= NULL; if (item->basic_const_item()) return; // Can't be better - Item_result res_type=item_cmp_type(comp_item->result_type(), - item->result_type()); + Item_result res_type=item_cmp_type(comp_item->cmp_type(), item->cmp_type()); char *name=item->name; // Alloced by sql_alloc switch (res_type) { + case TIME_RESULT: + { + bool is_null; + Item **ref_copy= ref; + /* the following call creates a constant and puts it in new_item */ + get_datetime_value(thd, &ref_copy, &new_item, comp_item, &is_null); + if (is_null) + new_item= new Item_null(name); + break; + } case STRING_RESULT: { char buff[MAX_FIELD_WIDTH]; @@ -7933,8 +8004,9 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) (Item*) new Item_decimal(name, result, length, decimals)); break; } - default: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); + break; } if (new_item) thd->change_item_tree(ref, new_item); @@ -7954,6 +8026,9 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) @note We only use this on the range optimizer/partition pruning, because in some cases we can't store the value in the field without some precision/character loss. + + @todo rewrite it to use Arg_comparator (currently it's a simplified and + incomplete version of it) */ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) @@ -8009,6 +8084,25 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) field_val= field->val_decimal(&field_buf); return my_decimal_cmp(item_val, field_val); } + /* + We have to check field->cmp_type() instead of res_type, + as result_type() - and thus res_type - can never be TIME_RESULT (yet). + */ + if (field->cmp_type() == TIME_RESULT) + { + MYSQL_TIME field_time, item_time; + if (field->type() == MYSQL_TYPE_TIME) + { + field->get_time(&field_time); + item->get_time(&item_time); + } + else + { + field->get_date(&field_time, TIME_FUZZY_DATE | TIME_INVALID_DATES); + item->get_date(&item_time, TIME_FUZZY_DATE | TIME_INVALID_DATES); + } + return my_time_compare(&field_time, &item_time); + } double result= item->val_real(); if (item->null_value) return 0; @@ -8048,11 +8142,14 @@ Item_cache* Item_cache::get_cache(const Item *item, const Item_result type) return new Item_cache_str(item); case ROW_RESULT: return new Item_cache_row(); - default: - // should never be in real life + case TIME_RESULT: + /* this item will store a packed datetime value as an integer */ + return new Item_cache_int(MYSQL_TYPE_DATETIME); + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); - return 0; + break; } + return 0; // Impossible } void Item_cache::store(Item *item) @@ -8065,6 +8162,11 @@ void Item_cache::store(Item *item) void Item_cache::print(String *str, enum_query_type query_type) { + if (value_cached) + { + print_value(str); + return; + } str->append(STRING_WITH_LEN("<cache>(")); if (example) example->print(str, query_type); @@ -8142,6 +8244,57 @@ longlong Item_cache_int::val_int() return value; } + +bool Item_cache_int::get_date(MYSQL_TIME *ltime, uint fuzzydate) +{ + if (!value_cached && !cache_value()) + goto err; + + if (cmp_type() == TIME_RESULT) + { + unpack_time(value, ltime); + ltime->time_type= mysql_type_to_time_type(field_type()); + } + else + { + int was_cut; + if (number_to_datetime(value, ltime, fuzzydate, &was_cut) == -1LL) + { + Lazy_string_num str(value); + make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + &str, MYSQL_TIMESTAMP_NONE, NullS); + goto err; + } + } + return 0; + +err: + bzero((char*) ltime,sizeof(*ltime)); + return 1; +} + + +int Item_cache_int::save_in_field(Field *field, bool no_conversions) +{ + int error; + if ((!value_cached && !cache_value()) || null_value) + return set_field_to_null_with_conversions(field, no_conversions); + + field->set_notnull(); + if (cmp_type() == TIME_RESULT) + { + MYSQL_TIME ltime; + unpack_time(value, <ime); + ltime.time_type= mysql_type_to_time_type(field_type()); + error= field->store_time(<ime, ltime.time_type); + } + else + error= field->store(value, unsigned_flag); + + return error ? error : field->table->in_use->is_error() ? 1 : 0; +} + + bool Item_cache_real::cache_value() { if (!example) @@ -8347,11 +8500,8 @@ my_decimal *Item_cache_str::val_decimal(my_decimal *decimal_val) int Item_cache_str::save_in_field(Field *field, bool no_conversions) { if ((!value_cached && !cache_value()) || null_value) - { - field->set_notnull(); - null_value= TRUE; - return 0; - } + return set_field_to_null_with_conversions(field, no_conversions); + int res= Item_cache::save_in_field(field, no_conversions); return (is_varbinary && field->type() == MYSQL_TYPE_STRING && value->length() < field->field_length) ? 1 : res; @@ -8510,7 +8660,7 @@ enum_field_types Item_type_holder::get_real_type(Item *item) case FIELD_ITEM: { /* - Item_fields::field_type ask Field_type() but sometimes field return + Item_field::field_type ask Field_type() but sometimes field return a different type, like for enum/set, so we need to ask real type. */ Field *field= ((Item_field *) item)->field; @@ -8552,7 +8702,8 @@ enum_field_types Item_type_holder::get_real_type(Item *item) case DECIMAL_RESULT: return MYSQL_TYPE_NEWDECIMAL; case ROW_RESULT: - default: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); return MYSQL_TYPE_VAR_STRING; } diff --git a/sql/item.h b/sql/item.h index 9bb00c5db84..2c26f8c4ef8 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1,5 +1,5 @@ /* 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. @@ -642,8 +642,11 @@ public: { return save_in_field(field, 1); } virtual bool send(Protocol *protocol, String *str); virtual bool eq(const Item *, bool binary_cmp) const; + /* result_type() of an item specifies how the value should be returned */ virtual Item_result result_type() const { return REAL_RESULT; } - virtual Item_result cast_to_int_type() const { return result_type(); } + /* ... while cmp_type() specifies how it should be compared */ + virtual Item_result cmp_type() const; + virtual Item_result cast_to_int_type() const { return cmp_type(); } virtual enum_field_types string_field_type() const; virtual enum_field_types field_type() const; virtual enum Type type() const =0; @@ -821,6 +824,8 @@ public: /* This is also used to create fields in CREATE ... SELECT: */ virtual Field *tmp_table_field(TABLE *t_arg) { return 0; } virtual const char *full_name() const { return name ? name : "???"; } + const char *field_name_or_null() + { return real_item()->type() == Item::FIELD_ITEM ? name : NULL; } /* *result* family of methods is analog of *val* family (see above) but @@ -900,6 +905,7 @@ public: } void print_item_w_name(String *, enum_query_type query_type); + void print_value(String *); virtual void update_used_tables() {} virtual void split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields) {} @@ -907,7 +913,7 @@ public: void split_sum_func2(THD *thd, Item **ref_pointer_array, List<Item> &fields, Item **ref, bool skip_registered); virtual bool get_date(MYSQL_TIME *ltime,uint fuzzydate); - virtual bool get_time(MYSQL_TIME *ltime); + bool get_time(MYSQL_TIME *ltime); virtual bool get_date_result(MYSQL_TIME *ltime,uint fuzzydate) { return get_date(ltime,fuzzydate); } /* @@ -1208,15 +1214,6 @@ public: { return 0; } - /* - result_as_longlong() must return TRUE for Items representing DATE/TIME - functions and DATE/TIME table fields. - Those Items have result_type()==STRING_RESULT (and not INT_RESULT), but - their values should be compared as integers (because the integer - representation is more precise than the string one). - */ - virtual bool result_as_longlong() { return FALSE; } - bool is_datetime(); /* Test whether an expression is expensive to compute. Used during @@ -1752,7 +1749,7 @@ public: } Item_result cast_to_int_type() const { - return field->cast_to_int_type(); + return field->cmp_type(); } enum_field_types field_type() const { @@ -1767,7 +1764,6 @@ public: Field *tmp_table_field(TABLE *t_arg) { return result_field; } bool get_date(MYSQL_TIME *ltime,uint fuzzydate); bool get_date_result(MYSQL_TIME *ltime,uint fuzzydate); - bool get_time(MYSQL_TIME *ltime); bool is_null() { return field->is_null(); } void update_null_value(); Item *get_tmp_table_item(THD *thd); @@ -1781,10 +1777,6 @@ public: bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool enumerate_field_refs_processor(uchar *arg); void cleanup(); - bool result_as_longlong() - { - return field->can_be_compared_as_longlong(); - } Item_equal *get_item_equal() { return item_equal; } void set_item_equal(Item_equal *item_eq) { item_equal= item_eq; } Item_equal *find_item_equal(COND_EQUAL *cond_equal); @@ -1936,6 +1928,7 @@ public: Item_param(uint pos_in_query_arg); enum Item_result result_type () const { return item_result_type; } + enum Item_result cast_to_int_type() const { return item_result_type; } enum Type type() const { return item_type; } enum_field_types field_type() const { return param_type; } @@ -1943,7 +1936,6 @@ public: longlong val_int(); my_decimal *val_decimal(my_decimal*); String *val_str(String*); - bool get_time(MYSQL_TIME *tm); bool get_date(MYSQL_TIME *tm, uint fuzzydate); int save_in_field(Field *field, bool no_conversions); @@ -2044,19 +2036,31 @@ class Item_uint :public Item_int { public: Item_uint(const char *str_arg, uint length); - Item_uint(ulonglong i) :Item_int((ulonglong) i, 10) {} + Item_uint(ulonglong i) :Item_int(i, 10) {} Item_uint(const char *str_arg, longlong i, uint length); double val_real() { DBUG_ASSERT(fixed == 1); return ulonglong2double((ulonglong)value); } String *val_str(String*); Item *clone_item() { return new Item_uint(name, value, max_length); } - int save_in_field(Field *field, bool no_conversions); virtual void print(String *str, enum_query_type query_type); Item_num *neg (); uint decimal_precision() const { return max_length; } }; +class Item_datetime :public Item_int +{ +protected: + MYSQL_TIME ltime; +public: + Item_datetime() :Item_int(0) { unsigned_flag=0; } + int save_in_field(Field *field, bool no_conversions); + longlong val_int(); + double val_real() { return (double)val_int(); } + void set(longlong packed); +}; + + /* decimal (fixed point) constant */ class Item_decimal :public Item_num { @@ -2349,10 +2353,11 @@ class Item_return_date_time :public Item_partition_func_safe_string { enum_field_types date_time_field_type; public: - Item_return_date_time(const char *name_arg, enum_field_types field_type_arg) - :Item_partition_func_safe_string(name_arg, 0, &my_charset_bin), + Item_return_date_time(const char *name_arg, uint length_arg, + enum_field_types field_type_arg) + :Item_partition_func_safe_string(name_arg, length_arg, &my_charset_bin), date_time_field_type(field_type_arg) - { } + { decimals= 0; } enum_field_types field_type() const { return date_time_field_type; } }; @@ -2592,10 +2597,6 @@ public: (*ref)->restore_to_before_no_rows_in_result(); } virtual void print(String *str, enum_query_type query_type); - bool result_as_longlong() - { - return (*ref)->result_as_longlong(); - } void cleanup(); Item_field *filed_for_view_update() { return (*ref)->filed_for_view_update(); } @@ -2632,11 +2633,6 @@ public: { return trace_unsupported_by_check_vcol_func_processor("ref"); } - bool get_time(MYSQL_TIME *ltime) - { - DBUG_ASSERT(fixed); - return (*ref)->get_time(ltime); - } }; @@ -2805,7 +2801,6 @@ public: } bool enumerate_field_refs_processor(uchar *arg) { return orig_item->enumerate_field_refs_processor(arg); } - bool result_as_longlong() { return orig_item->result_as_longlong(); } Item_field *filed_for_view_update() { return orig_item->filed_for_view_update(); } @@ -3562,7 +3557,7 @@ class Item_cache_int: public Item_cache protected: longlong value; public: - Item_cache_int(): Item_cache(), + Item_cache_int(): Item_cache(MYSQL_TYPE_LONGLONG), value(0) {} Item_cache_int(enum_field_types field_type_arg): Item_cache(field_type_arg), value(0) {} @@ -3574,8 +3569,20 @@ public: String* val_str(String *str); my_decimal *val_decimal(my_decimal *); enum Item_result result_type() const { return INT_RESULT; } - bool result_as_longlong() { return TRUE; } bool cache_value(); + bool get_date(MYSQL_TIME *ltime, uint fuzzydate); + int save_in_field(Field *field, bool no_conversions); + /* + Having a clone_item method tells optimizer that this object + is a constant and need not be optimized further. + Important when storing packed datetime values. + */ + Item *clone_item() + { + Item_cache_int *item= new Item_cache_int(cached_field_type); + item->store_longlong(this, value); + return item; + } }; @@ -3583,7 +3590,7 @@ class Item_cache_real: public Item_cache { double value; public: - Item_cache_real(): Item_cache(), + Item_cache_real(): Item_cache(MYSQL_TYPE_DOUBLE), value(0) {} double val_real(); @@ -3600,7 +3607,7 @@ class Item_cache_decimal: public Item_cache protected: my_decimal decimal_value; public: - Item_cache_decimal(): Item_cache() {} + Item_cache_decimal(): Item_cache(MYSQL_TYPE_NEWDECIMAL) {} double val_real(); longlong val_int(); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 9b12e5c844c..9b1d473bb4d 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -30,9 +30,6 @@ #include "sql_select.h" static bool convert_constant_item(THD *, Item_field *, Item **); -static longlong -get_year_value(THD *thd, Item ***item_arg, Item **cache_arg, - Item *warn_item, bool *is_null); static Item_result item_store_type(Item_result a, Item *item, my_bool unsigned_flag) @@ -138,10 +135,10 @@ static int cmp_row_type(Item* item1, Item* item2) static int agg_cmp_type(Item_result *type, Item **items, uint nitems) { uint i; - type[0]= items[0]->result_type(); + type[0]= items[0]->cmp_type(); for (i= 1 ; i < nitems ; i++) { - type[0]= item_cmp_type(type[0], items[i]->result_type()); + type[0]= item_cmp_type(type[0], items[i]->cmp_type()); /* When aggregating types of two row expressions we have to check that they have the same cardinality and that each component @@ -412,7 +409,8 @@ static bool convert_constant_item(THD *thd, Item_field *field_item, LINT_INIT(old_maps[0]); LINT_INIT(old_maps[1]); - if (table) + /* table->read_set may not be set if we come here from a CREATE TABLE */ + if (table && table->read_set) dbug_tmp_use_all_columns(table, old_maps, table->read_set, table->write_set); /* For comparison purposes allow invalid dates like 2000-01-32 */ @@ -448,7 +446,7 @@ static bool convert_constant_item(THD *thd, Item_field *field_item, } thd->variables.sql_mode= orig_sql_mode; thd->count_cuted_fields= orig_count_cuted_fields; - if (table) + if (table && table->read_set) dbug_tmp_restore_column_maps(table->read_set, table->write_set, old_maps); } return result; @@ -479,7 +477,6 @@ void Item_bool_func2::fix_length_and_dec() to the collation of A. */ - DTCollation coll; if (args[0]->result_type() == STRING_RESULT && args[1]->result_type() == STRING_RESULT && @@ -488,48 +485,29 @@ void Item_bool_func2::fix_length_and_dec() args[0]->cmp_context= args[1]->cmp_context= item_cmp_type(args[0]->result_type(), args[1]->result_type()); - // Make a special case of compare with fields to get nicer DATE comparisons - if (functype() == LIKE_FUNC) // Disable conversion in case of LIKE function. - { - set_cmp_func(); - return; - } + /* + Make a special case of compare with fields to get nicer comparisons + of numbers with constant string. + This directly contradicts the manual (number and a string should + be compared as doubles), but seems to provide more + "intuitive" behavior in some cases (but less intuitive in others). + But disable conversion in case of LIKE function. + */ thd= current_thd; - if (!thd->lex->is_ps_or_view_context_analysis()) + if (functype() != LIKE_FUNC && !thd->lex->is_ps_or_view_context_analysis()) { - if (args[0]->real_item()->type() == FIELD_ITEM) - { - Item_field *field_item= (Item_field*) (args[0]->real_item()); - if (field_item->field->can_be_compared_as_longlong() && - !(field_item->is_datetime() && - args[1]->result_type() == STRING_RESULT)) - { - if (convert_constant_item(thd, field_item, &args[1])) - { - cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, - INT_RESULT); // Works for all types. - args[0]->cmp_context= args[1]->cmp_context= INT_RESULT; - return; - } - } - } - if (args[1]->real_item()->type() == FIELD_ITEM) + int field; + if (args[field= 0]->real_item()->type() == FIELD_ITEM || + args[field= 1]->real_item()->type() == FIELD_ITEM) { - Item_field *field_item= (Item_field*) (args[1]->real_item()); - if (field_item->field->can_be_compared_as_longlong() && - !(field_item->is_datetime() && - args[0]->result_type() == STRING_RESULT)) - { - if (convert_constant_item(thd, field_item, &args[0])) - { - cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, - INT_RESULT); // Works for all types. - args[0]->cmp_context= args[1]->cmp_context= INT_RESULT; - return; - } - } + Item_field *field_item= (Item_field*) (args[field]->real_item()); + if (field_item->cmp_type() == INT_RESULT && + (field_item->field->special_const_compare() || + args[!field]->result_type() == STRING_RESULT) && + convert_constant_item(thd, field_item, &args[!field])) + args[0]->cmp_context= args[1]->cmp_context= INT_RESULT; } } set_cmp_func(); @@ -543,6 +521,8 @@ int Arg_comparator::set_compare_func(Item_result_field *item, Item_result type) [is_owner_equal_func()]; switch (type) { + case TIME_RESULT: + break; case ROW_RESULT: { uint n= (*a)->cols(); @@ -636,8 +616,9 @@ int Arg_comparator::set_compare_func(Item_result_field *item, Item_result type) } break; } - default: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); + break; } return 0; } @@ -651,12 +632,14 @@ int Arg_comparator::set_compare_func(Item_result_field *item, Item_result type) @param[in] warn_name Field name for issuing the warning @param[out] l_time The MYSQL_TIME objects is initialized. - Parses a date provided in the string str into a MYSQL_TIME object. If the - string contains an incorrect date or doesn't correspond to a date at all - then a warning is issued. The warn_type and the warn_name arguments are used - as the name and the type of the field when issuing the warning. If any input - was discarded (trailing or non-timestamp-y characters), return value will be - TRUE. + Parses a date provided in the string str into a MYSQL_TIME object. + The date is used for comparison, that is fuzzy dates are allowed + independently of sql_mode. + If the string contains an incorrect date or doesn't correspond to a date at + all then a warning is issued. The warn_type and the warn_name arguments are + used as the name and the type of the field when issuing the warning. If any + input was discarded (trailing or non-timestamp-y characters), return value + will be TRUE. @return Status flag @retval FALSE Success. @@ -669,16 +652,15 @@ bool get_mysql_time_from_str(THD *thd, String *str, timestamp_type warn_type, bool value; int error; enum_mysql_timestamp_type timestamp_type; + int flags= TIME_FUZZY_DATE | MODE_INVALID_DATES; + + if (warn_type == MYSQL_TIMESTAMP_TIME) + flags|= TIME_TIME_ONLY; timestamp_type= - str_to_datetime(str->ptr(), str->length(), l_time, - (TIME_FUZZY_DATE | MODE_INVALID_DATES | - (thd->variables.sql_mode & - (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE))), - &error); - - if (timestamp_type == MYSQL_TIMESTAMP_DATETIME || - timestamp_type == MYSQL_TIMESTAMP_DATE) + str_to_datetime(str->ptr(), str->length(), l_time, flags, &error); + + if (timestamp_type > MYSQL_TIMESTAMP_ERROR) /* Do not return yet, we may still want to throw a "trailing garbage" warning. @@ -726,238 +708,35 @@ static ulonglong get_date_from_str(THD *thd, String *str, if (*error_arg) return 0; - return TIME_to_ulonglong_datetime(&l_time); -} - - -/* - Check whether compare_datetime() can be used to compare items. - - SYNOPSIS - Arg_comparator::can_compare_as_dates() - a, b [in] items to be compared - const_value [out] converted value of the string constant, if any - - DESCRIPTION - Check several cases when the DATE/DATETIME comparator should be used. - The following cases are checked: - 1. Both a and b is a DATE/DATETIME field/function returning string or - int result. - 2. Only a or b is a DATE/DATETIME field/function returning string or - int result and the other item (b or a) is an item with string result. - If the second item is a constant one then it's checked to be - convertible to the DATE/DATETIME type. If the constant can't be - converted to a DATE/DATETIME then the compare_datetime() comparator - isn't used and the warning about wrong DATE/DATETIME value is issued. - In all other cases (date-[int|real|decimal]/[int|real|decimal]-date) - the comparison is handled by other comparators. - If the datetime comparator can be used and one the operands of the - comparison is a string constant that was successfully converted to a - DATE/DATETIME type then the result of the conversion is returned in the - const_value if it is provided. If there is no constant or - compare_datetime() isn't applicable then the *const_value remains - unchanged. - - RETURN - the found type of date comparison -*/ - -enum Arg_comparator::enum_date_cmp_type -Arg_comparator::can_compare_as_dates(Item *a, Item *b, ulonglong *const_value) -{ - enum enum_date_cmp_type cmp_type= CMP_DATE_DFLT; - Item *str_arg= 0, *date_arg= 0; - - if (a->type() == Item::ROW_ITEM || b->type() == Item::ROW_ITEM) - return CMP_DATE_DFLT; - - if (a->is_datetime()) - { - if (b->is_datetime()) - cmp_type= CMP_DATE_WITH_DATE; - else if (b->result_type() == STRING_RESULT) - { - cmp_type= CMP_DATE_WITH_STR; - date_arg= a; - str_arg= b; - } - } - else if (b->is_datetime() && a->result_type() == STRING_RESULT) - { - cmp_type= CMP_STR_WITH_DATE; - date_arg= b; - str_arg= a; - } - - if (cmp_type != CMP_DATE_DFLT) - { - THD *thd= current_thd; - /* - Do not cache GET_USER_VAR() function as its const_item() may return TRUE - for the current thread but it still may change during the execution. - Don't use cache while in the context analysis mode only (i.e. for - EXPLAIN/CREATE VIEW and similar queries). Cache is useless in such - cases and can cause problems. For example evaluating subqueries can - confuse storage engines since in context analysis mode tables - aren't locked. - */ - if (!thd->lex->is_ps_or_view_context_analysis() && - cmp_type != CMP_DATE_WITH_DATE && str_arg->const_item() && - (str_arg->type() != Item::FUNC_ITEM || - ((Item_func*)str_arg)->functype() != Item_func::GUSERVAR_FUNC)) - { - ulonglong value; - bool error; - String tmp, *str_val= 0; - timestamp_type t_type= (date_arg->field_type() == MYSQL_TYPE_DATE ? - MYSQL_TIMESTAMP_DATE : MYSQL_TIMESTAMP_DATETIME); - - str_val= str_arg->val_str(&tmp); - if (str_arg->null_value) - return CMP_DATE_DFLT; - value= get_date_from_str(thd, str_val, t_type, date_arg->name, &error); - if (error) - return CMP_DATE_DFLT; - if (const_value) - *const_value= value; - } - } - return cmp_type; + return pack_time(&l_time); } -/* - Retrieves correct TIME value from the given item. - - SYNOPSIS - get_time_value() - thd thread handle - item_arg [in/out] item to retrieve TIME value from - cache_arg [in/out] pointer to place to store the cache item to - warn_item [in] unused - is_null [out] TRUE <=> the item_arg is null +/** + Prepare the comparator (set the comparison function) for comparing + items *a1 and *a2 in the context of 'type'. - DESCRIPTION - Retrieves the correct TIME value from given item for comparison by the - compare_datetime() function. - If item's result can be compared as longlong then its int value is used - and a value returned by get_time function is used otherwise. - If an item is a constant one then its value is cached and it isn't - get parsed again. An Item_cache_int object is used for for cached values. - It seamlessly substitutes the original item. The cache item is marked as - non-constant to prevent re-caching it again. + @param[in] owner_arg Item, peforming the comparison (e.g. Item_func_eq) + @param[in,out] a1 first argument to compare + @param[in,out] a2 second argument to compare + @param[in] type type context to compare in - RETURN - obtained value + Both *a1 and *a2 can be replaced by this method - typically by constant + items, holding the cached converted value of the original (constant) item. */ -longlong -get_time_value(THD *thd, Item ***item_arg, Item **cache_arg, - Item *warn_item, bool *is_null) -{ - longlong value; - Item *item= **item_arg; - MYSQL_TIME ltime; - - if (item->result_as_longlong()) - { - value= item->val_int(); - *is_null= item->null_value; - } - else - { - *is_null= item->get_time(<ime); - value= !*is_null ? (longlong) TIME_to_ulonglong_datetime(<ime) : 0; - } - /* - Do not cache GET_USER_VAR() function as its const_item() may return TRUE - for the current thread but it still may change during the execution. - */ - if (item->const_item() && cache_arg && (item->type() != Item::FUNC_ITEM || - ((Item_func*)item)->functype() != Item_func::GUSERVAR_FUNC)) - { - Query_arena backup; - Query_arena *save_arena= thd->switch_to_arena_for_cached_items(&backup); - Item_cache_int *cache= new Item_cache_int(); - if (save_arena) - thd->set_query_arena(save_arena); - - /* Mark the cache as non-const to prevent re-caching. */ - cache->set_used_tables(1); - cache->store_longlong(item, value); - *cache_arg= cache; - *item_arg= cache_arg; - } - return value; -} - - int Arg_comparator::set_cmp_func(Item_result_field *owner_arg, Item **a1, Item **a2, Item_result type) { - ulonglong const_value= (ulonglong)-1; thd= current_thd; owner= owner_arg; set_null= set_null && owner_arg; a= a1; b= a2; - thd= current_thd; - if (can_compare_as_dates(*a, *b, &const_value)) - { - a_type= (*a)->field_type(); - b_type= (*b)->field_type(); - a_cache= 0; - b_cache= 0; - - if (const_value != (ulonglong)-1) - { - /* - cache_converted_constant can't be used here because it can't - correctly convert a DATETIME value from string to int representation. - */ - Query_arena backup; - Query_arena *save_arena= thd->switch_to_arena_for_cached_items(&backup); - Item_cache_int *cache= new Item_cache_int(MYSQL_TYPE_DATETIME); - if (save_arena) - thd->set_query_arena(save_arena); - - /* Mark the cache as non-const to prevent re-caching. */ - cache->set_used_tables(1); - if (!(*a)->is_datetime()) - { - cache->store_longlong((*a), const_value); - a_cache= cache; - a= (Item **)&a_cache; - } - else - { - cache->store_longlong((*b), const_value); - b_cache= cache; - b= (Item **)&b_cache; - } - } - is_nulls_eq= is_owner_equal_func(); - func= &Arg_comparator::compare_datetime; - get_value_a_func= &get_datetime_value; - get_value_b_func= &get_datetime_value; - return 0; - } - else if (type == STRING_RESULT && (*a)->field_type() == MYSQL_TYPE_TIME && - (*b)->field_type() == MYSQL_TYPE_TIME) - { - /* Compare TIME values as integers. */ - a_cache= 0; - b_cache= 0; - is_nulls_eq= is_owner_equal_func(); - func= &Arg_comparator::compare_datetime; - get_value_a_func= &get_time_value; - get_value_b_func= &get_time_value; - return 0; - } - else if (type == STRING_RESULT && - (*a)->result_type() == STRING_RESULT && - (*b)->result_type() == STRING_RESULT) + if (type == STRING_RESULT && + (*a)->result_type() == STRING_RESULT && + (*b)->result_type() == STRING_RESULT) { DTCollation coll; coll.set((*a)->collation.collation); @@ -965,8 +744,10 @@ int Arg_comparator::set_cmp_func(Item_result_field *owner_arg, b, 1, MY_COLL_CMP_CONV, 1)) return 1; } - else if (try_year_cmp_func(type)) - return 0; + if (type == INT_RESULT && + (*a)->field_type() == MYSQL_TYPE_YEAR && + (*b)->field_type() == MYSQL_TYPE_YEAR) + type= TIME_RESULT; a= cache_converted_constant(thd, a, &a_cache, type); b= cache_converted_constant(thd, b, &b_cache, type); @@ -974,45 +755,6 @@ int Arg_comparator::set_cmp_func(Item_result_field *owner_arg, } -/* - Helper function to call from Arg_comparator::set_cmp_func() -*/ - -bool Arg_comparator::try_year_cmp_func(Item_result type) -{ - if (type == ROW_RESULT) - return FALSE; - - bool a_is_year= (*a)->field_type() == MYSQL_TYPE_YEAR; - bool b_is_year= (*b)->field_type() == MYSQL_TYPE_YEAR; - - if (!a_is_year && !b_is_year) - return FALSE; - - if (a_is_year && b_is_year) - { - get_value_a_func= &get_year_value; - get_value_b_func= &get_year_value; - } - else if (a_is_year && (*b)->is_datetime()) - { - get_value_a_func= &get_year_value; - get_value_b_func= &get_datetime_value; - } - else if (b_is_year && (*a)->is_datetime()) - { - get_value_b_func= &get_year_value; - get_value_a_func= &get_datetime_value; - } - else - return FALSE; - - is_nulls_eq= is_owner_equal_func(); - func= &Arg_comparator::compare_datetime; - - return TRUE; -} - /** Convert and cache a constant. @@ -1034,9 +776,14 @@ Item** Arg_comparator::cache_converted_constant(THD *thd_arg, Item **value, Item **cache_item, Item_result type) { - /* Don't need cache if doing context analysis only. */ + /* + Don't need cache if doing context analysis only. + Also, get_datetime_value creates Item_cache internally. + Unless fixed, we should not do it here. + */ if (!thd->lex->is_ps_or_view_context_analysis() && - (*value)->const_item() && type != (*value)->result_type()) + (*value)->const_item() && type != (*value)->result_type() && + type != TIME_RESULT) { Item_cache *cache= Item_cache::get_cache(*value, type); cache->setup(*value); @@ -1054,111 +801,168 @@ void Arg_comparator::set_datetime_cmp_func(Item_result_field *owner_arg, owner= owner_arg; a= a1; b= b1; - a_type= (*a)->field_type(); - b_type= (*b)->field_type(); a_cache= 0; b_cache= 0; - is_nulls_eq= FALSE; - func= &Arg_comparator::compare_datetime; - get_value_a_func= &get_datetime_value; - get_value_b_func= &get_datetime_value; + func= comparator_matrix[TIME_RESULT][is_owner_equal_func()]; } - -/* +/** Retrieves correct DATETIME value from given item. - SYNOPSIS - get_datetime_value() - thd thread handle - item_arg [in/out] item to retrieve DATETIME value from - cache_arg [in/out] pointer to place to store the caching item to - warn_item [in] item for issuing the conversion warning - is_null [out] TRUE <=> the item_arg is null + @param[in] thd thread handle + @param[in,out] item_arg item to retrieve DATETIME value from + @param[in,out] cache_arg pointer to place to store the caching item to + @param[in] warn_item item for issuing the conversion warning + @param[out] is_null TRUE <=> the item_arg is null - DESCRIPTION + @details Retrieves the correct DATETIME value from given item for comparison by the compare_datetime() function. - If item's result can be compared as longlong then its int value is used - and its string value is used otherwise. Strings are always parsed and - converted to int values by the get_date_from_str() function. - This allows us to compare correctly string dates with missed insignificant - zeros. If an item is a constant one then its value is cached and it isn't - get parsed again. An Item_cache_int object is used for caching values. It - seamlessly substitutes the original item. The cache item is marked as - non-constant to prevent re-caching it again. In order to compare - correctly DATE and DATETIME items the result of the former are treated as - a DATETIME with zero time (00:00:00). - RETURN - obtained value + If the value should be compared as time (TIME_RESULT), it's retrieved as + MYSQL_TIME. Otherwise it's read as a number/string and converted to time. + Constant items are cached, so the convertion is only done once for them. + + Note the f_type behavior: if the item can be compared as time, then + f_type is this item's field_type(). Otherwise it's field_type() of + warn_item (which is the other operand of the comparison operator). + This logic provides correct string/number to date/time conversion + depending on the other operand (when comparing a string with a date, it's + parsed as a date, when comparing a string with a time it's parsed as a time) + + If the item is a constant it is replaced by the Item_cache_int, that + holds the packed datetime value. + + @return + MYSQL_TIME value, packed in a longlong, suitable for comparison. */ longlong get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, Item *warn_item, bool *is_null) { - longlong value= 0; - String buf, *str= 0; + longlong UNINIT_VAR(value); Item *item= **item_arg; + enum_field_types f_type= warn_item->field_type(); - if (item->result_as_longlong()) - { + switch (item->cmp_type()) { + case TIME_RESULT: + /* if it's our Item_cache_int, as created below, we simply use the value */ + if (item->result_type() == INT_RESULT) + { + value= item->val_int(); + cache_arg= 0; + } + else + { + MYSQL_TIME buf; + if (item->get_date_result(&buf, TIME_FUZZY_DATE | TIME_INVALID_DATES)) + DBUG_ASSERT(item->null_value); + else + value= pack_time(&buf); + f_type= item->field_type(); // for Item_cache_int below. + } + break; + case INT_RESULT: value= item->val_int(); - *is_null= item->null_value; - enum_field_types f_type= item->field_type(); - /* - Item_date_add_interval may return MYSQL_TYPE_STRING as the result - field type. To detect that the DATE value has been returned we - compare it with 100000000L - any DATE value should be less than it. - Don't shift cached DATETIME values up for the second time. - */ - if (f_type == MYSQL_TYPE_DATE || - (f_type != MYSQL_TYPE_DATETIME && value < 100000000L)) - value*= 1000000L; - } - else - { - str= item->val_str(&buf); - *is_null= item->null_value; + + if (item->field_type() == MYSQL_TYPE_YEAR) + { + /* + Coerce value to the 19XX form in order to correctly compare + YEAR(2) & YEAR(4) types. + Here we are converting all item values but YEAR(4) fields since + 1) YEAR(4) already has a regular YYYY form and + 2) we don't want to convert zero/bad YEAR(4) values to the + value of 2000. + */ + Item *real_item= item->real_item(); + if (real_item->max_length == 2) + { + if (value < 70) + value+= 100; + if (value <= 1900) + value+= 1900; + } + value*= 13ULL * 32ULL * 24ULL * 60ULL * 60ULL * 1000000ULL; + } + else + { + MYSQL_TIME buf; + int was_cut; + longlong res; + + if (f_type == MYSQL_TYPE_TIME) + res= number_to_time((double)value, &buf, &was_cut); + else + res= number_to_datetime(value, &buf, TIME_INVALID_DATES|TIME_FUZZY_DATE, + &was_cut); + if (res == -1) + { + const Lazy_string_num str(value); + make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, &str, + mysql_type_to_time_type(f_type), + warn_item->field_name_or_null()); + value= 0; + } + else + value= pack_time(&buf); + } + break; + case STRING_RESULT: + case DECIMAL_RESULT: + case REAL_RESULT: + { + char strbuf[MAX_DATETIME_FULL_WIDTH]; + String buf(strbuf, sizeof(strbuf), &my_charset_bin), *str; + if ((str= item->val_str(&buf))) + { + /* + Convert strings to the integer DATE/DATETIME representation. + + Even if both dates provided in strings we cannot compare them + directly as strings as there is no warranty that they are correct + and do not miss some insignificant zeros. + */ + bool error; + value= (longlong) get_date_from_str(thd, str, + mysql_type_to_time_type(f_type), + warn_item->field_name_or_null(), + &error); + /* + If str did not contain a valid date according to the current + SQL_MODE, get_date_from_str() has already thrown a warning, + and we don't want to throw NULL on invalid date (see 5.2.6 + "SQL modes" in the manual), so we're done here. + */ + } + break; + } + case ROW_RESULT: + case IMPOSSIBLE_RESULT: + DBUG_ASSERT(0); } - if (*is_null) + if ((*is_null= item->null_value)) return ~(ulonglong) 0; - /* - Convert strings to the integer DATE/DATETIME representation. - Even if both dates provided in strings we can't compare them directly as - strings as there is no warranty that they are correct and do not miss - some insignificant zeros. - */ - if (str) + if (cache_arg && item->const_item()) { - bool error; - enum_field_types f_type= warn_item->field_type(); - timestamp_type t_type= f_type == - MYSQL_TYPE_DATE ? MYSQL_TIMESTAMP_DATE : MYSQL_TIMESTAMP_DATETIME; - value= (longlong) get_date_from_str(thd, str, t_type, warn_item->name, &error); /* - If str did not contain a valid date according to the current - SQL_MODE, get_date_from_str() has already thrown a warning, - and we don't want to throw NULL on invalid date (see 5.2.6 - "SQL modes" in the manual), so we're done here. + cache the packed datetime value in the Item_cache object. + Because the packed datetime value is longlong, we use Item_cache_int, + and it has result_type() == INT_RESULT. + But we create it to have field_type() == MYSQL_TYPE_TIME (or + MYSQL_TIMESTAMP_DATE or MYSQL_TYPE_DATETIME), and thus it will have + cmp_type() == TIME_RESULT. + As no other item can have this combination of cmp_type() and + result_type(), it allows us to identify our cache items, see + 'case TIME_RESULT:' above. */ - } - /* - Do not cache GET_USER_VAR() function as its const_item() may return TRUE - for the current thread but it still may change during the execution. - */ - if (item->const_item() && cache_arg && (item->type() != Item::FUNC_ITEM || - ((Item_func*)item)->functype() != Item_func::GUSERVAR_FUNC)) - { Query_arena backup; Query_arena *save_arena= thd->switch_to_arena_for_cached_items(&backup); - Item_cache_int *cache= new Item_cache_int(MYSQL_TYPE_DATETIME); + Item_cache_int *cache= new Item_cache_int(f_type); if (save_arena) thd->set_query_arena(save_arena); - - /* Mark the cache as non-const to prevent re-caching. */ - cache->set_used_tables(1); + cache->store_longlong(item, value); *cache_arg= cache; *item_arg= cache_arg; @@ -1168,67 +972,6 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, /* - Retrieves YEAR value of 19XX-00-00 00:00:00 form from given item. - - SYNOPSIS - get_year_value() - thd thread handle - item_arg [in/out] item to retrieve YEAR value from - cache_arg [in/out] pointer to place to store the caching item to - warn_item [in] item for issuing the conversion warning - is_null [out] TRUE <=> the item_arg is null - - DESCRIPTION - Retrieves the YEAR value of 19XX form from given item for comparison by the - compare_datetime() function. - Converts year to DATETIME of form YYYY-00-00 00:00:00 for the compatibility - with the get_datetime_value function result. - - RETURN - obtained value -*/ - -static longlong -get_year_value(THD *thd, Item ***item_arg, Item **cache_arg, - Item *warn_item, bool *is_null) -{ - longlong value= 0; - Item *item= **item_arg; - - value= item->val_int(); - *is_null= item->null_value; - if (*is_null) - return ~(ulonglong) 0; - - /* - Coerce value to the 19XX form in order to correctly compare - YEAR(2) & YEAR(4) types. - Here we are converting all item values but YEAR(4) fields since - 1) YEAR(4) already has a regular YYYY form and - 2) we don't want to convert zero/bad YEAR(4) values to the - value of 2000. - */ - Item *real_item= item->real_item(); - Field *field= NULL; - if (real_item->type() == Item::FIELD_ITEM) - field= ((Item_field *)real_item)->field; - else if (real_item->type() == Item::CACHE_ITEM) - field= ((Item_cache *)real_item)->field(); - if (!(field && field->type() == MYSQL_TYPE_YEAR && field->field_length == 4)) - { - if (value < 70) - value+= 100; - if (value <= 1900) - value+= 1900; - } - /* Convert year to DATETIME of form YYYY-00-00 00:00:00 (YYYY0000000000). */ - value*= 10000000000LL; - - return value; -} - - -/* Compare items values as dates. SYNOPSIS @@ -1240,18 +983,9 @@ get_year_value(THD *thd, Item ***item_arg, Item **cache_arg, with help of the get_datetime_value() function. RETURN - If is_nulls_eq is TRUE: - 1 if items are equal or both are null - 0 otherwise - If is_nulls_eq is FALSE: -1 a < b or at least one item is null 0 a == b 1 a > b - See the table: - is_nulls_eq | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | - a_is_null | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | - b_is_null | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | - result | 1 | 0 | 0 |0/1|-1 |-1 |-1 |-1/0/1| */ int Arg_comparator::compare_datetime() @@ -1259,34 +993,40 @@ int Arg_comparator::compare_datetime() bool a_is_null, b_is_null; longlong a_value, b_value; + if (set_null) + owner->null_value= 1; + /* Get DATE/DATETIME/TIME value of the 'a' item. */ - a_value= (*get_value_a_func)(thd, &a, &a_cache, *b, &a_is_null); - if (!is_nulls_eq && a_is_null) - { - if (set_null) - owner->null_value= 1; + a_value= get_datetime_value(thd, &a, &a_cache, *b, &a_is_null); + if (a_is_null) return -1; - } /* Get DATE/DATETIME/TIME value of the 'b' item. */ - b_value= (*get_value_b_func)(thd, &b, &b_cache, *a, &b_is_null); - if (a_is_null || b_is_null) - { - if (set_null) - owner->null_value= is_nulls_eq ? 0 : 1; - return is_nulls_eq ? (a_is_null == b_is_null) : -1; - } + b_value= get_datetime_value(thd, &b, &b_cache, *a, &b_is_null); + if (b_is_null) + return -1; /* Here we have two not-NULL values. */ if (set_null) owner->null_value= 0; /* Compare values. */ - if (is_nulls_eq) - return (a_value == b_value); - return a_value < b_value ? -1 : (a_value > b_value ? 1 : 0); + return a_value < b_value ? -1 : a_value > b_value ? 1 : 0; } +int Arg_comparator::compare_e_datetime() +{ + bool a_is_null, b_is_null; + longlong a_value, b_value; + + /* Get DATE/DATETIME/TIME value of the 'a' item. */ + a_value= get_datetime_value(thd, &a, &a_cache, *b, &a_is_null); + + /* Get DATE/DATETIME/TIME value of the 'b' item. */ + b_value= get_datetime_value(thd, &b, &b_cache, *a, &b_is_null); + return a_is_null || b_is_null ? a_is_null == b_is_null + : a_value == b_value; +} int Arg_comparator::compare_string() { @@ -2411,12 +2151,9 @@ bool Item_func_between::fix_fields(THD *thd, Item **ref) void Item_func_between::fix_length_and_dec() { - max_length= 1; - int i; - bool datetime_found= FALSE; - int time_items_found= 0; - compare_as_dates= TRUE; THD *thd= current_thd; + max_length= 1; + compare_as_dates= 0; /* As some compare functions are generated after sql_yacc, @@ -2431,51 +2168,44 @@ void Item_func_between::fix_length_and_dec() return; /* - Detect the comparison of DATE/DATETIME items. - At least one of items should be a DATE/DATETIME item and other items - should return the STRING result. + When comparing as date/time, we need to convert non-temporal values + (e.g. strings) to MYSQL_TIME. get_datetime_value() does it + automatically when one of the operands is a date/time. But here we + may need to compare two strings as dates (str1 BETWEEN str2 AND date). + For this to work, we need to know what date/time type we compare + strings as. */ - if (cmp_type == STRING_RESULT) + if (cmp_type == TIME_RESULT) { - for (i= 0; i < 3; i++) + for (int i= 0; i < 3; i++) { - if (args[i]->is_datetime()) + if (args[i]->cmp_type() == TIME_RESULT) { - datetime_found= TRUE; + if (args[i]->field_type() != MYSQL_TYPE_TIME || + (args[i]->field_type() == MYSQL_TYPE_TIME && compare_as_dates==0)) + compare_as_dates= args[i]; continue; } - if (args[i]->field_type() == MYSQL_TYPE_TIME && - args[i]->result_as_longlong()) - time_items_found++; } } - if (!datetime_found) - compare_as_dates= FALSE; - if (compare_as_dates) - { - ge_cmp.set_datetime_cmp_func(this, args, args + 1); - le_cmp.set_datetime_cmp_func(this, args, args + 2); - } - else if (time_items_found == 3) - { - /* Compare TIME items as integers. */ - cmp_type= INT_RESULT; - } - else if (args[0]->real_item()->type() == FIELD_ITEM && - thd->lex->sql_command != SQLCOM_CREATE_VIEW && - thd->lex->sql_command != SQLCOM_SHOW_CREATE) + /* See the comment about the similar block in Item_bool_func2 */ + if (args[0]->real_item()->type() == FIELD_ITEM && + !thd->lex->is_ps_or_view_context_analysis()) { Item_field *field_item= (Item_field*) (args[0]->real_item()); - if (field_item->field->can_be_compared_as_longlong()) + if (field_item->cmp_type() == INT_RESULT) { + bool special= field_item->field->special_const_compare(); /* The following can't be recoded with || as convert_constant_item changes the argument */ - if (convert_constant_item(thd, field_item, &args[1])) + if ((special || args[1]->result_type() == STRING_RESULT) && + convert_constant_item(thd, field_item, &args[1])) cmp_type=INT_RESULT; // Works for all types. - if (convert_constant_item(thd, field_item, &args[2])) + if ((special || args[2]->result_type() == STRING_RESULT) && + convert_constant_item(thd, field_item, &args[2])) cmp_type=INT_RESULT; // Works for all types. } } @@ -2483,29 +2213,48 @@ void Item_func_between::fix_length_and_dec() longlong Item_func_between::val_int() -{ // ANSI BETWEEN +{ DBUG_ASSERT(fixed == 1); - if (compare_as_dates) + + switch (cmp_type) { + case TIME_RESULT: { - int ge_res, le_res; + THD *thd= current_thd; + longlong value, a, b; + Item *cache, **ptr; + bool value_is_null, a_is_null, b_is_null; + + ptr= &args[0]; + value= get_datetime_value(thd, &ptr, &cache, compare_as_dates, + &value_is_null); + if (ptr != &args[0]) + thd->change_item_tree(&args[0], *ptr); - ge_res= ge_cmp.compare(); - if ((null_value= args[0]->null_value)) + if ((null_value= value_is_null)) return 0; - le_res= le_cmp.compare(); - if (!args[1]->null_value && !args[2]->null_value) - return (longlong) ((ge_res >= 0 && le_res <=0) != negated); - else if (args[1]->null_value) - { - null_value= le_res > 0; // not null if false range. - } + ptr= &args[1]; + a= get_datetime_value(thd, &ptr, &cache, compare_as_dates, &a_is_null); + if (ptr != &args[1]) + thd->change_item_tree(&args[1], *ptr); + + ptr= &args[2]; + b= get_datetime_value(thd, &ptr, &cache, compare_as_dates, &b_is_null); + if (ptr != &args[2]) + thd->change_item_tree(&args[2], *ptr); + + if (!a_is_null && !b_is_null) + return (longlong) ((value >= a && value <= b) != negated); + if (a_is_null && b_is_null) + null_value=1; + else if (a_is_null) + null_value= value <= b; // not null if false range. else - { - null_value= ge_res < 0; - } + null_value= value >= a; + break; } - else if (cmp_type == STRING_RESULT) + + case STRING_RESULT: { String *value,*a,*b; value=args[0]->val_str(&value0); @@ -2529,8 +2278,9 @@ longlong Item_func_between::val_int() // Set to not null if false range. null_value= sortcmp(value,a,cmp_collation.collation) >= 0; } + break; } - else if (cmp_type == INT_RESULT) + case INT_RESULT: { longlong value=args[0]->val_int(), a, b; if ((null_value=args[0]->null_value)) @@ -2549,8 +2299,9 @@ longlong Item_func_between::val_int() { null_value= value >= a; } + break; } - else if (cmp_type == DECIMAL_RESULT) + case DECIMAL_RESULT: { my_decimal dec_buf, *dec= args[0]->val_decimal(&dec_buf), a_buf, *a_dec, b_buf, *b_dec; @@ -2567,8 +2318,9 @@ longlong Item_func_between::val_int() null_value= (my_decimal_cmp(dec, b_dec) <= 0); else null_value= (my_decimal_cmp(dec, a_dec) >= 0); + break; } - else + case REAL_RESULT: { double value= args[0]->val_real(),a,b; if ((null_value=args[0]->null_value)) @@ -2587,6 +2339,13 @@ longlong Item_func_between::val_int() { null_value= value >= a; } + break; + } + case ROW_RESULT: + case IMPOSSIBLE_RESULT: + DBUG_ASSERT(0); + null_value= 1; + return 0; } return (longlong) (!null_value && negated); } @@ -2637,7 +2396,8 @@ Item_func_ifnull::fix_length_and_dec() decimals= 0; break; case ROW_RESULT: - default: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); } cached_field_type= agg_field_type(args, 2); @@ -3349,6 +3109,21 @@ double Item_func_coalesce::real_op() } +bool Item_func_coalesce::get_date(MYSQL_TIME *ltime,uint fuzzydate) +{ + DBUG_ASSERT(fixed == 1); + null_value= 0; + for (uint i= 0; i < arg_count; i++) + { + bool res= args[i]->get_date(ltime, fuzzydate); + if (!args[i]->null_value) + return res; + } + null_value=1; + return 1; +} + + my_decimal *Item_func_coalesce::decimal_op(my_decimal *decimal_value) { DBUG_ASSERT(fixed == 1); @@ -3368,6 +3143,14 @@ void Item_func_coalesce::fix_length_and_dec() { cached_field_type= agg_field_type(args, arg_count); agg_result_type(&hybrid_type, args, arg_count); + Item_result cmp_type; + agg_cmp_type(&cmp_type, args, arg_count); + ///< @todo let result_type() return TIME_RESULT and remove this special case + if (cmp_type == TIME_RESULT) + { + count_real_length(); + return; + } switch (hybrid_type) { case STRING_RESULT: count_only_length(); @@ -3385,7 +3168,8 @@ void Item_func_coalesce::fix_length_and_dec() decimals= 0; break; case ROW_RESULT: - default: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); } } @@ -3714,7 +3498,8 @@ cmp_item* cmp_item::get_comparator(Item_result type, return new cmp_item_row; case DECIMAL_RESULT: return new cmp_item_decimal; - default: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); break; } @@ -4065,7 +3850,7 @@ void Item_func_in::fix_length_and_dec() skip_column= TRUE; break; } - else if (itm->is_datetime()) + else if (itm->cmp_type() == TIME_RESULT) { datetime_found= TRUE; /* @@ -4121,19 +3906,22 @@ void Item_func_in::fix_length_and_dec() So we must check here if the column on the left and all the constant values on the right can be compared as integers and adjust the comparison type accordingly. + + See the comment about the similar block in Item_bool_func2 */ if (args[0]->real_item()->type() == FIELD_ITEM && - thd->lex->sql_command != SQLCOM_CREATE_VIEW && - thd->lex->sql_command != SQLCOM_SHOW_CREATE && + !thd->lex->is_ps_or_view_context_analysis() && cmp_type != INT_RESULT) { Item_field *field_item= (Item_field*) (args[0]->real_item()); - if (field_item->field->can_be_compared_as_longlong()) + if (field_item->cmp_type() == INT_RESULT) { + bool special= field_item->field->special_const_compare(); bool all_converted= TRUE; for (arg=args+1, arg_end=args+arg_count; arg != arg_end ; arg++) { - if (!convert_constant_item (thd, field_item, &arg[0])) + if (!(special || arg[0]->result_type() == STRING_RESULT) || + !convert_constant_item(thd, field_item, &arg[0])) all_converted= FALSE; } if (all_converted) @@ -4162,9 +3950,10 @@ void Item_func_in::fix_length_and_dec() case DECIMAL_RESULT: array= new in_decimal(arg_count - 1); break; - default: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); - return; + break; } } if (array && !(thd->is_fatal_error)) // If not EOM @@ -5556,9 +5345,9 @@ Item_equal::Item_equal(Item *f1, Item *f2, bool with_const_item) { const_item_cache= 0; with_const= with_const_item; - compare_as_dates= with_const_item && f2->is_datetime(); equal_items.push_back(f1); equal_items.push_back(f2); + compare_as_dates= with_const_item && f2->cmp_type() == TIME_RESULT; } @@ -5615,7 +5404,7 @@ void Item_equal::add_const(Item *c, Item *f) { with_const= TRUE; if (f) - compare_as_dates= f->is_datetime(); + compare_as_dates= f->cmp_type() == TIME_RESULT; equal_items.push_front(c); return; } @@ -5636,7 +5425,6 @@ void Item_equal::add_const(Item *c, Item *f) const_item_cache= 1; } - /** @brief Check whether a field is referred to in the multiple equality diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index e4e6cd391a8..c6d2e929530 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -34,32 +34,22 @@ class Arg_comparator: public Sql_alloc Item **a, **b; arg_cmp_func func; Item_result_field *owner; + bool set_null; // TRUE <=> set owner->null_value Arg_comparator *comparators; // used only for compare_row() double precision; /* Fields used in DATE/DATETIME comparison. */ THD *thd; - enum_field_types a_type, b_type; // Types of a and b items Item *a_cache, *b_cache; // Cached values of a and b items - bool is_nulls_eq; // TRUE <=> compare for the EQUAL_FUNC - bool set_null; // TRUE <=> set owner->null_value // when one of arguments is NULL. - enum enum_date_cmp_type { CMP_DATE_DFLT= 0, CMP_DATE_WITH_DATE, - CMP_DATE_WITH_STR, CMP_STR_WITH_DATE }; - longlong (*get_value_a_func)(THD *thd, Item ***item_arg, Item **cache_arg, - Item *warn_item, bool *is_null); - longlong (*get_value_b_func)(THD *thd, Item ***item_arg, Item **cache_arg, - Item *warn_item, bool *is_null); - bool try_year_cmp_func(Item_result type); public: DTCollation cmp_collation; /* Allow owner function to use string buffers. */ String value1, value2; - Arg_comparator(): comparators(0), thd(0), a_cache(0), b_cache(0), set_null(TRUE), - get_value_a_func(0), get_value_b_func(0) {}; - Arg_comparator(Item **a1, Item **a2): a(a1), b(a2), comparators(0), thd(0), - a_cache(0), b_cache(0), set_null(TRUE), - get_value_a_func(0), get_value_b_func(0) {}; + Arg_comparator(): set_null(TRUE), comparators(0), thd(0), + a_cache(0), b_cache(0) {}; + Arg_comparator(Item **a1, Item **a2): a(a1), b(a2), set_null(TRUE), + comparators(0), thd(0), a_cache(0), b_cache(0) {}; int set_compare_func(Item_result_field *owner, Item_result type); inline int set_compare_func(Item_result_field *owner_arg) @@ -76,8 +66,8 @@ public: { set_null= set_null_arg; return set_cmp_func(owner_arg, a1, a2, - item_cmp_type((*a1)->result_type(), - (*a2)->result_type())); + item_cmp_type((*a1)->cmp_type(), + (*a2)->cmp_type())); } inline int compare() { return (this->*func)(); } @@ -100,14 +90,12 @@ public: int compare_real_fixed(); int compare_e_real_fixed(); int compare_datetime(); // compare args[0] & args[1] as DATETIMEs - - static enum enum_date_cmp_type can_compare_as_dates(Item *a, Item *b, - ulonglong *const_val_arg); + int compare_e_datetime(); Item** cache_converted_constant(THD *thd, Item **value, Item **cache, Item_result type); void set_datetime_cmp_func(Item_result_field *owner_arg, Item **a1, Item **b1); - static arg_cmp_func comparator_matrix [5][2]; + static arg_cmp_func comparator_matrix [6][2]; inline bool is_owner_equal_func() { return (owner->type() == Item::FUNC_ITEM && @@ -636,9 +624,7 @@ public: Item_result cmp_type; String value0,value1,value2; /* TRUE <=> arguments will be compared as dates. */ - bool compare_as_dates; - /* Comparators used for DATE/DATETIME comparison. */ - Arg_comparator ge_cmp, le_cmp; + Item *compare_as_dates; Item_func_between(Item *a, Item *b, Item *c) :Item_func_opt_neg(a, b, c), compare_as_dates(FALSE) {} longlong val_int(); @@ -711,6 +697,7 @@ public: const char *func_name() const { return "coalesce"; } table_map not_null_tables() const { return 0; } enum_field_types field_type() const { return cached_field_type; } + bool get_date(MYSQL_TIME *ltime,uint fuzzydate); }; @@ -916,6 +903,16 @@ public: lval_cache(0) {}; void set(uint pos,Item *item); uchar *get_value(Item *item); + Item* create_item() + { + return new Item_datetime(); + } + void value_to_item(uint pos, Item *item) + { + packed_longlong *val= reinterpret_cast<packed_longlong*>(base)+pos; + Item_datetime *dt= reinterpret_cast<Item_datetime*>(item); + dt->set(val->val); + } friend int cmp_longlong(void *cmp_arg, packed_longlong *a,packed_longlong *b); }; diff --git a/sql/item_create.cc b/sql/item_create.cc index 9c66c94382d..519599bbde0 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -31,7 +31,28 @@ ============================================================================= */ -/* +static const char* item_name(Item *a, String *str) +{ + if (a->name) + return a->name; + str->length(0); + a->print(str, QT_ORDINARY); + return str->c_ptr_safe(); +} + + +static void wrong_precision_error(uint errcode, Item *a, + ulonglong number, ulong maximum) +{ + char buff[1024]; + String buf(buff, sizeof(buff), system_charset_info); + + my_error(errcode, MYF(0), (uint) min(number, UINT_MAX32), + item_name(a, &buf), maximum); +} + + +/** Get precision and scale for a declaration return @@ -42,18 +63,16 @@ bool get_length_and_scale(ulonglong length, ulonglong decimals, ulong *out_length, uint *out_decimals, uint max_precision, uint max_scale, - const char *name) + Item *a) { if (length > (ulonglong) max_precision) { - my_error(ER_TOO_BIG_PRECISION, MYF(0), (int) length, name, - max_precision); + wrong_precision_error(ER_TOO_BIG_PRECISION, a, length, max_precision); return 1; } if (decimals > (ulonglong) max_scale) { - my_error(ER_TOO_BIG_SCALE, MYF(0), (int) decimals, name, - DECIMAL_MAX_SCALE); + wrong_precision_error(ER_TOO_BIG_SCALE, a, decimals, max_scale); return 1; } @@ -5124,10 +5143,22 @@ create_func_cast(THD *thd, Item *a, Cast_target cast_type, res= new (thd->mem_root) Item_date_typecast(a); break; case ITEM_CAST_TIME: - res= new (thd->mem_root) Item_time_typecast(a); + if (decimals > MAX_DATETIME_PRECISION) + { + wrong_precision_error(ER_TOO_BIG_PRECISION, a, decimals, + MAX_DATETIME_PRECISION); + return 0; + } + res= new (thd->mem_root) Item_time_typecast(a, (uint) decimals); break; case ITEM_CAST_DATETIME: - res= new (thd->mem_root) Item_datetime_typecast(a); + if (decimals > MAX_DATETIME_PRECISION) + { + wrong_precision_error(ER_TOO_BIG_PRECISION, a, decimals, + MAX_DATETIME_PRECISION); + return 0; + } + res= new (thd->mem_root) Item_datetime_typecast(a, (uint) decimals); break; case ITEM_CAST_DECIMAL: { @@ -5135,7 +5166,7 @@ create_func_cast(THD *thd, Item *a, Cast_target cast_type, uint dec; if (get_length_and_scale(length, decimals, &len, &dec, DECIMAL_MAX_PRECISION, DECIMAL_MAX_SCALE, - a->name)) + a)) return NULL; res= new (thd->mem_root) Item_decimal_typecast(a, len, dec); break; @@ -5152,7 +5183,7 @@ create_func_cast(THD *thd, Item *a, Cast_target cast_type, } else if (get_length_and_scale(length, decimals, &len, &dec, DECIMAL_MAX_PRECISION, NOT_FIXED_DEC-1, - a->name)) + a)) return NULL; res= new (thd->mem_root) Item_double_typecast(a, (uint) length, (uint) decimals); @@ -5166,7 +5197,9 @@ create_func_cast(THD *thd, Item *a, Cast_target cast_type, { if (length > MAX_FIELD_BLOBLENGTH) { - my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), "cast as char", + char buff[1024]; + String buf(buff, sizeof(buff), system_charset_info); + my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), item_name(a, &buf), MAX_FIELD_BLOBLENGTH); return NULL; } diff --git a/sql/item_func.cc b/sql/item_func.cc index c0c18167796..fb4bca2f671 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -505,7 +505,8 @@ Field *Item_func::tmp_table_field(TABLE *table) field= Field_new_decimal::create_from_item(this); break; case ROW_RESULT: - default: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: // This case should never be chosen DBUG_ASSERT(0); field= 0; @@ -763,7 +764,9 @@ void Item_func_num1::find_num_type() break; case DECIMAL_RESULT: break; - default: + case TIME_RESULT: + case ROW_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); } DBUG_PRINT("info", ("Type: %s", @@ -820,7 +823,9 @@ String *Item_func_numhybrid::val_str(String *str) } case STRING_RESULT: return str_op(&str_value); - default: + case TIME_RESULT: + case ROW_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); } return str; @@ -855,7 +860,9 @@ double Item_func_numhybrid::val_real() return (res ? my_strntod(res->charset(), (char*) res->ptr(), res->length(), &end_not_used, &err_not_used) : 0.0); } - default: + case TIME_RESULT: + case ROW_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); } return 0.0; @@ -890,7 +897,9 @@ longlong Item_func_numhybrid::val_int() CHARSET_INFO *cs= res->charset(); return (*(cs->cset->strtoll10))(cs, res->ptr(), &end, &err_not_used); } - default: + case TIME_RESULT: + case ROW_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); } return 0; @@ -928,7 +937,8 @@ my_decimal *Item_func_numhybrid::val_decimal(my_decimal *decimal_value) break; } case ROW_RESULT: - default: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); } return val; @@ -987,8 +997,7 @@ longlong Item_func_signed::val_int() longlong value; int error; - if (args[0]->cast_to_int_type() != STRING_RESULT || - args[0]->result_as_longlong()) + if (args[0]->cast_to_int_type() != STRING_RESULT) { value= args[0]->val_int(); null_value= args[0]->null_value; @@ -1051,8 +1060,7 @@ longlong Item_func_unsigned::val_int() goto err; // Warn about overflow return value; } - else if (args[0]->cast_to_int_type() != STRING_RESULT || - args[0]->result_as_longlong()) + else if (args[0]->cast_to_int_type() != STRING_RESULT) { value= args[0]->val_int(); null_value= args[0]->null_value; @@ -1445,7 +1453,7 @@ void Item_func_div::fix_length_and_dec() DBUG_ENTER("Item_func_div::fix_length_and_dec"); prec_increment= current_thd->variables.div_precincrement; Item_num_op::fix_length_and_dec(); - switch(hybrid_type) { + switch (hybrid_type) { case REAL_RESULT: { decimals=max(args[0]->decimals,args[1]->decimals)+prec_increment; @@ -1468,7 +1476,10 @@ void Item_func_div::fix_length_and_dec() case DECIMAL_RESULT: result_precision(); break; - default: + case STRING_RESULT: + case ROW_RESULT: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); } maybe_null= 1; // devision by zero @@ -1938,8 +1949,7 @@ void Item_func_int_val::find_num_type() { DBUG_ENTER("Item_func_int_val::find_num_type"); DBUG_PRINT("info", ("name %s", func_name())); - switch(hybrid_type= args[0]->result_type()) - { + switch (hybrid_type= args[0]->result_type()) { case STRING_RESULT: case REAL_RESULT: hybrid_type= REAL_RESULT; @@ -1962,7 +1972,9 @@ void Item_func_int_val::find_num_type() hybrid_type= INT_RESULT; } break; - default: + case ROW_RESULT: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); } DBUG_PRINT("info", ("Type: %s", @@ -2138,7 +2150,9 @@ void Item_func_round::fix_length_and_dec() unsigned_flag); break; } - default: + case ROW_RESULT: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); /* This result type isn't handled */ } } @@ -2346,10 +2360,10 @@ double Item_func_units::val_real() void Item_func_min_max::fix_length_and_dec() { int max_int_part=0; - bool datetime_found= FALSE; decimals=0; max_length=0; maybe_null=0; + thd= current_thd; cmp_type=args[0]->result_type(); for (uint i=0 ; i < arg_count ; i++) @@ -2358,23 +2372,17 @@ void Item_func_min_max::fix_length_and_dec() set_if_bigger(decimals, args[i]->decimals); set_if_bigger(max_int_part, args[i]->decimal_int_part()); if (args[i]->maybe_null) - maybe_null=1; - cmp_type=item_cmp_type(cmp_type,args[i]->result_type()); - if (args[i]->result_type() != ROW_RESULT && args[i]->is_datetime()) + maybe_null= 1; + cmp_type= item_cmp_type(cmp_type,args[i]->result_type()); + if (args[i]->cmp_type() == TIME_RESULT) { - datetime_found= TRUE; - if (!datetime_item || args[i]->field_type() == MYSQL_TYPE_DATETIME) - datetime_item= args[i]; + if (!compare_as_dates || args[i]->field_type() == MYSQL_TYPE_DATETIME) + compare_as_dates= args[i]; } } if (cmp_type == STRING_RESULT) { agg_arg_charsets(collation, args, arg_count, MY_COLL_CMP_CONV, 1); - if (datetime_found) - { - thd= current_thd; - compare_as_dates= TRUE; - } } else if ((cmp_type == DECIMAL_RESULT) || (cmp_type == INT_RESULT)) max_length= my_decimal_precision_to_length_no_truncation(max_int_part + @@ -2382,61 +2390,69 @@ void Item_func_min_max::fix_length_and_dec() unsigned_flag); else if (cmp_type == REAL_RESULT) max_length= float_length(decimals); - cached_field_type= agg_field_type(args, arg_count); + + if (compare_as_dates) + cached_field_type= compare_as_dates->field_type(); + else + cached_field_type= agg_field_type(args, arg_count); } /* Compare item arguments in the DATETIME context. - SYNOPSIS - cmp_datetimes() - value [out] found least/greatest DATE/DATETIME value - DESCRIPTION Compare item arguments as DATETIME values and return the index of the least/greatest argument in the arguments array. - The correct integer DATE/DATETIME value of the found argument is + The correct DATE/DATETIME value of the found argument is stored to the value pointer, if latter is provided. RETURN - 0 If one of arguments is NULL or there was a execution error - # index of the least/greatest argument + 1 If one of arguments is NULL or there was a execution error + 0 Otherwise */ -uint Item_func_min_max::cmp_datetimes(ulonglong *value) +bool Item_func_min_max::get_date(MYSQL_TIME *ltime, uint fuzzy_date) { longlong UNINIT_VAR(min_max); - uint min_max_idx= 0; + DBUG_ASSERT(fixed == 1); + + /* + just like ::val_int() method of an string item can be called, + for example, SELECT CONCAT("10", "12") + 1, + ::get_date() can be called for non-temporal values, + for example, SELECT MONTH(GREATEST("2011-11-21", "2010-10-09")) + + */ + if (!compare_as_dates) + return Item_func::get_date(ltime, fuzzy_date); + + null_value= 0; for (uint i=0; i < arg_count ; i++) { Item **arg= args + i; bool is_null; - longlong res= get_datetime_value(thd, &arg, 0, datetime_item, &is_null); + longlong res= get_datetime_value(thd, &arg, 0, compare_as_dates, &is_null); - /* Check if we need to stop (because of error or KILL) and stop the loop */ - if (thd->is_error()) + /* Check if we need to stop (because of error or KILL) and stop the loop */ + if (thd->is_error() || args[i]->null_value) { null_value= 1; - return 0; + return 1; } - if ((null_value= args[i]->null_value)) - return 0; if (i == 0 || (res < min_max ? cmp_sign : -cmp_sign) > 0) - { min_max= res; - min_max_idx= i; - } } - if (value) + unpack_time(min_max, ltime); + if (compare_as_dates->field_type() == MYSQL_TYPE_DATE) { - *value= min_max; - if (datetime_item->field_type() == MYSQL_TYPE_DATE) - *value/= 1000000L; + ltime->time_type= MYSQL_TIMESTAMP_DATE; + ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0; } - return min_max_idx; + + return 0; } @@ -2445,19 +2461,14 @@ String *Item_func_min_max::val_str(String *str) DBUG_ASSERT(fixed == 1); if (compare_as_dates) { - String *str_res; - uint min_max_idx= cmp_datetimes(NULL); - if (null_value) + MYSQL_TIME ltime; + if (get_date(<ime, TIME_FUZZY_DATE)) return 0; - str_res= args[min_max_idx]->val_str(str); - if (args[min_max_idx]->null_value) - { - // check if the call to val_str() above returns a NULL value - null_value= 1; - return NULL; - } - str_res->set_charset(collation.collation); - return str_res; + + str->alloc(MAX_DATE_STRING_REP_LENGTH); + str->set_charset(collation.collation); + str->length(my_TIME_to_str(<ime, const_cast<char*>(str->ptr()), decimals)); + return str; } switch (cmp_type) { case INT_RESULT: @@ -2509,9 +2520,9 @@ String *Item_func_min_max::val_str(String *str) return res; } case ROW_RESULT: - default: - // This case should never be chosen - DBUG_ASSERT(0); + case TIME_RESULT: + case IMPOSSIBLE_RESULT: + DBUG_ASSERT(0); // This case should never be chosen return 0; } return 0; // Keep compiler happy @@ -2524,9 +2535,11 @@ double Item_func_min_max::val_real() double value=0.0; if (compare_as_dates) { - ulonglong result= 0; - (void)cmp_datetimes(&result); - return (double)result; + MYSQL_TIME ltime; + if (get_date(<ime, TIME_FUZZY_DATE)) + return 0; + + return TIME_to_double(<ime); } for (uint i=0; i < arg_count ; i++) { @@ -2551,9 +2564,11 @@ longlong Item_func_min_max::val_int() longlong value=0; if (compare_as_dates) { - ulonglong result= 0; - (void)cmp_datetimes(&result); - return (longlong)result; + MYSQL_TIME ltime; + if (get_date(<ime, TIME_FUZZY_DATE)) + return 0; + + return TIME_to_ulonglong(<ime); } for (uint i=0; i < arg_count ; i++) { @@ -2579,10 +2594,11 @@ my_decimal *Item_func_min_max::val_decimal(my_decimal *dec) if (compare_as_dates) { - ulonglong value= 0; - (void)cmp_datetimes(&value); - ulonglong2decimal(value, dec); - return dec; + MYSQL_TIME ltime; + if (get_date(<ime, TIME_FUZZY_DATE)) + return 0; + + return date2my_decimal(<ime, dec); } for (uint i=0; i < arg_count ; i++) { @@ -3065,8 +3081,7 @@ udf_handler::fix_fields(THD *thd, Item_result_field *func, if (arguments[i]->const_item()) { - switch (arguments[i]->result_type()) - { + switch (arguments[i]->result_type()) { case STRING_RESULT: case DECIMAL_RESULT: { @@ -3092,9 +3107,9 @@ udf_handler::fix_fields(THD *thd, Item_result_field *func, to+= ALIGN_SIZE(sizeof(double)); break; case ROW_RESULT: - default: - // This case should never be chosen - DBUG_ASSERT(0); + case TIME_RESULT: + case IMPOSSIBLE_RESULT: + DBUG_ASSERT(0); // This case should never be chosen break; } } @@ -3167,9 +3182,9 @@ bool udf_handler::get_arguments() } break; case ROW_RESULT: - default: - // This case should never be chosen - DBUG_ASSERT(0); + case TIME_RESULT: + case IMPOSSIBLE_RESULT: + DBUG_ASSERT(0); // This case should never be chosen break; } } @@ -3789,9 +3804,9 @@ longlong Item_func_benchmark::val_int() (void) args[1]->val_decimal(&tmp_decimal); break; case ROW_RESULT: - default: - // This case should never be chosen - DBUG_ASSERT(0); + case TIME_RESULT: + case IMPOSSIBLE_RESULT: + DBUG_ASSERT(0); // This case should never be chosen return 0; } } @@ -4145,6 +4160,7 @@ double user_var_entry::val_real(bool *null_value) case STRING_RESULT: return my_atof(value); // This is null terminated case ROW_RESULT: + case TIME_RESULT: case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); // Impossible break; @@ -4177,6 +4193,7 @@ longlong user_var_entry::val_int(bool *null_value) const return my_strtoll10(value, (char**) 0, &error);// String is null terminated } case ROW_RESULT: + case TIME_RESULT: case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); // Impossible break; @@ -4211,6 +4228,7 @@ String *user_var_entry::val_str(bool *null_value, String *str, str= 0; // EOM error break; case ROW_RESULT: + case TIME_RESULT: case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); // Impossible break; @@ -4239,6 +4257,7 @@ my_decimal *user_var_entry::val_decimal(bool *null_value, my_decimal *val) str2my_decimal(E_DEC_FATAL_ERROR, value, length, collation.collation, val); break; case ROW_RESULT: + case TIME_RESULT: case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); // Impossible break; @@ -4278,8 +4297,9 @@ Item_func_set_user_var::check(bool use_result_field) { save_result.vint= use_result_field ? result_field->val_int() : args[0]->val_int(); - unsigned_flag= use_result_field ? ((Field_num*)result_field)->unsigned_flag: - args[0]->unsigned_flag; + unsigned_flag= (use_result_field ? + ((Field_num*)result_field)->unsigned_flag: + args[0]->unsigned_flag); break; } case STRING_RESULT: @@ -4296,9 +4316,9 @@ Item_func_set_user_var::check(bool use_result_field) break; } case ROW_RESULT: - default: - // This case should never be chosen - DBUG_ASSERT(0); + case TIME_RESULT: + case IMPOSSIBLE_RESULT: + DBUG_ASSERT(0); // This case should never be chosen break; } DBUG_RETURN(FALSE); @@ -4331,9 +4351,9 @@ void Item_func_set_user_var::save_item_result(Item *item) save_result.vdec= item->val_decimal_result(&decimal_buff); break; case ROW_RESULT: - default: - // Should never happen - DBUG_ASSERT(0); + case TIME_RESULT: + case IMPOSSIBLE_RESULT: + DBUG_ASSERT(0); // This case should never be chosen break; } DBUG_VOID_RETURN; @@ -4399,9 +4419,9 @@ Item_func_set_user_var::update() break; } case ROW_RESULT: - default: - // This case should never be chosen - DBUG_ASSERT(0); + case TIME_RESULT: + case IMPOSSIBLE_RESULT: + DBUG_ASSERT(0); // This case should never be chosen break; } DBUG_RETURN(res); @@ -4834,7 +4854,7 @@ void Item_func_get_user_var::fix_length_and_dec() max_length= var_entry->length; collation.set(var_entry->collation); - switch(m_cached_result_type) { + switch (m_cached_result_type) { case REAL_RESULT: max_length= DBL_DIG + 8; break; @@ -4850,8 +4870,9 @@ void Item_func_get_user_var::fix_length_and_dec() decimals= DECIMAL_MAX_SCALE; break; case ROW_RESULT: // Keep compiler happy - default: - DBUG_ASSERT(0); + case TIME_RESULT: + case IMPOSSIBLE_RESULT: + DBUG_ASSERT(0); // This case should never be chosen break; } } diff --git a/sql/item_func.h b/sql/item_func.h index 90dbffb0a05..09b5ef9c0ec 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -323,6 +323,8 @@ class Item_func_numhybrid: public Item_func protected: Item_result hybrid_type; public: + Item_func_numhybrid() :Item_func(), hybrid_type(REAL_RESULT) + {} Item_func_numhybrid(Item *a) :Item_func(a), hybrid_type(REAL_RESULT) {} Item_func_numhybrid(Item *a,Item *b) @@ -868,25 +870,21 @@ class Item_func_min_max :public Item_func Item_result cmp_type; String tmp_value; int cmp_sign; - /* TRUE <=> arguments should be compared in the DATETIME context. */ - bool compare_as_dates; /* An item used for issuing warnings while string to DATETIME conversion. */ - Item *datetime_item; + Item *compare_as_dates; THD *thd; protected: enum_field_types cached_field_type; public: Item_func_min_max(List<Item> &list,int cmp_sign_arg) :Item_func(list), - cmp_type(INT_RESULT), cmp_sign(cmp_sign_arg), compare_as_dates(FALSE), - datetime_item(0) {} + cmp_type(INT_RESULT), cmp_sign(cmp_sign_arg), compare_as_dates(FALSE) {} double val_real(); longlong val_int(); String *val_str(String *); my_decimal *val_decimal(my_decimal *); + bool get_date(MYSQL_TIME *res, uint fuzzy_date); void fix_length_and_dec(); enum Item_result result_type () const { return cmp_type; } - bool result_as_longlong() { return compare_as_dates; }; - uint cmp_datetimes(ulonglong *value); enum_field_types field_type() const { return cached_field_type; } }; diff --git a/sql/item_row.h b/sql/item_row.h index c7fb8d59220..0b43309391d 100644 --- a/sql/item_row.h +++ b/sql/item_row.h @@ -66,6 +66,7 @@ public: table_map used_tables() const { return used_tables_cache; }; bool const_item() const { return const_item_cache; }; enum Item_result result_type() const { return ROW_RESULT; } + Item_result cmp_type() const { return ROW_RESULT; } void update_used_tables(); table_map not_null_tables() const { return not_null_tables_cache; } virtual void print(String *str, enum_query_type query_type); diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index e7e77fb58b6..69eea97bd09 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -3944,8 +3944,14 @@ String *Item_dyncol_get::val_str(String *str_result) case DYN_COL_TIME: { int length; + /* + We use AUTO_SEC_PART_DIGITS here to ensure that we do not loose + any microseconds from the data. This is safe to do as we are + asked to return the time argument as a string. + */ if (str_result->alloc(MAX_DATE_STRING_REP_LENGTH) || - !(length= my_TIME_to_str(&val.time_value, (char*) str_result->ptr()))) + !(length= my_TIME_to_str(&val.time_value, (char*) str_result->ptr(), + AUTO_SEC_PART_DIGITS))) goto null; str_result->set_charset(&my_charset_latin1); str_result->length(length); @@ -4086,7 +4092,7 @@ double Item_dyncol_get::val_real() case DYN_COL_DATE: case DYN_COL_TIME: return (ulonglong2double(TIME_to_ulonglong(&val.time_value)) + - val.time_value.second_part / (double) TIME_SUBSECOND_RANGE); + val.time_value.second_part / (double) TIME_SECOND_PART_FACTOR); } null: @@ -4145,10 +4151,12 @@ my_decimal *Item_dyncol_get::val_decimal(my_decimal *decimal_value) case DYN_COL_DATE: case DYN_COL_TIME: { - double tmp= (ulonglong2double(TIME_to_ulonglong(&val.time_value)) + - val.time_value.second_part / (double) TIME_SUBSECOND_RANGE); - /* This can't overflow as time is always in the range of decimal */ - double2my_decimal(E_DEC_FATAL_ERROR, tmp, decimal_value); + my_decimal time_part, sub_second, fractions; + longlong2decimal(TIME_to_ulonglong(&val.time_value), &time_part); + longlong2decimal(val.time_value.second_part, &sub_second); + my_decimal_div(E_DEC_FATAL_ERROR, &fractions, &sub_second, + &time_second_part_factor, TIME_SECOND_PART_DIGITS); + my_decimal_add(E_DEC_FATAL_ERROR, decimal_value, &time_part, &fractions); break; } } diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 2479f8ea92a..123289f52e7 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -652,17 +652,30 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref) Setup cache/comparator of MIN/MAX functions. When called by the copy_or_same function value_arg parameter contains calculated value of the original MIN/MAX object and it is saved in this object's cache. + + We mark the value and arg_cache with 'RAND_TABLE_BIT' to ensure + that Arg_comparator::compare_datetime() doesn't allocate new + item inside of Arg_comparator. This would cause compare_datetime() + and Item_sum_min::add() to use different values! */ void Item_sum_hybrid::setup_hybrid(Item *item, Item *value_arg) { - value= Item_cache::get_cache(item); + if (!(value= Item_cache::get_cache(item))) + return; value->setup(item); value->store(value_arg); - arg_cache= Item_cache::get_cache(item); + /* Don't cache value, as it will change */ + if (!item->const_item()) + value->set_used_tables(RAND_TABLE_BIT); + if (!(arg_cache= Item_cache::get_cache(item))) + return; arg_cache->setup(item); - cmp= new Arg_comparator(); - cmp->set_cmp_func(this, (Item**)&arg_cache, (Item**)&value, FALSE); + /* Don't cache value, as it will change */ + if (!item->const_item()) + arg_cache->set_used_tables(RAND_TABLE_BIT); + if (cmp= new Arg_comparator()) + cmp->set_cmp_func(this, (Item**)&arg_cache, (Item**)&value, FALSE); collation.set(item->collation); } @@ -687,14 +700,17 @@ Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table, */ switch (args[0]->field_type()) { case MYSQL_TYPE_DATE: - field= new Field_newdate(maybe_null, name, collation.collation); + field= new Field_newdate(0, maybe_null ? (uchar*)"" : 0, 0, Field::NONE, + name, collation.collation); break; case MYSQL_TYPE_TIME: - field= new Field_time(maybe_null, name, collation.collation); + field= new_Field_time(0, maybe_null ? (uchar*)"" : 0, 0, Field::NONE, + name, decimals, collation.collation); break; case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_DATETIME: - field= new Field_datetime(maybe_null, name, collation.collation); + field= new_Field_datetime(0, maybe_null ? (uchar*)"" : 0, 0, Field::NONE, + name, decimals, collation.collation); break; default: return Item_sum::create_tmp_field(group, table, convert_blob_length); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 76a3fed3d49..e4a2595efed 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -35,128 +35,12 @@ /** Day number for Dec 31st, 9999. */ #define MAX_DAY_NUMBER 3652424L -/** - @todo - OPTIMIZATION - - Replace the switch with a function that should be called for each - date type. - - Remove sprintf and opencode the conversion, like we do in - Field_datetime. - - The reason for this functions existence is that as we don't have a - way to know if a datetime/time value has microseconds in them - we are now only adding microseconds to the output if the - value has microseconds. - - We can't use a standard make_date_time() for this as we don't know - if someone will use %f in the format specifier in which case we would get - the microseconds twice. -*/ - -static bool make_datetime(date_time_format_types format, MYSQL_TIME *ltime, - String *str) +static bool make_datetime(MYSQL_TIME *ltime, String *str, uint decimals) { - char *buff; - CHARSET_INFO *cs= &my_charset_bin; - uint length= MAX_DATE_STRING_REP_LENGTH; - - if (str->alloc(length)) - return 1; - buff= (char*) str->ptr(); - - switch (format) { - case TIME_ONLY: - length= cs->cset->snprintf(cs, buff, length, "%s%02d:%02d:%02d", - ltime->neg ? "-" : "", - ltime->hour, ltime->minute, ltime->second); - break; - case TIME_MICROSECOND: - length= cs->cset->snprintf(cs, buff, length, "%s%02d:%02d:%02d.%06ld", - ltime->neg ? "-" : "", - ltime->hour, ltime->minute, ltime->second, - ltime->second_part); - break; - case DATE_ONLY: - length= cs->cset->snprintf(cs, buff, length, "%04d-%02d-%02d", - ltime->year, ltime->month, ltime->day); - break; - case DATE_TIME: - length= cs->cset->snprintf(cs, buff, length, - "%04d-%02d-%02d %02d:%02d:%02d", - ltime->year, ltime->month, ltime->day, - ltime->hour, ltime->minute, ltime->second); - break; - case DATE_TIME_MICROSECOND: - length= cs->cset->snprintf(cs, buff, length, - "%04d-%02d-%02d %02d:%02d:%02d.%06ld", - ltime->year, ltime->month, ltime->day, - ltime->hour, ltime->minute, ltime->second, - ltime->second_part); - break; - } - - str->length(length); - str->set_charset(cs); - return 0; -} - - -/* - Wrapper over make_datetime() with validation of the input MYSQL_TIME value - - NOTE - see make_datetime() for more information - - RETURN - 1 if there was an error during converion - 0 otherwise -*/ - -static bool make_datetime_with_warn(date_time_format_types format, MYSQL_TIME *ltime, - String *str) -{ - int warning= 0; - - if (make_datetime(format, ltime, str)) - return 1; - if (check_time_range(ltime, &warning)) - return 1; - if (!warning) - return 0; - - make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, - str->ptr(), str->length(), - MYSQL_TIMESTAMP_TIME, NullS); - return make_datetime(format, ltime, str); -} - - -/* - Wrapper over make_time() with validation of the input MYSQL_TIME value - - NOTE - see make_time() for more info - - RETURN - 1 if there was an error during conversion - 0 otherwise -*/ - -static bool make_time_with_warn(const DATE_TIME_FORMAT *format, - MYSQL_TIME *l_time, String *str) -{ - int warning= 0; - make_time(format, l_time, str); - if (check_time_range(l_time, &warning)) + if (str->alloc(MAX_DATE_STRING_REP_LENGTH)) return 1; - if (warning) - { - make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, - str->ptr(), str->length(), - MYSQL_TIMESTAMP_TIME, NullS); - make_time(format, l_time, str); - } - + str->length(my_TIME_to_str(ltime, const_cast<char*>(str->ptr()), decimals)); + str->set_charset(&my_charset_bin); return 0; } @@ -167,7 +51,6 @@ static bool make_time_with_warn(const DATE_TIME_FORMAT *format, SYNOPSIS: sec_to_time() seconds number of seconds - unsigned_flag 1, if 'seconds' is unsigned, 0, otherwise ltime output MYSQL_TIME value DESCRIPTION @@ -181,43 +64,53 @@ static bool make_time_with_warn(const DATE_TIME_FORMAT *format, 0 otherwise */ -static bool sec_to_time(longlong seconds, bool unsigned_flag, MYSQL_TIME *ltime) +bool Item_func_sec_to_time::sec_to_time(my_decimal *seconds, MYSQL_TIME *ltime) { + Lazy_string_decimal str(*seconds); uint sec; + longlong full_seconds; + my_decimal tmp, sub_seconds; bzero((char *)ltime, sizeof(*ltime)); + + ltime->time_type= MYSQL_TIMESTAMP_TIME; - if (seconds < 0) + if (seconds->sign()) { - if (unsigned_flag) - goto overflow; ltime->neg= 1; - if (seconds < -3020399) - goto overflow; - seconds= -seconds; + seconds->sign(0); } - else if (seconds > 3020399) + if (my_decimal_cmp(seconds, &max_seconds_for_time_type) > 0) goto overflow; + + /* We don't call my_decimal2int() here as we don't want rounding */ + (void) decimal2longlong(seconds, &full_seconds); - sec= (uint) ((ulonglong) seconds % 3600); - ltime->hour= (uint) (seconds/3600); + sec= (uint) (full_seconds % 3600); + ltime->hour= (uint) (full_seconds/3600); ltime->minute= sec/60; ltime->second= sec % 60; + /* + Calculate second_part + ltime->second_part= (seconds - floor(seconds)) * TIME_SECOND_PART_FACTOR + */ + my_decimal_floor(E_DEC_FATAL_ERROR, seconds, &tmp); + my_decimal_sub(E_DEC_FATAL_ERROR, &sub_seconds, seconds, &tmp); + my_decimal_mul(E_DEC_FATAL_ERROR, &tmp, &sub_seconds, + &time_second_part_factor); + (void) decimal2longlong(&tmp, &full_seconds); + ltime->second_part= full_seconds; + return 0; overflow: - ltime->hour= TIME_MAX_HOUR; - ltime->minute= TIME_MAX_MINUTE; - ltime->second= TIME_MAX_SECOND; - - char buf[22]; - int len= (int)(longlong10_to_str(seconds, buf, unsigned_flag ? 10 : -10) - - buf); + /* use check_time_range() to set ltime to the max value depending on dec */ + int unused; + ltime->hour= TIME_MAX_HOUR+1; + check_time_range(ltime, decimals, &unused); make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, - buf, len, MYSQL_TIMESTAMP_TIME, - NullS); - + &str, MYSQL_TIMESTAMP_TIME, NullS); return 1; } @@ -270,7 +163,8 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, const char *val, uint length, MYSQL_TIME *l_time, timestamp_type cached_timestamp_type, const char **sub_pattern_end, - const char *date_time_type) + const char *date_time_type, + uint fuzzy_date) { int weekday= 0, yearday= 0, daypart= 0; int week_number= -1; @@ -291,6 +185,8 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, if (!sub_pattern_end) bzero((char*) l_time, sizeof(*l_time)); + l_time->time_type= cached_timestamp_type; + for (; ptr != end && val != val_end; ptr++) { /* Skip pre-space between each argument */ @@ -464,7 +360,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, */ if (extract_date_time(&time_ampm_format, val, (uint)(val_end - val), l_time, - cached_timestamp_type, &val, "time")) + cached_timestamp_type, &val, "time", fuzzy_date)) DBUG_RETURN(1); break; @@ -472,7 +368,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, case 'T': if (extract_date_time(&time_24hrs_format, val, (uint)(val_end - val), l_time, - cached_timestamp_type, &val, "time")) + cached_timestamp_type, &val, "time", fuzzy_date)) DBUG_RETURN(1); break; @@ -579,6 +475,10 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, l_time->minute > 59 || l_time->second > 59) goto err; + if ((fuzzy_date & TIME_NO_ZERO_DATE) && + (l_time->year == 0 || l_time->month == 0 || l_time->day == 0)) + goto err; + if (val != val_end) { do @@ -1025,7 +925,7 @@ longlong Item_func_dayofyear::val_int() { DBUG_ASSERT(fixed == 1); MYSQL_TIME ltime; - if (get_arg0_date(<ime,TIME_NO_ZERO_DATE)) + if (get_arg0_date(<ime, TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE)) return 0; return (longlong) calc_daynr(ltime.year,ltime.month,ltime.day) - calc_daynr(ltime.year,1,1) + 1; @@ -1294,21 +1194,23 @@ longlong Item_func_year::val_int_endpoint(bool left_endp, bool *incl_endp) } -longlong Item_func_unix_timestamp::val_int() +bool Item_func_unix_timestamp::get_timestamp_value(my_time_t *seconds, + ulong *second_part) { - MYSQL_TIME ltime; - my_bool not_used; - DBUG_ASSERT(fixed == 1); - if (arg_count == 0) - return (longlong) current_thd->query_start(); if (args[0]->type() == FIELD_ITEM) { // Optimize timestamp field Field *field=((Item_field*) args[0])->field; if (field->type() == MYSQL_TYPE_TIMESTAMP) - return ((Field_timestamp*) field)->get_timestamp(&null_value); + { + if ((null_value= field->is_null())) + return 1; + *seconds= ((Field_timestamp*)field)->get_timestamp(second_part); + return 0; + } } - + + MYSQL_TIME ltime; if (get_arg0_date(<ime, 0)) { /* @@ -1317,14 +1219,42 @@ longlong Item_func_unix_timestamp::val_int() this case). */ null_value= args[0]->null_value; - return 0; + return 1; } + + uint error_code; + *seconds= TIME_to_timestamp(current_thd, <ime, &error_code); + *second_part= ltime.second_part; + return (null_value= (error_code == ER_WARN_DATA_OUT_OF_RANGE)); +} + + +longlong Item_func_unix_timestamp::int_op() +{ + if (arg_count == 0) + return (longlong) current_thd->query_start(); - return (longlong) TIME_to_timestamp(current_thd, <ime, ¬_used); + ulong second_part; + my_time_t seconds; + if (get_timestamp_value(&seconds, &second_part)) + return 0; + + return seconds; } -longlong Item_func_time_to_sec::val_int() +double Item_func_unix_timestamp::real_op() +{ + ulong second_part; + my_time_t seconds; + if (get_timestamp_value(&seconds, &second_part)) + return 0; + + return seconds + second_part/(double)TIME_SECOND_PART_FACTOR; +} + + +longlong Item_func_time_to_sec::int_op() { DBUG_ASSERT(fixed == 1); MYSQL_TIME ltime; @@ -1335,6 +1265,17 @@ longlong Item_func_time_to_sec::val_int() } +double Item_func_time_to_sec::real_op() +{ + DBUG_ASSERT(fixed == 1); + MYSQL_TIME ltime; + double seconds; + (void) get_arg0_time(<ime); + seconds=ltime.hour*3600L+ltime.minute*60+ltime.second+ltime.second_part/1e6; + return ltime.neg ? -seconds : seconds; +} + + /** Convert a string to a interval value. @@ -1493,32 +1434,62 @@ bool get_interval_value(Item *args,interval_type int_type, } -String *Item_date::val_str(String *str) +String *Item_temporal_func::val_str(String *str) { DBUG_ASSERT(fixed == 1); MYSQL_TIME ltime; if (get_date(<ime, TIME_FUZZY_DATE)) return (String *) 0; - if (str->alloc(MAX_DATE_STRING_REP_LENGTH)) + if (make_datetime(<ime, str, decimals)) { null_value= 1; return (String *) 0; } - make_date((DATE_TIME_FORMAT *) 0, <ime, str); return str; } -longlong Item_date::val_int() +longlong Item_temporal_func::val_int() { DBUG_ASSERT(fixed == 1); MYSQL_TIME ltime; if (get_date(<ime, TIME_FUZZY_DATE)) return 0; - return (longlong) (ltime.year*10000L+ltime.month*100+ltime.day); + return (longlong)TIME_to_ulonglong(<ime); +} + + +double Item_temporal_func::val_real() +{ + DBUG_ASSERT(fixed == 1); + MYSQL_TIME ltime; + if (get_date(<ime, TIME_FUZZY_DATE)) + return 0; + return TIME_to_double(<ime); +} + + +bool Item_temporal_func::get_time(MYSQL_TIME *ltime) +{ + /* + All temporal functions have a get_date() function which also + returns the time. + */ + if (get_date(ltime, TIME_TIME_ONLY | TIME_FUZZY_DATE | + sql_mode_for_dates())) + return 1; + /* Convert date to time */ + if (ltime->time_type == MYSQL_TIMESTAMP_DATE || + ltime->time_type == MYSQL_TIMESTAMP_DATETIME) + { + ltime->year= ltime->month= ltime->day= 0; + ltime->time_type= MYSQL_TIMESTAMP_TIME; + } + return 0; } + bool Item_func_from_days::get_date(MYSQL_TIME *ltime, uint fuzzy_date) { longlong value=args[0]->val_int(); @@ -1533,28 +1504,13 @@ bool Item_func_from_days::get_date(MYSQL_TIME *ltime, uint fuzzy_date) void Item_func_curdate::fix_length_and_dec() { - collation.set(&my_charset_bin); - decimals=0; - max_length=MAX_DATE_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; - store_now_in_TIME(<ime); /* We don't need to set second_part and neg because they already 0 */ ltime.hour= ltime.minute= ltime.second= 0; ltime.time_type= MYSQL_TIMESTAMP_DATE; - value= (longlong) TIME_to_ulonglong_date(<ime); -} - -String *Item_func_curdate::val_str(String *str) -{ - DBUG_ASSERT(fixed == 1); - if (str->alloc(MAX_DATE_STRING_REP_LENGTH)) - { - null_value= 1; - return (String *) 0; - } - make_date((DATE_TIME_FORMAT *) 0, <ime, str); - return str; + Item_datefunc::fix_length_and_dec(); + maybe_null= 0; } /** @@ -1564,8 +1520,7 @@ String *Item_func_curdate::val_str(String *str) void Item_func_curdate_local::store_now_in_TIME(MYSQL_TIME *now_time) { THD *thd= current_thd; - thd->variables.time_zone->gmt_sec_to_TIME(now_time, - (my_time_t)thd->query_start()); + thd->variables.time_zone->gmt_sec_to_TIME(now_time, thd->query_start()); thd->time_zone_used= 1; } @@ -1576,8 +1531,8 @@ void Item_func_curdate_local::store_now_in_TIME(MYSQL_TIME *now_time) */ void Item_func_curdate_utc::store_now_in_TIME(MYSQL_TIME *now_time) { - my_tz_UTC->gmt_sec_to_TIME(now_time, - (my_time_t)(current_thd->query_start())); + THD *thd= current_thd; + my_tz_UTC->gmt_sec_to_TIME(now_time, thd->query_start()); /* We are not flagging this query as using time zone, since it uses fixed UTC-SYSTEM time-zone. @@ -1593,26 +1548,35 @@ bool Item_func_curdate::get_date(MYSQL_TIME *res, } -String *Item_func_curtime::val_str(String *str) +bool Item_func_curtime::fix_fields(THD *thd, Item **items) { - DBUG_ASSERT(fixed == 1); - str_value.set(buff, buff_length, &my_charset_bin); - return &str_value; + if (decimals > TIME_SECOND_PART_DIGITS) + { + my_error(ER_TOO_BIG_PRECISION, MYF(0), decimals, func_name(), + TIME_SECOND_PART_DIGITS); + return 1; + } + return Item_timefunc::fix_fields(thd, items); } - -void Item_func_curtime::fix_length_and_dec() +bool Item_func_curtime::get_date(MYSQL_TIME *res, + uint fuzzy_date __attribute__((unused))) { - MYSQL_TIME ltime; - - decimals= DATETIME_DEC; - collation.set(&my_charset_bin); - store_now_in_TIME(<ime); - value= TIME_to_ulonglong_time(<ime); - buff_length= (uint) my_time_to_str(<ime, buff); - max_length= buff_length; + *res= ltime; + return 0; } +static void set_sec_part(ulong sec_part, MYSQL_TIME *ltime, Item *item) +{ + DBUG_ASSERT(item->decimals == AUTO_SEC_PART_DIGITS || + item->decimals <= TIME_SECOND_PART_DIGITS); + if (item->decimals) + { + ltime->second_part= sec_part; + if (item->decimals < TIME_SECOND_PART_DIGITS) + ltime->second_part= sec_part_truncate(ltime->second_part, item->decimals); + } +} /** Converts current time in my_time_t to MYSQL_TIME represenatation for local @@ -1621,8 +1585,10 @@ void Item_func_curtime::fix_length_and_dec() void Item_func_curtime_local::store_now_in_TIME(MYSQL_TIME *now_time) { THD *thd= current_thd; - thd->variables.time_zone->gmt_sec_to_TIME(now_time, - (my_time_t)thd->query_start()); + thd->variables.time_zone->gmt_sec_to_TIME(now_time, thd->query_start()); + now_time->year= now_time->month= now_time->day= 0; + now_time->time_type= MYSQL_TIMESTAMP_TIME; + set_sec_part(thd->query_start_sec_part(), now_time, this); thd->time_zone_used= 1; } @@ -1633,36 +1599,28 @@ void Item_func_curtime_local::store_now_in_TIME(MYSQL_TIME *now_time) */ void Item_func_curtime_utc::store_now_in_TIME(MYSQL_TIME *now_time) { - my_tz_UTC->gmt_sec_to_TIME(now_time, - (my_time_t)(current_thd->query_start())); + THD *thd= current_thd; + my_tz_UTC->gmt_sec_to_TIME(now_time, thd->query_start()); + now_time->year= now_time->month= now_time->day= 0; + now_time->time_type= MYSQL_TIMESTAMP_TIME; + set_sec_part(thd->query_start_sec_part(), now_time, this); /* We are not flagging this query as using time zone, since it uses fixed UTC-SYSTEM time-zone. */ } - -String *Item_func_now::val_str(String *str) +bool Item_func_now::fix_fields(THD *thd, Item **items) { - DBUG_ASSERT(fixed == 1); - str_value.set(buff,buff_length, &my_charset_bin); - return &str_value; -} - - -void Item_func_now::fix_length_and_dec() -{ - decimals= DATETIME_DEC; - collation.set(&my_charset_bin); - - store_now_in_TIME(<ime); - value= (longlong) TIME_to_ulonglong_datetime(<ime); - - buff_length= (uint) my_datetime_to_str(<ime, buff); - max_length= buff_length; + if (decimals > TIME_SECOND_PART_DIGITS) + { + my_error(ER_TOO_BIG_PRECISION, MYF(0), decimals, func_name(), + TIME_SECOND_PART_DIGITS); + return 1; + } + return Item_temporal_func::fix_fields(thd, items); } - /** Converts current time in my_time_t to MYSQL_TIME represenatation for local time zone. Defines time zone (local) used for whole NOW function. @@ -1670,8 +1628,8 @@ void Item_func_now::fix_length_and_dec() void Item_func_now_local::store_now_in_TIME(MYSQL_TIME *now_time) { THD *thd= current_thd; - thd->variables.time_zone->gmt_sec_to_TIME(now_time, - (my_time_t)thd->query_start()); + thd->variables.time_zone->gmt_sec_to_TIME(now_time, thd->query_start()); + set_sec_part(thd->query_start_sec_part(), now_time, this); thd->time_zone_used= 1; } @@ -1682,8 +1640,9 @@ void Item_func_now_local::store_now_in_TIME(MYSQL_TIME *now_time) */ void Item_func_now_utc::store_now_in_TIME(MYSQL_TIME *now_time) { - my_tz_UTC->gmt_sec_to_TIME(now_time, - (my_time_t)(current_thd->query_start())); + THD *thd= current_thd; + my_tz_UTC->gmt_sec_to_TIME(now_time, thd->query_start()); + set_sec_part(thd->query_start_sec_part(), now_time, this); /* We are not flagging this query as using time zone, since it uses fixed UTC-SYSTEM time-zone. @@ -1699,13 +1658,6 @@ bool Item_func_now::get_date(MYSQL_TIME *res, } -int Item_func_now::save_in_field(Field *to, bool no_conversions) -{ - to->set_notnull(); - return to->store_time(<ime, MYSQL_TIMESTAMP_DATETIME); -} - - /** Converts current time in my_time_t to MYSQL_TIME represenatation for local time zone. Defines time zone (local) used for whole SYSDATE function. @@ -1713,99 +1665,34 @@ int Item_func_now::save_in_field(Field *to, bool no_conversions) void Item_func_sysdate_local::store_now_in_TIME(MYSQL_TIME *now_time) { THD *thd= current_thd; - thd->variables.time_zone->gmt_sec_to_TIME(now_time, (my_time_t) my_time(0)); + my_hrtime_t now= my_hrtime(); + thd->variables.time_zone->gmt_sec_to_TIME(now_time, hrtime_to_my_time(now)); + set_sec_part(hrtime_sec_part(now), now_time, this); thd->time_zone_used= 1; } -String *Item_func_sysdate_local::val_str(String *str) -{ - DBUG_ASSERT(fixed == 1); - store_now_in_TIME(<ime); - buff_length= (uint) my_datetime_to_str(<ime, buff); - str_value.set(buff, buff_length, &my_charset_bin); - return &str_value; -} - - -longlong Item_func_sysdate_local::val_int() -{ - DBUG_ASSERT(fixed == 1); - store_now_in_TIME(<ime); - return (longlong) TIME_to_ulonglong_datetime(<ime); -} - - -double Item_func_sysdate_local::val_real() -{ - DBUG_ASSERT(fixed == 1); - store_now_in_TIME(<ime); - return ulonglong2double(TIME_to_ulonglong_datetime(<ime)); -} - - -void Item_func_sysdate_local::fix_length_and_dec() -{ - decimals= 0; - collation.set(&my_charset_bin); - max_length= MAX_DATETIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; -} - - bool Item_func_sysdate_local::get_date(MYSQL_TIME *res, uint fuzzy_date __attribute__((unused))) { - store_now_in_TIME(<ime); - *res= ltime; - return 0; -} - - -int Item_func_sysdate_local::save_in_field(Field *to, bool no_conversions) -{ - store_now_in_TIME(<ime); - to->set_notnull(); - to->store_time(<ime, MYSQL_TIMESTAMP_DATETIME); + store_now_in_TIME(res); return 0; } -String *Item_func_sec_to_time::val_str(String *str) +bool Item_func_sec_to_time::get_date(MYSQL_TIME *ltime, uint fuzzy_date) { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - longlong arg_val= args[0]->val_int(); - - if ((null_value=args[0]->null_value) || - str->alloc(MAX_DATE_STRING_REP_LENGTH)) - { - null_value= 1; - return (String*) 0; - } - - sec_to_time(arg_val, args[0]->unsigned_flag, <ime); + my_decimal tmp_buff, *tmp= args[0]->val_decimal(&tmp_buff); - make_time((DATE_TIME_FORMAT *) 0, <ime, str); - return str; -} - + if ((null_value= args[0]->null_value)) + return 1; -longlong Item_func_sec_to_time::val_int() -{ - DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - longlong arg_val= args[0]->val_int(); + sec_to_time(tmp, ltime); - if ((null_value=args[0]->null_value)) - return 0; - - sec_to_time(arg_val, args[0]->unsigned_flag, <ime); - - return (ltime.neg ? -1 : 1) * - ((ltime.hour)*10000 + ltime.minute*100 + ltime.second); + return 0; } - void Item_func_date_format::fix_length_and_dec() { THD* thd= current_thd; @@ -1941,27 +1828,12 @@ String *Item_func_date_format::val_str(String *str) String *format; MYSQL_TIME l_time; uint size; + int is_time_flag = is_time_format ? TIME_TIME_ONLY : 0; DBUG_ASSERT(fixed == 1); - - if (!is_time_format) - { - if (get_arg0_date(&l_time, TIME_FUZZY_DATE)) - return 0; - } - else - { - String *res; - if (!(res=args[0]->val_str(str)) || - (str_to_time_with_warn(res->ptr(), res->length(), &l_time, - current_thd->variables.sql_mode & - (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | - MODE_INVALID_DATES)))) - goto null_date; - - l_time.year=l_time.month=l_time.day=0; - null_value=0; - } - + + if (get_arg0_date(&l_time, TIME_FUZZY_DATE | is_time_flag)) + return 0; + if (!(format = args[1]->val_str(str)) || !format->length()) goto null_date; @@ -1999,100 +1871,31 @@ null_date: void Item_func_from_unixtime::fix_length_and_dec() { thd= current_thd; - collation.set(&my_charset_bin); - decimals= DATETIME_DEC; - max_length=MAX_DATETIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; - maybe_null= 1; thd->time_zone_used= 1; + decimals= args[0]->decimals; + Item_temporal_func::fix_length_and_dec(); } -String *Item_func_from_unixtime::val_str(String *str) -{ - MYSQL_TIME time_tmp; - - DBUG_ASSERT(fixed == 1); - - if (get_date(&time_tmp, 0)) - return 0; - - if (str->alloc(MAX_DATE_STRING_REP_LENGTH)) - { - null_value= 1; - return 0; - } - - make_datetime((DATE_TIME_FORMAT *) 0, &time_tmp, str); - - return str; -} - - -longlong Item_func_from_unixtime::val_int() -{ - MYSQL_TIME time_tmp; - - DBUG_ASSERT(fixed == 1); - - if (get_date(&time_tmp, 0)) - return 0; - - return (longlong) TIME_to_ulonglong_datetime(&time_tmp); -} - bool Item_func_from_unixtime::get_date(MYSQL_TIME *ltime, uint fuzzy_date __attribute__((unused))) { - ulonglong tmp= (ulonglong)(args[0]->val_int()); - /* - "tmp > TIMESTAMP_MAX_VALUE" check also covers case of negative - from_unixtime() argument since tmp is unsigned. - */ - if ((null_value= (args[0]->null_value || tmp > TIMESTAMP_MAX_VALUE))) - return 1; + double tmp= args[0]->val_real(); + if (args[0]->null_value || tmp < 0 || tmp > TIMESTAMP_MAX_VALUE) + return (null_value= 1); thd->variables.time_zone->gmt_sec_to_TIME(ltime, (my_time_t)tmp); - return 0; + ltime->second_part= (ulong)((tmp - floor(tmp))*TIME_SECOND_PART_FACTOR); + + return (null_value= 0); } void Item_func_convert_tz::fix_length_and_dec() { - collation.set(&my_charset_bin); - decimals= 0; - max_length= MAX_DATETIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; - maybe_null= 1; -} - - -String *Item_func_convert_tz::val_str(String *str) -{ - MYSQL_TIME time_tmp; - - if (get_date(&time_tmp, 0)) - return 0; - - if (str->alloc(MAX_DATE_STRING_REP_LENGTH)) - { - null_value= 1; - return 0; - } - - make_datetime((DATE_TIME_FORMAT *) 0, &time_tmp, str); - - return str; -} - - -longlong Item_func_convert_tz::val_int() -{ - MYSQL_TIME time_tmp; - - if (get_date(&time_tmp, 0)) - return 0; - - return (longlong)TIME_to_ulonglong_datetime(&time_tmp); + decimals= args[0]->decimals; + Item_temporal_func::fix_length_and_dec(); } @@ -2115,7 +1918,8 @@ bool Item_func_convert_tz::get_date(MYSQL_TIME *ltime, to_tz_cached= args[2]->const_item(); } - if (from_tz==0 || to_tz==0 || get_arg0_date(ltime, TIME_NO_ZERO_DATE)) + if (from_tz==0 || to_tz==0 || + get_arg0_date(ltime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) { null_value= 1; return 1; @@ -2124,9 +1928,12 @@ bool Item_func_convert_tz::get_date(MYSQL_TIME *ltime, { my_bool not_used; my_time_tmp= from_tz->TIME_to_gmt_sec(ltime, ¬_used); + ulong sec_part= ltime->second_part; /* my_time_tmp is guranteed to be in the allowed range */ if (my_time_tmp) to_tz->gmt_sec_to_TIME(ltime, my_time_tmp); + /* we rely on the fact that no timezone conversion can change sec_part */ + ltime->second_part= sec_part; } null_value= 0; @@ -2137,7 +1944,7 @@ bool Item_func_convert_tz::get_date(MYSQL_TIME *ltime, void Item_func_convert_tz::cleanup() { from_tz_cached= to_tz_cached= 0; - Item_date_func::cleanup(); + Item_temporal_func::cleanup(); } @@ -2145,21 +1952,20 @@ void Item_date_add_interval::fix_length_and_dec() { enum_field_types arg0_field_type; - collation.set(&my_charset_bin); - maybe_null=1; - max_length=MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; - value.alloc(max_length); - /* The field type for the result of an Item_date function is defined as follows: - If first arg is a MYSQL_TYPE_DATETIME result is MYSQL_TYPE_DATETIME - If first arg is a MYSQL_TYPE_DATE and the interval type uses hours, - minutes or seconds then type is MYSQL_TYPE_DATETIME. + minutes or seconds then type is MYSQL_TYPE_DATETIME + otherwise it's MYSQL_TYPE_DATE + - if first arg is a MYSQL_TYPE_TIME and the interval type isn't using + anything larger than days, then the result is MYSQL_TYPE_TIME, + otherwise - MYSQL_TYPE_DATETIME. - Otherwise the result is MYSQL_TYPE_STRING - (This is because you can't know if the string contains a DATE, MYSQL_TIME or - DATETIME argument) + (This is because you can't know if the string contains a DATE, + MYSQL_TIME or DATETIME argument) */ cached_field_type= MYSQL_TYPE_STRING; arg0_field_type= args[0]->field_type(); @@ -2173,6 +1979,20 @@ void Item_date_add_interval::fix_length_and_dec() else cached_field_type= MYSQL_TYPE_DATETIME; } + else if (arg0_field_type == MYSQL_TYPE_TIME) + { + if (int_type >= INTERVAL_DAY && int_type != INTERVAL_YEAR_MONTH) + cached_field_type= arg0_field_type; + else + cached_field_type= MYSQL_TYPE_DATETIME; + } + if (int_type == INTERVAL_MICROSECOND || int_type >= INTERVAL_DAY_MICROSECOND) + decimals= 6; + else + decimals= args[0]->decimals; + + Item_temporal_func::fix_length_and_dec(); + value.alloc(max_length); } @@ -2182,7 +2002,7 @@ bool Item_date_add_interval::get_date(MYSQL_TIME *ltime, uint fuzzy_date) { INTERVAL interval; - if (args[0]->get_date(ltime, TIME_NO_ZERO_DATE) || + if (args[0]->get_date(ltime, TIME_NO_ZERO_DATE | TIME_FUZZY_DATE) || get_interval_value(args[1], int_type, &value, &interval)) return (null_value=1); @@ -2195,44 +2015,6 @@ bool Item_date_add_interval::get_date(MYSQL_TIME *ltime, uint fuzzy_date) } -String *Item_date_add_interval::val_str(String *str) -{ - DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - enum date_time_format_types format; - - if (Item_date_add_interval::get_date(<ime, TIME_NO_ZERO_DATE)) - return 0; - - if (ltime.time_type == MYSQL_TIMESTAMP_DATE) - format= DATE_ONLY; - else if (ltime.second_part) - format= DATE_TIME_MICROSECOND; - else - format= DATE_TIME; - - if (!make_datetime(format, <ime, str)) - return str; - - null_value=1; - return 0; -} - - -longlong Item_date_add_interval::val_int() -{ - DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - longlong date; - if (Item_date_add_interval::get_date(<ime, TIME_NO_ZERO_DATE)) - return (longlong) 0; - date = (ltime.year*100L + ltime.month)*100L + ltime.day; - return ltime.time_type == MYSQL_TIMESTAMP_DATE ? date : - ((date*100L + ltime.hour)*100L+ ltime.minute)*100L + ltime.second; -} - - - bool Item_date_add_interval::eq(const Item *item, bool binary_cmp) const { Item_date_add_interval *other= (Item_date_add_interval*) item; @@ -2314,29 +2096,12 @@ longlong Item_extract::val_int() uint year; ulong week_format; long neg; - if (date_value) - { - if (get_arg0_date(<ime, TIME_FUZZY_DATE)) - return 0; - neg=1; - } - else - { - char buf[40]; - String value(buf, sizeof(buf), &my_charset_bin);; - String *res= args[0]->val_str(&value); - if (!res || - str_to_time_with_warn(res->ptr(), res->length(), <ime, - current_thd->variables.sql_mode & - (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | - MODE_INVALID_DATES))) - { - null_value=1; - return 0; - } - neg= ltime.neg ? -1 : 1; - null_value=0; - } + int is_time_flag = date_value ? 0 : TIME_TIME_ONLY; + + if (get_arg0_date(<ime, TIME_FUZZY_DATE | is_time_flag)) + return 0; + neg= ltime.neg ? -1 : 1; + switch (int_type) { case INTERVAL_YEAR: return ltime.year; case INTERVAL_YEAR_MONTH: return ltime.year*100L+ltime.month; @@ -2419,7 +2184,7 @@ bool Item_char_typecast::eq(const Item *item, bool binary_cmp) const return 1; } -void Item_typecast::print(String *str, enum_query_type query_type) +void Item_temporal_typecast::print(String *str, enum_query_type query_type) { str->append(STRING_WITH_LEN("cast(")); args[0]->print(str, query_type); @@ -2434,13 +2199,13 @@ void Item_char_typecast::print(String *str, enum_query_type query_type) str->append(STRING_WITH_LEN("cast(")); args[0]->print(str, query_type); str->append(STRING_WITH_LEN(" as char")); - if (cast_length >= 0) + if (cast_length != ~0U) { str->append('('); char buffer[20]; // my_charset_bin is good enough for numbers String st(buffer, sizeof(buffer), &my_charset_bin); - st.set((ulonglong)cast_length, &my_charset_bin); + st.set(static_cast<ulonglong>(cast_length), &my_charset_bin); str->append(st); str->append(')'); } @@ -2492,7 +2257,7 @@ String *Item_char_typecast::val_str(String *str) and the result is longer than cast length, e.g. CAST('string' AS CHAR(1)) */ - if (cast_length >= 0) + if (cast_length != ~0U) { if (res->length() > (length= (uint32) res->charpos(cast_length))) { // Safe even if const arg @@ -2512,16 +2277,15 @@ String *Item_char_typecast::val_str(String *str) res->c_ptr_safe()); res->length((uint) length); } - else if (cast_cs == &my_charset_bin && res->length() < (uint) cast_length) + else if (cast_cs == &my_charset_bin && res->length() < cast_length) { - if (res->alloced_length() < (uint) cast_length) + if (res->alloced_length() < cast_length) { str_value.alloc(cast_length); str_value.copy(*res); res= &str_value; } - bzero((char*) res->ptr() + res->length(), - (uint) cast_length - res->length()); + bzero((char*) res->ptr() + res->length(), cast_length - res->length()); res->length(cast_length); } } @@ -2571,142 +2335,75 @@ void Item_char_typecast::fix_length_and_dec() from_cs != &my_charset_bin && cast_cs != &my_charset_bin); collation.set(cast_cs, DERIVATION_IMPLICIT); - char_length= (cast_length >= 0) ? cast_length : + char_length= ((cast_length != ~0U) ? cast_length : args[0]->max_length / - (cast_cs == &my_charset_bin ? 1 : args[0]->collation.collation->mbmaxlen); + (cast_cs == &my_charset_bin ? 1 : + args[0]->collation.collation->mbmaxlen)); max_length= char_length * cast_cs->mbmaxlen; } -String *Item_datetime_typecast::val_str(String *str) -{ - DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - - if (!get_arg0_date(<ime, TIME_FUZZY_DATE) && - !make_datetime(ltime.second_part ? DATE_TIME_MICROSECOND : DATE_TIME, - <ime, str)) - return str; - - null_value=1; - return 0; -} - - -longlong Item_datetime_typecast::val_int() -{ - DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - if (get_arg0_date(<ime,1)) - { - null_value= 1; - return 0; - } - - return TIME_to_ulonglong_datetime(<ime); -} - - -bool Item_time_typecast::get_time(MYSQL_TIME *ltime) +bool Item_time_typecast::get_date(MYSQL_TIME *ltime, uint fuzzy_date) { - bool res= get_arg0_time(ltime); + if (get_arg0_time(ltime)) + return 1; /* - For MYSQL_TIMESTAMP_TIME value we can have non-zero day part, + MYSQL_TIMESTAMP_TIME value can have non-zero day part, which we should not lose. */ - if (ltime->time_type == MYSQL_TIMESTAMP_DATETIME) + if (ltime->time_type != MYSQL_TIMESTAMP_TIME) ltime->year= ltime->month= ltime->day= 0; ltime->time_type= MYSQL_TIMESTAMP_TIME; - return res; -} - -bool Item_time_typecast::get_date(MYSQL_TIME *ltime, uint fuzzy_date) -{ - return get_time(ltime); -} - - -longlong Item_time_typecast::val_int() -{ - MYSQL_TIME ltime; - if (get_time(<ime)) - { - null_value= 1; - return 0; - } - return ltime.hour * 10000L + ltime.minute * 100 + ltime.second; -} - -String *Item_time_typecast::val_str(String *str) -{ - DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - - if (!get_time(<ime) && - !make_datetime(ltime.second_part ? TIME_MICROSECOND : TIME_ONLY, - <ime, str)) - return str; - - null_value=1; return 0; } bool Item_date_typecast::get_date(MYSQL_TIME *ltime, uint fuzzy_date) { - bool res= get_arg0_date(ltime, TIME_FUZZY_DATE); + if (get_arg0_date(ltime, fuzzy_date & ~TIME_TIME_ONLY)) + return 1; ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0; ltime->time_type= MYSQL_TIMESTAMP_DATE; - return res; -} - - -bool Item_date_typecast::get_time(MYSQL_TIME *ltime) -{ - bzero((char *)ltime, sizeof(MYSQL_TIME)); - return args[0]->null_value; + return 0; } -String *Item_date_typecast::val_str(String *str) +bool Item_datetime_typecast::get_date(MYSQL_TIME *ltime, uint fuzzy_date) { - DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; + if (get_arg0_date(ltime, fuzzy_date & ~TIME_TIME_ONLY)) + return 1; - if (!get_arg0_date(<ime, TIME_FUZZY_DATE) && - !str->alloc(MAX_DATE_STRING_REP_LENGTH)) + /* + ltime is valid MYSQL_TYPE_TIME (according to fuzzy_date). + But not every valid TIME value is a valid DATETIME value! + */ + if (ltime->time_type == MYSQL_TIMESTAMP_TIME && ltime->hour >= 24) { - make_date((DATE_TIME_FORMAT *) 0, <ime, str); - return str; + Lazy_string_time str(ltime); + make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + &str, MYSQL_TIMESTAMP_DATETIME, 0); + return (null_value= 1); } - null_value=1; + ltime->time_type= MYSQL_TIMESTAMP_DATETIME; return 0; } -longlong Item_date_typecast::val_int() -{ - DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - if ((null_value= args[0]->get_date(<ime, TIME_FUZZY_DATE))) - return 0; - return (longlong) (ltime.year * 10000L + ltime.month * 100 + ltime.day); -} /** MAKEDATE(a,b) is a date function that creates a date value from a year and day value. NOTES: - As arguments are integers, we can't know if the year is a 2 digit or 4 digit year. - In this case we treat all years < 100 as 2 digit years. Ie, this is not safe - for dates between 0000-01-01 and 0099-12-31 + As arguments are integers, we can't know if the year is a 2 digit + or 4 digit year. In this case we treat all years < 100 as 2 digit + years. Ie, this is not safe for dates between 0000-01-01 and + 0099-12-31 */ -String *Item_func_makedate::val_str(String *str) +bool Item_func_makedate::get_date(MYSQL_TIME *ltime, uint fuzzy_date) { DBUG_ASSERT(fixed == 1); - MYSQL_TIME l_time; long daynr= (long) args[1]->val_int(); long year= (long) args[0]->val_int(); long days; @@ -2722,66 +2419,21 @@ String *Item_func_makedate::val_str(String *str) /* Day number from year 0 to 9999-12-31 */ if (days >= 0 && days <= MAX_DAY_NUMBER) { - null_value=0; - get_date_from_daynr(days,&l_time.year,&l_time.month,&l_time.day); - if (str->alloc(MAX_DATE_STRING_REP_LENGTH)) - goto err; - make_date((DATE_TIME_FORMAT *) 0, &l_time, str); - return str; - } - -err: - null_value=1; - return 0; -} - - -/* - MAKEDATE(a,b) is a date function that creates a date value - from a year and day value. - - NOTES: - As arguments are integers, we can't know if the year is a 2 digit or 4 digit year. - In this case we treat all years < 100 as 2 digit years. Ie, this is not safe - for dates between 0000-01-01 and 0099-12-31 -*/ - -longlong Item_func_makedate::val_int() -{ - DBUG_ASSERT(fixed == 1); - MYSQL_TIME l_time; - long daynr= (long) args[1]->val_int(); - long year= (long) args[0]->val_int(); - long days; - - if (args[0]->null_value || args[1]->null_value || - year < 0 || daynr <= 0) - goto err; - - if (year < 100) - year= year_2000_handling(year); - - days= calc_daynr(year,1,1) + daynr - 1; - /* Day number from year 0 to 9999-12-31 */ - if (days >= 0 && days < MAX_DAY_NUMBER) - { - null_value=0; - get_date_from_daynr(days,&l_time.year,&l_time.month,&l_time.day); - return (longlong) (l_time.year * 10000L + l_time.month * 100 + l_time.day); + bzero(ltime, sizeof(*ltime)); + ltime->time_type= MYSQL_TIMESTAMP_DATE; + get_date_from_daynr(days, <ime->year, <ime->month, <ime->day); + return (null_value= 0); } err: - null_value= 1; - return 0; + return (null_value= 1); } void Item_func_add_time::fix_length_and_dec() { enum_field_types arg0_field_type; - decimals=0; - max_length=MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; - maybe_null= 1; + decimals= max(args[0]->decimals, args[1]->decimals); /* The field type for the result of an Item_func_add_time function is defined @@ -2801,6 +2453,7 @@ void Item_func_add_time::fix_length_and_dec() cached_field_type= MYSQL_TYPE_DATETIME; else if (arg0_field_type == MYSQL_TYPE_TIME) cached_field_type= MYSQL_TYPE_TIME; + Item_temporal_func::fix_length_and_dec(); } /** @@ -2813,38 +2466,38 @@ void Item_func_add_time::fix_length_and_dec() Result: Time value or datetime value */ -String *Item_func_add_time::val_str(String *str) +bool Item_func_add_time::get_date(MYSQL_TIME *ltime, uint fuzzy_date) { DBUG_ASSERT(fixed == 1); - MYSQL_TIME l_time1, l_time2, l_time3; + MYSQL_TIME l_time1, l_time2; bool is_time= 0; long days, microseconds; longlong seconds; - int l_sign= sign; + int l_sign= sign, was_cut= 0; + uint dec= decimals; - null_value=0; if (is_date) // TIMESTAMP function { if (get_arg0_date(&l_time1, TIME_FUZZY_DATE) || args[1]->get_time(&l_time2) || l_time1.time_type == MYSQL_TIMESTAMP_TIME || l_time2.time_type != MYSQL_TIMESTAMP_TIME) - goto null_date; + return (null_value= 1); } else // ADDTIME function { if (args[0]->get_time(&l_time1) || args[1]->get_time(&l_time2) || l_time2.time_type == MYSQL_TIMESTAMP_DATETIME) - goto null_date; + return (null_value= 1); is_time= (l_time1.time_type == MYSQL_TIMESTAMP_TIME); } if (l_time1.neg != l_time2.neg) l_sign= -l_sign; - bzero((char *)&l_time3, sizeof(l_time3)); + bzero(ltime, sizeof(*ltime)); - l_time3.neg= calc_time_diff(&l_time1, &l_time2, -l_sign, + ltime->neg= calc_time_diff(&l_time1, &l_time2, -l_sign, &seconds, µseconds); /* @@ -2852,35 +2505,40 @@ String *Item_func_add_time::val_str(String *str) is non-zero we need to swap sign to get proper result. */ if (l_time1.neg && (seconds || microseconds)) - l_time3.neg= 1-l_time3.neg; // Swap sign of result + ltime->neg= 1-ltime->neg; // Swap sign of result - if (!is_time && l_time3.neg) - goto null_date; + if (!is_time && ltime->neg) + return (null_value= 1); days= (long)(seconds/86400L); - calc_time_from_sec(&l_time3, (long)(seconds%86400L), microseconds); + calc_time_from_sec(ltime, (long)(seconds%86400L), microseconds); + + ltime->time_type= is_time ? MYSQL_TIMESTAMP_TIME : MYSQL_TIMESTAMP_DATETIME; + + if (cached_field_type == MYSQL_TYPE_STRING && + (l_time1.second_part || l_time2.second_part)) + dec= TIME_SECOND_PART_DIGITS; if (!is_time) { - get_date_from_daynr(days,&l_time3.year,&l_time3.month,&l_time3.day); - if (l_time3.day && - !make_datetime(l_time1.second_part || l_time2.second_part ? - DATE_TIME_MICROSECOND : DATE_TIME, - &l_time3, str)) - return str; - goto null_date; + get_date_from_daynr(days,<ime->year,<ime->month,<ime->day); + if (!ltime->day) + return (null_value= 1); + return (null_value= 0); } - l_time3.hour+= days*24; - if (!make_datetime_with_warn(l_time1.second_part || l_time2.second_part ? - TIME_MICROSECOND : TIME_ONLY, - &l_time3, str)) - return str; + ltime->hour+= days*24; -null_date: - null_value=1; - return 0; + MYSQL_TIME copy= *ltime; + Lazy_string_time str(©); + + check_time_range(ltime, decimals, &was_cut); + if (was_cut) + make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + &str, MYSQL_TIMESTAMP_TIME, NullS); + + return (null_value= 0); } @@ -2913,15 +2571,19 @@ void Item_func_add_time::print(String *str, enum_query_type query_type) Result: Time value */ -String *Item_func_timediff::val_str(String *str) +bool Item_func_timediff::get_date(MYSQL_TIME *ltime, uint fuzzy_date) { DBUG_ASSERT(fixed == 1); longlong seconds; long microseconds; - int l_sign= 1; - MYSQL_TIME l_time1 ,l_time2, l_time3; + int l_sign= 1, was_cut= 0; + MYSQL_TIME l_time1,l_time2,l_time3; + Lazy_string_time str(&l_time3); + + /* the following may be true in, for example, date_add(timediff(...), ... */ + if (fuzzy_date & TIME_NO_ZERO_IN_DATE) + goto null_date; - null_value= 0; if (args[0]->get_time(&l_time1) || args[1]->get_time(&l_time2) || l_time1.time_type != l_time2.time_type) @@ -2943,16 +2605,30 @@ String *Item_func_timediff::val_str(String *str) if (l_time1.neg && (seconds || microseconds)) l_time3.neg= 1-l_time3.neg; // Swap sign of result + /* + seconds is longlong, when casted to long it may become a small number + even if the original seconds value was too large and invalid. + as a workaround we limit seconds by a large invalid long number + ("invalid" means > TIME_MAX_SECOND) + */ + set_if_smaller(seconds, INT_MAX32); + calc_time_from_sec(&l_time3, (long) seconds, microseconds); - if (!make_datetime_with_warn(l_time1.second_part || l_time2.second_part ? - TIME_MICROSECOND : TIME_ONLY, - &l_time3, str)) - return str; + if ((fuzzy_date & TIME_NO_ZERO_DATE) && (seconds == 0) && + (microseconds == 0)) + goto null_date; + + *ltime= l_time3; + check_time_range(ltime, decimals, &was_cut); + + if (was_cut) + make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + &str, MYSQL_TIMESTAMP_TIME, NullS); + return (null_value= 0); null_date: - null_value=1; - return 0; + return (null_value= 1); } /** @@ -2961,10 +2637,9 @@ null_date: Result: Time value */ -String *Item_func_maketime::val_str(String *str) +bool Item_func_maketime::get_date(MYSQL_TIME *ltime, uint fuzzy_date) { DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; bool overflow= 0; longlong hour= args[0]->val_int(); @@ -2975,12 +2650,11 @@ String *Item_func_maketime::val_str(String *str) args[1]->null_value || args[2]->null_value || minute < 0 || minute > 59 || - second < 0 || second > 59 || - str->alloc(MAX_DATE_STRING_REP_LENGTH)))) - return 0; + second < 0 || second > 59))) + return 1; - bzero((char *)<ime, sizeof(ltime)); - ltime.neg= 0; + bzero(ltime, sizeof(*ltime)); + ltime->time_type= MYSQL_TIMESTAMP_TIME; /* Check for integer overflows */ if (hour < 0) @@ -2988,22 +2662,22 @@ String *Item_func_maketime::val_str(String *str) if (args[0]->unsigned_flag) overflow= 1; else - ltime.neg= 1; + ltime->neg= 1; } - if (-hour > UINT_MAX || hour > UINT_MAX) + if (-hour > TIME_MAX_HOUR || hour > TIME_MAX_HOUR) overflow= 1; if (!overflow) { - ltime.hour= (uint) ((hour < 0 ? -hour : hour)); - ltime.minute= (uint) minute; - ltime.second= (uint) second; + ltime->hour= (uint) ((hour < 0 ? -hour : hour)); + ltime->minute= (uint) minute; + ltime->second= (uint) second; } else { - ltime.hour= TIME_MAX_HOUR; - ltime.minute= TIME_MAX_MINUTE; - ltime.second= TIME_MAX_SECOND; + ltime->hour= TIME_MAX_HOUR; + ltime->minute= TIME_MAX_MINUTE; + ltime->second= TIME_MAX_SECOND; char buf[28]; char *ptr= longlong10_to_str(hour, buf, args[0]->unsigned_flag ? 10 : -10); int len = (int)(ptr - buf) + @@ -3013,12 +2687,7 @@ String *Item_func_maketime::val_str(String *str) NullS); } - if (make_time_with_warn((DATE_TIME_FORMAT *) 0, <ime, str)) - { - null_value= 1; - return 0; - } - return str; + return 0; } @@ -3049,8 +2718,8 @@ longlong Item_func_timestamp_diff::val_int() int neg= 1; null_value= 0; - if (args[0]->get_date(<ime1, TIME_NO_ZERO_DATE) || - args[1]->get_date(<ime2, TIME_NO_ZERO_DATE)) + if (args[0]->get_date(<ime1, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE) || + args[1]->get_date(<ime2, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) goto null_date; if (calc_time_diff(<ime2,<ime1, 1, @@ -3300,9 +2969,9 @@ get_date_time_result_type(const char *format, uint length) have all types of date-time components and can end our search. */ return DATE_TIME_MICROSECOND; + } } } - } /* We don't have all three types of date-time components */ if (frac_second_used) @@ -3319,40 +2988,39 @@ get_date_time_result_type(const char *format, uint length) void Item_func_str_to_date::fix_length_and_dec() { - maybe_null= 1; - decimals=0; - cached_format_type= DATE_TIME; cached_field_type= MYSQL_TYPE_DATETIME; - max_length= MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; - cached_timestamp_type= MYSQL_TIMESTAMP_NONE; + decimals= NOT_FIXED_DEC; if ((const_item= args[1]->const_item())) { char format_buff[64]; String format_str(format_buff, sizeof(format_buff), &my_charset_bin); String *format= args[1]->val_str(&format_str); + decimals= 0; if (!args[1]->null_value) { - cached_format_type= get_date_time_result_type(format->ptr(), - format->length()); + date_time_format_types cached_format_type= + get_date_time_result_type(format->ptr(), format->length()); switch (cached_format_type) { case DATE_ONLY: - cached_timestamp_type= MYSQL_TIMESTAMP_DATE; cached_field_type= MYSQL_TYPE_DATE; - max_length= MAX_DATE_WIDTH * MY_CHARSET_BIN_MB_MAXLEN; break; - case TIME_ONLY: case TIME_MICROSECOND: - cached_timestamp_type= MYSQL_TIMESTAMP_TIME; + decimals= 6; + /* fall through */ + case TIME_ONLY: cached_field_type= MYSQL_TYPE_TIME; - max_length= MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN; break; - default: - cached_timestamp_type= MYSQL_TIMESTAMP_DATETIME; + case DATE_TIME_MICROSECOND: + decimals= 6; + /* fall through */ + case DATE_TIME: cached_field_type= MYSQL_TYPE_DATETIME; break; } } } + cached_timestamp_type= mysql_type_to_time_type(cached_field_type); + Item_temporal_func::fix_length_and_dec(); } @@ -3361,21 +3029,19 @@ bool Item_func_str_to_date::get_date(MYSQL_TIME *ltime, uint fuzzy_date) DATE_TIME_FORMAT date_time_format; char val_buff[64], format_buff[64]; String val_string(val_buff, sizeof(val_buff), &my_charset_bin), *val; - String format_str(format_buff, sizeof(format_buff), &my_charset_bin), *format; + String format_str(format_buff, sizeof(format_buff), &my_charset_bin), + *format; val= args[0]->val_str(&val_string); format= args[1]->val_str(&format_str); if (args[0]->null_value || args[1]->null_value) goto null_date; - null_value= 0; - bzero((char*) ltime, sizeof(*ltime)); date_time_format.format.str= (char*) format->ptr(); date_time_format.format.length= format->length(); if (extract_date_time(&date_time_format, val->ptr(), val->length(), - ltime, cached_timestamp_type, 0, "datetime") || - ((fuzzy_date & TIME_NO_ZERO_DATE) && - (ltime->year == 0 || ltime->month == 0 || ltime->day == 0))) + ltime, cached_timestamp_type, 0, "datetime", + fuzzy_date)) goto null_date; if (cached_timestamp_type == MYSQL_TIMESTAMP_TIME && ltime->day) { @@ -3387,6 +3053,7 @@ bool Item_func_str_to_date::get_date(MYSQL_TIME *ltime, uint fuzzy_date) ltime->hour+= ltime->day*24; ltime->day= 0; } + null_value= 0; return 0; null_date: @@ -3394,22 +3061,6 @@ null_date: } -String *Item_func_str_to_date::val_str(String *str) -{ - DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - - if (Item_func_str_to_date::get_date(<ime, TIME_FUZZY_DATE)) - return 0; - - if (!make_datetime((const_item ? cached_format_type : - (ltime.second_part ? DATE_TIME_MICROSECOND : DATE_TIME)), - <ime, str)) - return str; - return 0; -} - - bool Item_func_last_day::get_date(MYSQL_TIME *ltime, uint fuzzy_date) { if (get_arg0_date(ltime, fuzzy_date & ~TIME_FUZZY_DATE) || diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 27c3401a6a9..d9559794c00 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -358,15 +358,38 @@ class Item_func_dayname :public Item_func_weekday }; -class Item_func_unix_timestamp :public Item_int_func +class Item_func_seconds_hybrid: public Item_func_numhybrid { - String value; public: - Item_func_unix_timestamp() :Item_int_func() {} - Item_func_unix_timestamp(Item *a) :Item_int_func(a) {} - longlong val_int(); + Item_func_seconds_hybrid() :Item_func_numhybrid() {} + Item_func_seconds_hybrid(Item *a) :Item_func_numhybrid(a) {} + void fix_num_length_and_dec() + { + if (arg_count) + decimals= args[0]->decimals; + if (decimals != NOT_FIXED_DEC) + set_if_smaller(decimals, TIME_SECOND_PART_DIGITS); + max_length=17 + (decimals ? decimals + 1 : 0); + } + void find_num_type() { hybrid_type= decimals ? REAL_RESULT : INT_RESULT; } + my_decimal *decimal_op(my_decimal* buf) { DBUG_ASSERT(0); return 0; } + String *str_op(String *str) { DBUG_ASSERT(0); return 0; } +}; + + +class Item_func_unix_timestamp :public Item_func_seconds_hybrid +{ + bool get_timestamp_value(my_time_t *seconds, ulong *second_part); +public: + Item_func_unix_timestamp() :Item_func_seconds_hybrid() {} + Item_func_unix_timestamp(Item *a) :Item_func_seconds_hybrid(a) {} const char *func_name() const { return "unix_timestamp"; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + void fix_num_length_and_dec() + { + maybe_null= false; + Item_func_seconds_hybrid::fix_num_length_and_dec(); + } /* UNIX_TIMESTAMP() depends on the current timezone (and thus may not be used as a partitioning function) @@ -376,11 +399,6 @@ public: { return !has_timestamp_args(); } - void fix_length_and_dec() - { - decimals=0; - max_length=10*MY_CHARSET_BIN_MB_MAXLEN; - } bool check_vcol_func_processor(uchar *int_arg) { /* @@ -389,20 +407,21 @@ public: */ return trace_unsupported_by_check_vcol_func_processor(func_name()); } + longlong int_op(); + double real_op(); }; -class Item_func_time_to_sec :public Item_int_func + +class Item_func_time_to_sec :public Item_func_seconds_hybrid { public: - Item_func_time_to_sec(Item *item) :Item_int_func(item) {} - longlong val_int(); + Item_func_time_to_sec(Item *item) :Item_func_seconds_hybrid(item) {} const char *func_name() const { return "time_to_sec"; } - void fix_length_and_dec() + void fix_num_length_and_dec() { - maybe_null= TRUE; - decimals=0; - max_length=10*MY_CHARSET_BIN_MB_MAXLEN; + maybe_null= true; + Item_func_seconds_hybrid::fix_num_length_and_dec(); } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} @@ -410,126 +429,101 @@ public: { return !has_time_args(); } + longlong int_op(); + double real_op(); }; -/* - This can't be a Item_str_func, because the val_real() functions are special -*/ - -class Item_date :public Item_func +class Item_temporal_func: public Item_func { public: - Item_date() :Item_func() {} - Item_date(Item *a) :Item_func(a) {} + Item_temporal_func() :Item_func() {} + Item_temporal_func(Item *a) :Item_func(a) {} + Item_temporal_func(Item *a, Item *b) :Item_func(a,b) {} + Item_temporal_func(Item *a, Item *b, Item *c) :Item_func(a,b,c) {} enum Item_result result_type () const { return STRING_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_DATE; } + enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } String *val_str(String *str); longlong val_int(); - double val_real() { return val_real_from_decimal(); } - const char *func_name() const { return "date"; } - void fix_length_and_dec() - { - collation.set(&my_charset_bin); - decimals=0; - max_length=MAX_DATE_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; - } - Field *tmp_table_field(TABLE *table) - { - return tmp_table_field_from_field_type(table, 0); - } - bool result_as_longlong() { return TRUE; } + double val_real(); + bool get_date(MYSQL_TIME *res, uint fuzzy_date) { DBUG_ASSERT(0); return 1; } + bool get_time(MYSQL_TIME *res); my_decimal *val_decimal(my_decimal *decimal_value) - { - DBUG_ASSERT(fixed == 1); - return val_decimal_from_date(decimal_value); - } + { return val_decimal_from_date(decimal_value); } + Field *tmp_table_field(TABLE *table) + { return tmp_table_field_from_field_type(table, 0); } int save_in_field(Field *field, bool no_conversions) - { - return save_date_in_field(field); + { return save_date_in_field(field); } + void fix_length_and_dec() + { + static const uint max_time_type_width[5]= + { MAX_DATETIME_WIDTH, MAX_DATETIME_WIDTH, MAX_DATE_WIDTH, + MAX_DATETIME_WIDTH, MIN_TIME_WIDTH }; + + max_length= max_time_type_width[mysql_type_to_time_type(field_type())+2]; + if (decimals) + { + if (decimals == NOT_FIXED_DEC) + max_length+= TIME_SECOND_PART_DIGITS + 1; + else + { + set_if_smaller(decimals, TIME_SECOND_PART_DIGITS); + max_length+= decimals + 1; + } + } + /* + We set maybe_null to 1 as default as any bad argument with date or + time can get us to return NULL. + */ + maybe_null= 1; } }; -class Item_date_func :public Item_str_func +class Item_datefunc :public Item_temporal_func { public: - Item_date_func() :Item_str_func() {} - Item_date_func(Item *a) :Item_str_func(a) {} - Item_date_func(Item *a,Item *b) :Item_str_func(a,b) {} - Item_date_func(Item *a,Item *b, Item *c) :Item_str_func(a,b,c) {} - enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } - Field *tmp_table_field(TABLE *table) - { - return tmp_table_field_from_field_type(table, 0); - } - bool result_as_longlong() { return TRUE; } - double val_real() { return (double) val_int(); } - my_decimal *val_decimal(my_decimal *decimal_value) - { - DBUG_ASSERT(fixed == 1); - return val_decimal_from_date(decimal_value); - } - int save_in_field(Field *field, bool no_conversions) - { - return save_date_in_field(field); - } + Item_datefunc() :Item_temporal_func() { } + Item_datefunc(Item *a) :Item_temporal_func(a) { } + enum_field_types field_type() const { return MYSQL_TYPE_DATE; } + const char *func_name() const { return "date"; } + bool get_date(MYSQL_TIME *res, uint fuzzy_date) + { return get_arg0_date(res, fuzzy_date); } }; -class Item_str_timefunc :public Item_str_func +class Item_timefunc :public Item_temporal_func { public: - Item_str_timefunc() :Item_str_func() {} - Item_str_timefunc(Item *a) :Item_str_func(a) {} - Item_str_timefunc(Item *a,Item *b) :Item_str_func(a,b) {} - Item_str_timefunc(Item *a, Item *b, Item *c) :Item_str_func(a, b ,c) {} + Item_timefunc() :Item_temporal_func() {} + Item_timefunc(Item *a) :Item_temporal_func(a) {} + Item_timefunc(Item *a,Item *b) :Item_temporal_func(a,b) {} + Item_timefunc(Item *a, Item *b, Item *c) :Item_temporal_func(a, b ,c) {} enum_field_types field_type() const { return MYSQL_TYPE_TIME; } - void fix_length_and_dec() - { - decimals= DATETIME_DEC; - max_length=MAX_TIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; - } - Field *tmp_table_field(TABLE *table) - { - return tmp_table_field_from_field_type(table, 0); - } - double val_real() { return val_real_from_decimal(); } - my_decimal *val_decimal(my_decimal *decimal_value) - { - DBUG_ASSERT(fixed == 1); - return val_decimal_from_time(decimal_value); - } - int save_in_field(Field *field, bool no_conversions) - { - return save_time_in_field(field); - } - longlong val_int() { return val_int_from_decimal(); } - bool result_as_longlong() { return TRUE; } }; /* Abstract CURTIME function. Children should define what time zone is used */ -class Item_func_curtime :public Item_str_timefunc +class Item_func_curtime :public Item_timefunc { - longlong value; - char buff[9*2+32]; - uint buff_length; + MYSQL_TIME ltime; public: - Item_func_curtime() :Item_str_timefunc() {} - Item_func_curtime(Item *a) :Item_str_timefunc(a) {} - double val_real() { DBUG_ASSERT(fixed == 1); return (double) value; } - longlong val_int() { DBUG_ASSERT(fixed == 1); return value; } - String *val_str(String *str); - void fix_length_and_dec(); + Item_func_curtime(uint dec) :Item_timefunc() { decimals= dec; } + bool fix_fields(THD *, Item **); + void fix_length_and_dec() + { + store_now_in_TIME(<ime); + Item_timefunc::fix_length_and_dec(); + maybe_null= 0; + } + bool get_date(MYSQL_TIME *res, uint fuzzy_date); /* Abstract method that defines which time zone is used for conversion. Converts time current time in my_time_t representation to broken-down MYSQL_TIME representation using UTC-SYSTEM or per-thread time zone. */ virtual void store_now_in_TIME(MYSQL_TIME *now_time)=0; - bool result_as_longlong() { return TRUE; } bool check_vcol_func_processor(uchar *int_arg) { return trace_unsupported_by_check_vcol_func_processor(func_name()); @@ -540,8 +534,7 @@ public: class Item_func_curtime_local :public Item_func_curtime { public: - Item_func_curtime_local() :Item_func_curtime() {} - Item_func_curtime_local(Item *a) :Item_func_curtime(a) {} + Item_func_curtime_local(uint dec) :Item_func_curtime(dec) {} const char *func_name() const { return "curtime"; } virtual void store_now_in_TIME(MYSQL_TIME *now_time); }; @@ -550,8 +543,7 @@ public: class Item_func_curtime_utc :public Item_func_curtime { public: - Item_func_curtime_utc() :Item_func_curtime() {} - Item_func_curtime_utc(Item *a) :Item_func_curtime(a) {} + Item_func_curtime_utc(uint dec) :Item_func_curtime(dec) {} const char *func_name() const { return "utc_time"; } virtual void store_now_in_TIME(MYSQL_TIME *now_time); }; @@ -559,14 +551,11 @@ public: /* Abstract CURDATE function. See also Item_func_curtime. */ -class Item_func_curdate :public Item_date +class Item_func_curdate :public Item_datefunc { - longlong value; MYSQL_TIME ltime; public: - Item_func_curdate() :Item_date() {} - longlong val_int() { DBUG_ASSERT(fixed == 1); return (value) ; } - String *val_str(String *str); + Item_func_curdate() :Item_datefunc() {} void fix_length_and_dec(); bool get_date(MYSQL_TIME *res, uint fuzzy_date); virtual void store_now_in_TIME(MYSQL_TIME *now_time)=0; @@ -597,21 +586,19 @@ public: /* Abstract CURRENT_TIMESTAMP function. See also Item_func_curtime */ -class Item_func_now :public Item_date_func + +class Item_func_now :public Item_temporal_func { -protected: - longlong value; - char buff[20*2+32]; // +32 to make my_snprintf_{8bit|ucs2} happy - uint buff_length; MYSQL_TIME ltime; public: - Item_func_now() :Item_date_func() {} - Item_func_now(Item *a) :Item_date_func(a) {} - enum Item_result result_type () const { return STRING_RESULT; } - longlong val_int() { DBUG_ASSERT(fixed == 1); return value; } - int save_in_field(Field *to, bool no_conversions); - String *val_str(String *str); - void fix_length_and_dec(); + Item_func_now(uint dec) :Item_temporal_func() { decimals= dec; } + bool fix_fields(THD *, Item **); + void fix_length_and_dec() + { + store_now_in_TIME(<ime); + Item_temporal_func::fix_length_and_dec(); + maybe_null= 0; + } bool get_date(MYSQL_TIME *res, uint fuzzy_date); virtual void store_now_in_TIME(MYSQL_TIME *now_time)=0; bool check_vcol_func_processor(uchar *int_arg) @@ -624,8 +611,7 @@ public: class Item_func_now_local :public Item_func_now { public: - Item_func_now_local() :Item_func_now() {} - Item_func_now_local(Item *a) :Item_func_now(a) {} + Item_func_now_local(uint dec) :Item_func_now(dec) {} const char *func_name() const { return "now"; } virtual void store_now_in_TIME(MYSQL_TIME *now_time); virtual enum Functype functype() const { return NOW_FUNC; } @@ -635,8 +621,7 @@ public: class Item_func_now_utc :public Item_func_now { public: - Item_func_now_utc() :Item_func_now() {} - Item_func_now_utc(Item *a) :Item_func_now(a) {} + Item_func_now_utc(uint dec) :Item_func_now(dec) {} const char *func_name() const { return "utc_timestamp"; } virtual void store_now_in_TIME(MYSQL_TIME *now_time); }; @@ -649,16 +634,10 @@ public: class Item_func_sysdate_local :public Item_func_now { public: - Item_func_sysdate_local() :Item_func_now() {} - Item_func_sysdate_local(Item *a) :Item_func_now(a) {} + Item_func_sysdate_local(uint dec) :Item_func_now(dec) {} bool const_item() const { return 0; } const char *func_name() const { return "sysdate"; } void store_now_in_TIME(MYSQL_TIME *now_time); - double val_real(); - longlong val_int(); - int save_in_field(Field *to, bool no_conversions); - String *val_str(String *str); - void fix_length_and_dec(); bool get_date(MYSQL_TIME *res, uint fuzzy_date); void update_used_tables() { @@ -668,10 +647,10 @@ public: }; -class Item_func_from_days :public Item_date +class Item_func_from_days :public Item_datefunc { public: - Item_func_from_days(Item *a) :Item_date(a) {} + Item_func_from_days(Item *a) :Item_datefunc(a) {} const char *func_name() const { return "from_days"; } bool get_date(MYSQL_TIME *res, uint fuzzy_date); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} @@ -700,13 +679,11 @@ public: }; -class Item_func_from_unixtime :public Item_date_func +class Item_func_from_unixtime :public Item_temporal_func { THD *thd; public: - Item_func_from_unixtime(Item *a) :Item_date_func(a) {} - longlong val_int(); - String *val_str(String *str); + Item_func_from_unixtime(Item *a) :Item_temporal_func(a) {} const char *func_name() const { return "from_unixtime"; } void fix_length_and_dec(); bool get_date(MYSQL_TIME *res, uint fuzzy_date); @@ -727,7 +704,7 @@ class Time_zone; tables can be used during this function calculation for loading time zone descriptions. */ -class Item_func_convert_tz :public Item_date_func +class Item_func_convert_tz :public Item_temporal_func { /* If time zone parameters are constants we are caching objects that @@ -739,9 +716,7 @@ class Item_func_convert_tz :public Item_date_func Time_zone *from_tz, *to_tz; public: Item_func_convert_tz(Item *a, Item *b, Item *c): - Item_date_func(a, b, c), from_tz_cached(0), to_tz_cached(0) {} - longlong val_int(); - String *val_str(String *str); + Item_temporal_func(a, b, c), from_tz_cached(0), to_tz_cached(0) {} const char *func_name() const { return "convert_tz"; } void fix_length_and_dec(); bool get_date(MYSQL_TIME *res, uint fuzzy_date); @@ -749,29 +724,22 @@ class Item_func_convert_tz :public Item_date_func }; -class Item_func_sec_to_time :public Item_str_timefunc +class Item_func_sec_to_time :public Item_timefunc { + bool sec_to_time(my_decimal *seconds, MYSQL_TIME *ltime); public: - Item_func_sec_to_time(Item *item) :Item_str_timefunc(item) {} - double val_real() - { - DBUG_ASSERT(fixed == 1); - return (double) Item_func_sec_to_time::val_int(); - } - longlong val_int(); - String *val_str(String *); + Item_func_sec_to_time(Item *item) :Item_timefunc(item) {} + bool get_date(MYSQL_TIME *res, uint fuzzy_date); void fix_length_and_dec() - { - Item_str_timefunc::fix_length_and_dec(); - collation.set(&my_charset_bin); - maybe_null=1; + { + decimals= args[0]->decimals; + Item_timefunc::fix_length_and_dec(); } const char *func_name() const { return "sec_to_time"; } - bool result_as_longlong() { return TRUE; } }; -class Item_date_add_interval :public Item_date_func +class Item_date_add_interval :public Item_temporal_func { String value; enum_field_types cached_field_type; @@ -780,12 +748,10 @@ public: const interval_type int_type; // keep it public const bool date_sub_interval; // keep it public Item_date_add_interval(Item *a,Item *b,interval_type type_arg,bool neg_arg) - :Item_date_func(a,b),int_type(type_arg), date_sub_interval(neg_arg) {} - String *val_str(String *); + :Item_temporal_func(a,b),int_type(type_arg), date_sub_interval(neg_arg) {} const char *func_name() const { return "date_add_interval"; } void fix_length_and_dec(); enum_field_types field_type() const { return cached_field_type; } - longlong val_int(); bool get_date(MYSQL_TIME *res, uint fuzzy_date); bool eq(const Item *item, bool binary_cmp) const; virtual void print(String *str, enum_query_type query_type); @@ -846,184 +812,84 @@ class Item_extract :public Item_int_func }; -class Item_typecast :public Item_str_func -{ -public: - Item_typecast(Item *a) :Item_str_func(a) {} - String *val_str(String *a) - { - DBUG_ASSERT(fixed == 1); - String *tmp=args[0]->val_str(a); - null_value=args[0]->null_value; - if (tmp) - tmp->set_charset(collation.collation); - return tmp; - } - void fix_length_and_dec() - { - collation.set(&my_charset_bin); - max_length=args[0]->max_length; - } - virtual const char* cast_type() const= 0; - virtual void print(String *str, enum_query_type query_type); -}; - - -class Item_typecast_maybe_null :public Item_typecast -{ -public: - Item_typecast_maybe_null(Item *a) :Item_typecast(a) {} - void fix_length_and_dec() - { - collation.set(&my_charset_bin); - max_length=args[0]->max_length; - maybe_null= 1; - } -}; - - -class Item_char_typecast :public Item_typecast +class Item_char_typecast :public Item_str_func { - int cast_length; + uint cast_length; CHARSET_INFO *cast_cs, *from_cs; bool charset_conversion; String tmp_value; public: - Item_char_typecast(Item *a, int length_arg, CHARSET_INFO *cs_arg) - :Item_typecast(a), cast_length(length_arg), cast_cs(cs_arg) {} + Item_char_typecast(Item *a, uint length_arg, CHARSET_INFO *cs_arg) + :Item_str_func(a), cast_length(length_arg), cast_cs(cs_arg) {} enum Functype functype() const { return CHAR_TYPECAST_FUNC; } bool eq(const Item *item, bool binary_cmp) const; const char *func_name() const { return "cast_as_char"; } - const char* cast_type() const { return "char"; }; String *val_str(String *a); void fix_length_and_dec(); - virtual void print(String *str, enum_query_type query_type); + void print(String *str, enum_query_type query_type); }; -class Item_date_typecast :public Item_typecast_maybe_null +class Item_temporal_typecast: public Item_temporal_func +{ +public: + Item_temporal_typecast(Item *a) :Item_temporal_func(a) {} + virtual const char *cast_type() const = 0; + void print(String *str, enum_query_type query_type); + void fix_length_and_dec() + { + if (decimals == NOT_FIXED_DEC) + decimals= args[0]->decimals; + Item_temporal_func::fix_length_and_dec(); + } +}; + +class Item_date_typecast :public Item_temporal_typecast { public: - Item_date_typecast(Item *a) :Item_typecast_maybe_null(a) {} + Item_date_typecast(Item *a) :Item_temporal_typecast(a) {} const char *func_name() const { return "cast_as_date"; } - String *val_str(String *str); bool get_date(MYSQL_TIME *ltime, uint fuzzy_date); - bool get_time(MYSQL_TIME *ltime); const char *cast_type() const { return "date"; } enum_field_types field_type() const { return MYSQL_TYPE_DATE; } - Field *tmp_table_field(TABLE *table) - { - return tmp_table_field_from_field_type(table, 0); - } - void fix_length_and_dec() - { - collation.set(&my_charset_bin); - max_length= 10; - maybe_null= 1; - } - bool result_as_longlong() { return TRUE; } - longlong val_int(); - double val_real() { return (double) val_int(); } - my_decimal *val_decimal(my_decimal *decimal_value) - { - DBUG_ASSERT(fixed == 1); - return val_decimal_from_date(decimal_value); - } - int save_in_field(Field *field, bool no_conversions) - { - return save_date_in_field(field); - } }; -class Item_time_typecast :public Item_typecast_maybe_null +class Item_time_typecast :public Item_temporal_typecast { public: - Item_time_typecast(Item *a) :Item_typecast_maybe_null(a) {} + Item_time_typecast(Item *a, uint dec_arg) + :Item_temporal_typecast(a) { decimals= dec_arg; } const char *func_name() const { return "cast_as_time"; } bool get_date(MYSQL_TIME *ltime, uint fuzzy_date); - String *val_str(String *str); - bool get_time(MYSQL_TIME *ltime); const char *cast_type() const { return "time"; } enum_field_types field_type() const { return MYSQL_TYPE_TIME; } - Field *tmp_table_field(TABLE *table) - { - return tmp_table_field_from_field_type(table, 0); - } - void fix_length_and_dec() - { - collation.set(&my_charset_bin); - max_length= 17; - maybe_null= 1; - } - bool result_as_longlong() { return TRUE; } - longlong val_int(); - double val_real() { return val_real_from_decimal(); } - my_decimal *val_decimal(my_decimal *decimal_value) - { - DBUG_ASSERT(fixed == 1); - return val_decimal_from_time(decimal_value); - } - int save_in_field(Field *field, bool no_conversions) - { - return save_time_in_field(field); - } }; -class Item_datetime_typecast :public Item_typecast_maybe_null +class Item_datetime_typecast :public Item_temporal_typecast { public: - Item_datetime_typecast(Item *a) :Item_typecast_maybe_null(a) {} + Item_datetime_typecast(Item *a, uint dec_arg) + :Item_temporal_typecast(a) { decimals= dec_arg; } const char *func_name() const { return "cast_as_datetime"; } - String *val_str(String *str); const char *cast_type() const { return "datetime"; } enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } - Field *tmp_table_field(TABLE *table) - { - return tmp_table_field_from_field_type(table, 0); - } - void fix_length_and_dec() - { - collation.set(&my_charset_bin); - maybe_null= 1; - max_length= MAX_DATETIME_FULL_WIDTH * MY_CHARSET_BIN_MB_MAXLEN; - decimals= DATETIME_DEC; - } - bool result_as_longlong() { return TRUE; } - longlong val_int(); - double val_real() { return val_real_from_decimal(); } - double val() { return (double) val_int(); } - my_decimal *val_decimal(my_decimal *decimal_value) - { - DBUG_ASSERT(fixed == 1); - return val_decimal_from_date(decimal_value); - } - int save_in_field(Field *field, bool no_conversions) - { - return save_date_in_field(field); - } + bool get_date(MYSQL_TIME *ltime, uint fuzzy_date); }; -class Item_func_makedate :public Item_date_func + +class Item_func_makedate :public Item_temporal_func { public: - Item_func_makedate(Item *a,Item *b) :Item_date_func(a,b) {} - String *val_str(String *str); + Item_func_makedate(Item *a,Item *b) :Item_temporal_func(a,b) {} const char *func_name() const { return "makedate"; } enum_field_types field_type() const { return MYSQL_TYPE_DATE; } - void fix_length_and_dec() - { - decimals=0; - max_length=MAX_DATE_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; - /* It returns NULL when the second argument is less or equal to 0 */ - maybe_null= 1; - } - longlong val_int(); + bool get_date(MYSQL_TIME *ltime, uint fuzzy_date); }; -class Item_func_add_time :public Item_str_func +class Item_func_add_time :public Item_temporal_func { const bool is_date; int sign; @@ -1031,63 +897,39 @@ class Item_func_add_time :public Item_str_func public: Item_func_add_time(Item *a, Item *b, bool type_arg, bool neg_arg) - :Item_str_func(a, b), is_date(type_arg) { sign= neg_arg ? -1 : 1; } - String *val_str(String *str); + :Item_temporal_func(a, b), is_date(type_arg) { sign= neg_arg ? -1 : 1; } enum_field_types field_type() const { return cached_field_type; } void fix_length_and_dec(); - - Field *tmp_table_field(TABLE *table) - { - return tmp_table_field_from_field_type(table, 0); - } + bool get_date(MYSQL_TIME *ltime, uint fuzzy_date); virtual void print(String *str, enum_query_type query_type); const char *func_name() const { return "add_time"; } - double val_real() { return val_real_from_decimal(); } - my_decimal *val_decimal(my_decimal *decimal_value) - { - DBUG_ASSERT(fixed == 1); - if (cached_field_type == MYSQL_TYPE_TIME) - return val_decimal_from_time(decimal_value); - if (cached_field_type == MYSQL_TYPE_DATETIME) - return val_decimal_from_date(decimal_value); - return Item_str_func::val_decimal(decimal_value); - } - int save_in_field(Field *field, bool no_conversions) - { - if (cached_field_type == MYSQL_TYPE_TIME) - return save_time_in_field(field); - if (cached_field_type == MYSQL_TYPE_DATETIME) - return save_date_in_field(field); - return Item_str_func::save_in_field(field, no_conversions); - } }; -class Item_func_timediff :public Item_str_timefunc +class Item_func_timediff :public Item_timefunc { public: Item_func_timediff(Item *a, Item *b) - :Item_str_timefunc(a, b) {} - String *val_str(String *str); + :Item_timefunc(a, b) {} const char *func_name() const { return "timediff"; } void fix_length_and_dec() { - Item_str_timefunc::fix_length_and_dec(); - maybe_null= 1; + decimals= max(args[0]->decimals, args[1]->decimals); + Item_timefunc::fix_length_and_dec(); } + bool get_date(MYSQL_TIME *ltime, uint fuzzy_date); }; -class Item_func_maketime :public Item_str_timefunc +class Item_func_maketime :public Item_timefunc { public: Item_func_maketime(Item *a, Item *b, Item *c) - :Item_str_timefunc(a, b, c) - { - maybe_null= TRUE; - } - String *val_str(String *str); + :Item_timefunc(a, b, c) + {} const char *func_name() const { return "maketime"; } + bool get_date(MYSQL_TIME *ltime, uint fuzzy_date); }; + class Item_func_microsecond :public Item_int_func { public: @@ -1149,32 +991,26 @@ public: }; -class Item_func_str_to_date :public Item_str_func +class Item_func_str_to_date :public Item_temporal_func { enum_field_types cached_field_type; - date_time_format_types cached_format_type; timestamp_type cached_timestamp_type; bool const_item; public: Item_func_str_to_date(Item *a, Item *b) - :Item_str_func(a, b), const_item(false) + :Item_temporal_func(a, b), const_item(false) {} - String *val_str(String *str); bool get_date(MYSQL_TIME *ltime, uint fuzzy_date); const char *func_name() const { return "str_to_date"; } enum_field_types field_type() const { return cached_field_type; } void fix_length_and_dec(); - Field *tmp_table_field(TABLE *table) - { - return tmp_table_field_from_field_type(table, 1); - } }; -class Item_func_last_day :public Item_date +class Item_func_last_day :public Item_datefunc { public: - Item_func_last_day(Item *a) :Item_date(a) {} + Item_func_last_day(Item *a) :Item_datefunc(a) {} const char *func_name() const { return "last_day"; } bool get_date(MYSQL_TIME *res, uint fuzzy_date); }; diff --git a/sql/log.cc b/sql/log.cc index 1aeac1814af..56a3647ce97 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -396,7 +396,7 @@ void Log_to_csv_event_handler::cleanup() */ bool Log_to_csv_event_handler:: - log_general(THD *thd, time_t event_time, const char *user_host, + log_general(THD *thd, my_hrtime_t event_time, const char *user_host, uint user_host_len, int thread_id, const char *command_type, uint command_type_len, const char *sql_text, uint sql_text_len, @@ -478,8 +478,8 @@ bool Log_to_csv_event_handler:: DBUG_ASSERT(table->field[0]->type() == MYSQL_TYPE_TIMESTAMP); - ((Field_timestamp*) table->field[0])->store_timestamp((my_time_t) - event_time); + ((Field_timestamp*) table->field[0])->store_TIME( + hrtime_to_my_time(event_time), hrtime_sec_part(event_time)); /* do a write */ if (table->field[1]->store(user_host, user_host_len, client_cs) || @@ -543,7 +543,6 @@ err: log_slow() thd THD of the query current_time current timestamp - query_start_arg command start timestamp user_host the pointer to the string with user@host info user_host_len length of the user_host string. this is computed once and passed to all general log event handlers @@ -566,7 +565,7 @@ err: */ bool Log_to_csv_event_handler:: - log_slow(THD *thd, time_t current_time, time_t query_start_arg, + log_slow(THD *thd, my_hrtime_t current_time, const char *user_host, uint user_host_len, ulonglong query_utime, ulonglong lock_utime, bool is_command, const char *sql_text, uint sql_text_len) @@ -580,6 +579,11 @@ bool Log_to_csv_event_handler:: Open_tables_state open_tables_backup; CHARSET_INFO *client_cs= thd->variables.character_set_client; bool save_time_zone_used; + long query_time= (long) min(query_utime/1000000, TIME_MAX_VALUE_SECONDS); + long lock_time= (long) min(lock_utime/1000000, TIME_MAX_VALUE_SECONDS); + long query_time_micro= (long) (query_utime % 1000000); + long lock_time_micro= (long) (lock_utime % 1000000); + DBUG_ENTER("Log_to_csv_event_handler::log_slow"); thd->push_internal_handler(& error_handler); @@ -621,45 +625,34 @@ bool Log_to_csv_event_handler:: /* store the time and user values */ DBUG_ASSERT(table->field[0]->type() == MYSQL_TYPE_TIMESTAMP); - ((Field_timestamp*) table->field[0])->store_timestamp((my_time_t) - current_time); + ((Field_timestamp*) table->field[0])->store_TIME( + hrtime_to_my_time(current_time), hrtime_sec_part(current_time)); if (table->field[1]->store(user_host, user_host_len, client_cs)) goto err; - if (query_start_arg) - { - longlong query_time= (longlong) (query_utime/1000000); - longlong lock_time= (longlong) (lock_utime/1000000); - /* - A TIME field can not hold the full longlong range; query_time or - lock_time may be truncated without warning here, if greater than - 839 hours (~35 days) - */ - MYSQL_TIME t; - t.neg= 0; + /* + A TIME field can not hold the full longlong range; query_time or + lock_time may be truncated without warning here, if greater than + 839 hours (~35 days) + */ + MYSQL_TIME t; + t.neg= 0; + + /* fill in query_time field */ + calc_time_from_sec(&t, query_time, query_time_micro); + if (table->field[2]->store_time(&t, MYSQL_TIMESTAMP_TIME)) + goto err; + /* lock_time */ + calc_time_from_sec(&t, lock_time, lock_time_micro); + if (table->field[3]->store_time(&t, MYSQL_TIMESTAMP_TIME)) + goto err; + /* rows_sent */ + if (table->field[4]->store((longlong) thd->sent_row_count, TRUE)) + goto err; + /* rows_examined */ + if (table->field[5]->store((longlong) thd->examined_row_count, TRUE)) + goto err; - /* fill in query_time field */ - calc_time_from_sec(&t, (long) min(query_time, (longlong) TIME_MAX_VALUE_SECONDS), 0); - if (table->field[2]->store_time(&t, MYSQL_TIMESTAMP_TIME)) - goto err; - /* lock_time */ - calc_time_from_sec(&t, (long) min(lock_time, (longlong) TIME_MAX_VALUE_SECONDS), 0); - if (table->field[3]->store_time(&t, MYSQL_TIMESTAMP_TIME)) - goto err; - /* rows_sent */ - if (table->field[4]->store((longlong) thd->sent_row_count, TRUE)) - goto err; - /* rows_examined */ - if (table->field[5]->store((longlong) thd->examined_row_count, TRUE)) - goto err; - } - else - { - table->field[2]->set_null(); - table->field[3]->set_null(); - table->field[4]->set_null(); - table->field[5]->set_null(); - } /* fill database field */ if (thd->db) { @@ -796,14 +789,14 @@ void Log_to_file_event_handler::init_pthread_objects() /** Wrapper around MYSQL_LOG::write() for slow log. */ bool Log_to_file_event_handler:: - log_slow(THD *thd, time_t current_time, time_t query_start_arg, + log_slow(THD *thd, my_hrtime_t current_time, const char *user_host, uint user_host_len, ulonglong query_utime, ulonglong lock_utime, bool is_command, const char *sql_text, uint sql_text_len) { Silence_log_table_errors error_handler; thd->push_internal_handler(&error_handler); - bool retval= mysql_slow_log.write(thd, current_time, query_start_arg, + bool retval= mysql_slow_log.write(thd, hrtime_to_my_time(current_time), user_host, user_host_len, query_utime, lock_utime, is_command, sql_text, sql_text_len); @@ -818,7 +811,7 @@ bool Log_to_file_event_handler:: */ bool Log_to_file_event_handler:: - log_general(THD *thd, time_t event_time, const char *user_host, + log_general(THD *thd, my_hrtime_t event_time, const char *user_host, uint user_host_len, int thread_id, const char *command_type, uint command_type_len, const char *sql_text, uint sql_text_len, @@ -826,7 +819,8 @@ bool Log_to_file_event_handler:: { Silence_log_table_errors error_handler; thd->push_internal_handler(&error_handler); - bool retval= mysql_log.write(event_time, user_host, user_host_len, + bool retval= mysql_log.write(hrtime_to_time(event_time), user_host, + user_host_len, thread_id, command_type, command_type_len, sql_text, sql_text_len); thd->pop_internal_handler(); @@ -1035,8 +1029,6 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length, if (*slow_log_handler_list) { - time_t current_time; - /* do not log slow queries from replication threads */ if (thd->slave_thread && !opt_log_slow_slave_statements) return 0; @@ -1056,16 +1048,12 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length, sctx->ip ? sctx->ip : "", "]", NullS) - user_host_buff); - current_time= my_time_possible_from_micro(current_utime); - if (thd->start_utime) - { - query_utime= (current_utime - thd->start_utime); - lock_utime= (thd->utime_after_lock - thd->start_utime); - } - else - { - query_utime= lock_utime= 0; - } + DBUG_ASSERT(thd->start_utime); + DBUG_ASSERT(thd->start_time); + query_utime= (current_utime - thd->start_utime); + lock_utime= (thd->utime_after_lock - thd->start_utime); + my_hrtime_t current_time= { hrtime_from_time(thd->start_time) + + thd->start_time_sec_part + query_utime }; if (!query) { @@ -1086,7 +1074,7 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length, } for (current_handler= slow_log_handler_list; *current_handler ;) - error= (*current_handler++)->log_slow(thd, current_time, thd->start_time, + error= (*current_handler++)->log_slow(thd, current_time, user_host_buff, user_host_len, query_utime, lock_utime, is_command, query, query_length) || error; @@ -1104,7 +1092,7 @@ bool LOGGER::general_log_write(THD *thd, enum enum_server_command command, char user_host_buff[MAX_USER_HOST_SIZE + 1]; Security_context *sctx= thd->security_ctx; uint user_host_len= 0; - time_t current_time; + my_hrtime_t current_time; DBUG_ASSERT(thd); @@ -1121,7 +1109,7 @@ bool LOGGER::general_log_write(THD *thd, enum enum_server_command command, sctx->ip ? sctx->ip : "", "]", NullS) - user_host_buff; - current_time= my_time(0); + current_time= my_hrtime(); while (*current_handler) error|= (*current_handler++)-> log_general(thd, current_time, user_host_buff, @@ -2367,7 +2355,6 @@ err: thd THD of the query current_time current timestamp - query_start_arg command start timestamp user_host the pointer to the string with user@host info user_host_len length of the user_host string. this is computed once and passed to all general log event handlers @@ -2389,7 +2376,7 @@ err: */ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, - time_t query_start_arg, const char *user_host, + const char *user_host, uint user_host_len, ulonglong query_utime, ulonglong lock_utime, bool is_command, const char *sql_text, uint sql_text_len) diff --git a/sql/log.h b/sql/log.h index 6437046b17b..3a6c484a55b 100644 --- a/sql/log.h +++ b/sql/log.h @@ -281,7 +281,7 @@ public: uint user_host_len, int thread_id, const char *command_type, uint command_type_len, const char *sql_text, uint sql_text_len); - bool write(THD *thd, time_t current_time, time_t query_start_arg, + bool write(THD *thd, time_t current_time, const char *user_host, uint user_host_len, ulonglong query_utime, ulonglong lock_utime, bool is_command, const char *sql_text, uint sql_text_len); @@ -581,14 +581,14 @@ public: virtual bool init()= 0; virtual void cleanup()= 0; - virtual bool log_slow(THD *thd, time_t current_time, - time_t query_start_arg, const char *user_host, + virtual bool log_slow(THD *thd, my_hrtime_t current_time, + const char *user_host, uint user_host_len, ulonglong query_utime, ulonglong lock_utime, bool is_command, const char *sql_text, uint sql_text_len)= 0; virtual bool log_error(enum loglevel level, const char *format, va_list args)= 0; - virtual bool log_general(THD *thd, time_t event_time, const char *user_host, + virtual bool log_general(THD *thd, my_hrtime_t event_time, const char *user_host, uint user_host_len, int thread_id, const char *command_type, uint command_type_len, const char *sql_text, uint sql_text_len, @@ -610,14 +610,14 @@ public: virtual bool init(); virtual void cleanup(); - virtual bool log_slow(THD *thd, time_t current_time, - time_t query_start_arg, const char *user_host, + virtual bool log_slow(THD *thd, my_hrtime_t current_time, + const char *user_host, uint user_host_len, ulonglong query_utime, ulonglong lock_utime, bool is_command, const char *sql_text, uint sql_text_len); virtual bool log_error(enum loglevel level, const char *format, va_list args); - virtual bool log_general(THD *thd, time_t event_time, const char *user_host, + virtual bool log_general(THD *thd, my_hrtime_t event_time, const char *user_host, uint user_host_len, int thread_id, const char *command_type, uint command_type_len, const char *sql_text, uint sql_text_len, @@ -642,14 +642,14 @@ public: virtual bool init(); virtual void cleanup(); - virtual bool log_slow(THD *thd, time_t current_time, - time_t query_start_arg, const char *user_host, + virtual bool log_slow(THD *thd, my_hrtime_t current_time, + const char *user_host, uint user_host_len, ulonglong query_utime, ulonglong lock_utime, bool is_command, const char *sql_text, uint sql_text_len); virtual bool log_error(enum loglevel level, const char *format, va_list args); - virtual bool log_general(THD *thd, time_t event_time, const char *user_host, + virtual bool log_general(THD *thd, my_hrtime_t event_time, const char *user_host, uint user_host_len, int thread_id, const char *command_type, uint command_type_len, const char *sql_text, uint sql_text_len, diff --git a/sql/log_event.cc b/sql/log_event.cc index 3219f770858..3dad0c87e00 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -713,7 +713,8 @@ Log_event::Log_event(THD* thd_arg, uint16 flags_arg, bool using_trans) checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF) { server_id= thd->server_id; - when= thd->start_time; + when= thd->start_time; + when_sec_part=thd->start_time_sec_part; cache_stmt= using_trans; } @@ -735,7 +736,8 @@ Log_event::Log_event() We can't call my_time() here as this would cause a call before my_init() is called */ - when= 0; + when= 0; + when_sec_part=0; log_pos= 0; } #endif /* !MYSQL_CLIENT */ @@ -754,6 +756,7 @@ Log_event::Log_event(const char* buf, thd = 0; #endif when = uint4korr(buf); + when_sec_part= 0; server_id = uint4korr(buf + SERVER_ID_OFFSET); data_written= uint4korr(buf + EVENT_LEN_OFFSET); if (description_event->binlog_version==1) @@ -842,21 +845,13 @@ int Log_event::do_update_pos(Relay_log_info *rli) DBUG_EXECUTE_IF("let_first_flush_log_change_timestamp", if (debug_not_change_ts_if_art_event == 1 && is_artificial_event()) - { - debug_not_change_ts_if_art_event= 0; - }); -#ifndef DBUG_OFF - rli->stmt_done(log_pos, - is_artificial_event() && - debug_not_change_ts_if_art_event > 0 ? 0 : when); -#else - rli->stmt_done(log_pos, is_artificial_event()? 0 : when); -#endif + debug_not_change_ts_if_art_event= 0; ); + rli->stmt_done(log_pos, is_artificial_event() + IF_DBUG(&& debug_not_change_ts_if_art_event > 0) ? + 0 : when); DBUG_EXECUTE_IF("let_first_flush_log_change_timestamp", if (debug_not_change_ts_if_art_event == 0) - { - debug_not_change_ts_if_art_event= 2; - }); + debug_not_change_ts_if_art_event= 2; ); } return 0; // Cannot fail currently } @@ -1097,7 +1092,7 @@ bool Log_event::write_header(IO_CACHE* file, ulong event_data_length) log_pos= my_b_safe_tell(file)+data_written; } - now= (ulong) get_time(); // Query start time + now= get_time(); // Query start time /* Header will be of size LOG_EVENT_HEADER_LEN for all events, except for @@ -2020,6 +2015,7 @@ beg: uint64 i64= uint8korr(ptr); /* YYYYMMDDhhmmss */ d= (ulong) (i64 / 1000000); t= (ulong) (i64 % 1000000); + my_b_printf(file, "%04d-%02d-%02d %02d:%02d:%02d", (int) (d / 10000), (int) (d % 10000) / 100, (int) (d % 100), (int) (t / 10000), (int) (t % 10000) / 100, (int) t % 100); @@ -2275,12 +2271,10 @@ end: delete td; } -#ifdef MYSQL_CLIENT void free_table_map_log_event(Table_map_log_event *event) { delete event; } -#endif void Log_event::print_base64(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info, @@ -2363,15 +2357,13 @@ void Log_event::print_base64(IO_CACHE* file, void Log_event::print_timestamp(IO_CACHE* file, time_t* ts) { struct tm *res; + time_t my_when= when; DBUG_ENTER("Log_event::print_timestamp"); if (!ts) - ts = &when; -#ifdef MYSQL_SERVER // This is always false - struct tm tm_tmp; - localtime_r(ts,(res= &tm_tmp)); -#else + { + ts = &my_when; + } res=localtime(ts); -#endif my_b_printf(file,"%02d%02d%02d %2d:%02d:%02d", res->tm_year % 100, @@ -2655,6 +2647,15 @@ bool Query_log_event::write(IO_CACHE* file) memcpy(start, host.str, host.length); start+= host.length; } + + } + + if (thd && thd->query_start_sec_part_used) + { + *start++= Q_HRNOW; + get_time(); + int3store(start, when_sec_part); + start+= 3; } /* NOTE: When adding new status vars, please don't forget to update @@ -2748,7 +2749,7 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, error_code= errcode; - time(&end_time); + end_time= my_time(0); exec_time = (ulong) (end_time - thd_arg->start_time); /** @todo this means that if we have no catalog, then it is replicated @@ -2883,6 +2884,7 @@ code_name(int code) case Q_CHARSET_DATABASE_CODE: return "Q_CHARSET_DATABASE_CODE"; case Q_TABLE_MAP_FOR_UPDATE_CODE: return "Q_TABLE_MAP_FOR_UPDATE_CODE"; case Q_MASTER_DATA_WRITTEN_CODE: return "Q_MASTER_DATA_WRITTEN_CODE"; + case Q_HRNOW: return "Q_HRNOW"; } sprintf(buf, "CODE#%d", code); return buf; @@ -3099,6 +3101,14 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, CHECK_SPACE(pos, end, host.length); host.str= (char *)pos; pos+= host.length; + break; + } + case Q_HRNOW: + { + CHECK_SPACE(pos, end, 3); + when_sec_part= uint3korr(pos); + pos+= 3; + break; } default: /* That's why you must write status vars in growing order of code */ @@ -3178,7 +3188,7 @@ void Query_log_event::print_query_header(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info) { // TODO: print the catalog ?? - char buff[40],*end; // Enough for SET TIMESTAMP + char buff[64], *end; // Enough for SET TIMESTAMP bool different_db= 1; uint32 tmp; @@ -3205,6 +3215,11 @@ void Query_log_event::print_query_header(IO_CACHE* file, } end=int10_to_str((long) when, strmov(buff,"SET TIMESTAMP="),10); + if (when_sec_part) + { + *end++= '.'; + end=int10_to_str(when_sec_part, end, 10); + } end= strmov(end, print_event_info->delimiter); *end++='\n'; my_b_write(file, (uchar*) buff, (uint) (end-buff)); @@ -3466,7 +3481,7 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli, */ if (is_trans_keyword() || rpl_filter->db_ok(thd->db)) { - thd->set_time((time_t)when); + thd->set_time(when, when_sec_part); thd->set_query((char*)query_arg, q_len_arg); VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query_id = next_query_id(); @@ -3913,7 +3928,7 @@ bool Start_log_event_v3::write(IO_CACHE* file) int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version); memcpy(buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN); if (!dont_set_created) - created= when= get_time(); + created= get_time(); // this sets when and when_sec_part as a side effect int4store(buff + ST_CREATED_OFFSET,created); return (write_header(file, sizeof(buff)) || wrapper_my_b_safe_write(file, (uchar*) buff, sizeof(buff)) || @@ -4337,7 +4352,7 @@ bool Format_description_log_event::write(IO_CACHE* file) int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version); memcpy((char*) buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN); if (!dont_set_created) - created= when= get_time(); + created= get_time(); int4store(buff + ST_CREATED_OFFSET,created); buff[ST_COMMON_HEADER_LEN_OFFSET]= LOG_EVENT_HEADER_LEN; memcpy((char*) buff+ST_COMMON_HEADER_LEN_OFFSET + 1, (uchar*) post_header_len, @@ -5145,7 +5160,7 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli, */ if (rpl_filter->db_ok(thd->db)) { - thd->set_time((time_t)when); + thd->set_time(when, when_sec_part); VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query_id = next_query_id(); VOID(pthread_mutex_unlock(&LOCK_thread_count)); @@ -7991,7 +8006,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) TIMESTAMP column to a table with one. So we call set_time(), like in SBR. Presently it changes nothing. */ - thd->set_time((time_t)when); + thd->set_time(when, when_sec_part); /* Now we are in a statement and will stay in a statement until we diff --git a/sql/log_event.h b/sql/log_event.h index 53fc8f986b4..4053db4e3e7 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -267,6 +267,7 @@ struct sql_ex_info 1 + 2 /* type, charset_database_number */ + \ 1 + 8 /* type, table_map_for_update */ + \ 1 + 4 /* type, master_data_written */ + \ + 1 + 3 /* type, sec_part of NOW() */ + \ 1 + 16 + 1 + 60/* type, user_len, user, host_len, host */) #define MAX_LOG_EVENT_HEADER ( /* in order of Query_log_event::write */ \ LOG_EVENT_HEADER_LEN + /* write_header */ \ @@ -338,6 +339,8 @@ struct sql_ex_info #define Q_INVOKER 11 +#define Q_HRNOW 128 + /* Intvar event post-header */ /* Intvar event data */ @@ -920,7 +923,8 @@ public: execution time, which guarantees good replication (otherwise, we could have a query and its event with different timestamps). */ - time_t when; + my_time_t when; + ulong when_sec_part; /* The number of seconds the query took to run on the master. */ ulong exec_time; /* Number of bytes written by write() function */ @@ -1071,16 +1075,28 @@ public: { return 0; } virtual bool write_data_body(IO_CACHE* file __attribute__((unused))) { return 0; } - inline time_t get_time() + inline my_time_t get_time() { THD *tmp_thd; if (when) return when; if (thd) - return thd->start_time; + { + when= thd->start_time; + when_sec_part= thd->start_time_sec_part; + return when; + } + /* thd will only be 0 here at time of log creation */ if ((tmp_thd= current_thd)) - return tmp_thd->start_time; - return my_time(0); + { + when= tmp_thd->start_time; + when_sec_part= tmp_thd->start_time_sec_part; + return when; + } + my_hrtime_t hrtime= my_hrtime(); + when= hrtime_to_my_time(hrtime); + when_sec_part= hrtime_sec_part(hrtime); + return when; } #endif virtual Log_event_type get_type_code() = 0; diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index 1c46e31d128..06ad5cc3f93 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -159,7 +159,7 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info TIMESTAMP column to a table with one. So we call set_time(), like in SBR. Presently it changes nothing. */ - ev_thd->set_time((time_t)ev->when); + ev_thd->set_time(ev->when, ev->when_sec_part); /* There are a few flags that are replicated with each row event. Make sure to set/clear them before executing the main body of @@ -1660,7 +1660,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli) TIMESTAMP column to a table with one. So we call set_time(), like in SBR. Presently it changes nothing. */ - thd->set_time((time_t)when); + thd->set_time(when, when_sec_part); /* There are a few flags that are replicated with each row event. Make sure to set/clear them before executing the main body of diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 36cbbe71f80..29b6aa88740 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -830,9 +830,95 @@ typedef my_bool (*qc_engine_callback)(THD *thd, char *table_key, uint key_length, ulonglong *engine_data); #include "sql_string.h" +#include "my_decimal.h" + +/* + to unify the code that differs only in the argument passed to the + error message (string vs. number) + + We pass this container around, and only convert the number + to a string when necessary. +*/ +class Lazy_string +{ +public: + Lazy_string() {} + virtual ~Lazy_string() {} + virtual void copy_to(String *str) const = 0; +}; + +class Lazy_string_str : public Lazy_string +{ + const char *str; + size_t len; +public: + Lazy_string_str(const char *str_arg, size_t len_arg) + : Lazy_string(), str(str_arg), len(len_arg) {} + void copy_to(String *dst) const + { dst->copy(str, len, system_charset_info); } +}; + +class Lazy_string_num : public Lazy_string +{ + longlong num; +public: + Lazy_string_num(longlong num_arg) : Lazy_string(), num(num_arg) {} + void copy_to(String *dst) const { dst->set(num, &my_charset_bin); } +}; + +class Lazy_string_decimal : public Lazy_string +{ + my_decimal num; +public: + Lazy_string_decimal(my_decimal num_arg) : Lazy_string(), num(num_arg) + { + num.fix_buffer_pointer(); + } + void copy_to(String *dst) const + { + my_decimal2string(E_DEC_FATAL_ERROR, &num, 0, 0, '0', dst); + } +}; + +class Lazy_string_double: public Lazy_string +{ + double num; +public: + Lazy_string_double(double num_arg) : Lazy_string(), num(num_arg) {} + void copy_to(String *dst) const + { dst->set_real(num, NOT_FIXED_DEC, &my_charset_bin); } +}; + +class Lazy_string_time : public Lazy_string +{ + const MYSQL_TIME *ltime; +public: + Lazy_string_time(const MYSQL_TIME *ltime_arg) + : Lazy_string(), ltime(ltime_arg) {} + void copy_to(String *dst) const + { + dst->alloc(MAX_DATETIME_FULL_WIDTH); + dst->length((uint) my_TIME_to_str(ltime, (char*) dst->ptr(), + AUTO_SEC_PART_DIGITS)); + dst->set_charset(&my_charset_bin); + } +}; + +static inline enum enum_mysql_timestamp_type +mysql_type_to_time_type(enum enum_field_types mysql_type) +{ + switch (mysql_type) { + case MYSQL_TYPE_TIME: return MYSQL_TIMESTAMP_TIME; + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATETIME: return MYSQL_TIMESTAMP_DATETIME; + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_DATE: return MYSQL_TIMESTAMP_DATE; + default: return MYSQL_TIMESTAMP_ERROR; + } +} + #include "sql_list.h" #include "sql_map.h" -#include "my_decimal.h" #include "handler.h" #include "parse_file.h" #include "table.h" @@ -856,6 +942,7 @@ typedef Comp_creator* (*chooser_compare_func_creator)(bool invert); #endif #include "item.h" extern my_decimal decimal_zero; +extern my_decimal max_seconds_for_time_type, time_second_part_factor; /* sql_parse.cc */ void free_items(Item *item); @@ -2357,7 +2444,7 @@ ulong convert_period_to_month(ulong period); ulong convert_month_to_period(ulong month); void get_date_from_daynr(long daynr,uint *year, uint *month, uint *day); -my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, my_bool *not_exist); +my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error); bool str_to_time_with_warn(const char *str,uint length,MYSQL_TIME *l_time, ulong fuzzydate); timestamp_type str_to_datetime_with_warn(const char *str, uint length, @@ -2367,14 +2454,23 @@ void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds); void make_truncated_value_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, - const char *str_val, - uint str_length, timestamp_type time_type, + const Lazy_string *str_val, + timestamp_type time_type, const char *field_name); bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime, ulong fuzzy_date); bool decimal_to_datetime_with_warn(decimal_t *value, MYSQL_TIME *ltime, ulong fuzzy_date); +static inline void make_truncated_value_warning(THD *thd, + MYSQL_ERROR::enum_warning_level level, const char *str_val, + uint str_length, timestamp_type time_type, + const char *field_name) +{ + const Lazy_string_str str(str_val, str_length); + make_truncated_value_warning(thd, level, &str, time_type, field_name); +} + bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL interval); bool calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, longlong *seconds_out, long *microseconds_out); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 9fb9e2bd1c9..9ab78bf3543 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -151,6 +151,7 @@ static event_handle_t eh; static Report_t ref; static void *refneb= NULL; my_bool event_flag= FALSE; + static int volumeid= -1; /* NEB event callback */ @@ -433,12 +434,13 @@ bool opt_large_files= sizeof(my_off_t) > 4; */ static my_bool opt_help= 0, opt_verbose= 0; -arg_cmp_func Arg_comparator::comparator_matrix[5][2] = +arg_cmp_func Arg_comparator::comparator_matrix[6][2] = {{&Arg_comparator::compare_string, &Arg_comparator::compare_e_string}, {&Arg_comparator::compare_real, &Arg_comparator::compare_e_real}, {&Arg_comparator::compare_int_signed, &Arg_comparator::compare_e_int}, {&Arg_comparator::compare_row, &Arg_comparator::compare_e_row}, - {&Arg_comparator::compare_decimal, &Arg_comparator::compare_e_decimal}}; + {&Arg_comparator::compare_decimal, &Arg_comparator::compare_e_decimal}, + {&Arg_comparator::compare_datetime, &Arg_comparator::compare_e_datetime}}; const char *log_output_names[] = { "NONE", "FILE", "TABLE", NullS}; static const unsigned int log_output_names_len[]= { 4, 4, 5, 0 }; @@ -457,6 +459,13 @@ static my_bool opt_debugging= 0, opt_external_locking= 0, opt_console= 0; static my_bool opt_short_log_format= 0; static my_bool opt_ignore_wrong_options= 0, opt_expect_abort= 0; static my_bool opt_sync= 0, opt_thread_alarm; +/* + Set this to 1 if you want to that 'strict mode' should affect all date + operations. If this is 0, then date checking is only done when storing + dates into a table. +*/ +my_bool strict_date_checking= 0; + static uint kill_cached_threads, wake_thread; ulong thread_created; uint thread_handling; @@ -710,6 +719,8 @@ const char *in_additional_cond= "<IN COND>"; const char *in_having_cond= "<IN HAVING>"; my_decimal decimal_zero; +my_decimal max_seconds_for_time_type, time_second_part_factor; + /* classes for comparation parsing/processing */ Eq_creator eq_creator; Ne_creator ne_creator; @@ -2112,7 +2123,7 @@ static bool cache_thread() this thread for handling of new THD object/connection. */ thd->mysys_var->abort= 0; - thd->thr_create_utime= my_micro_time(); + thd->thr_create_utime= microsecond_interval_timer(); threads.append(thd); return(1); } @@ -3469,7 +3480,6 @@ static int init_common_variables(const char *conf_file_name, int argc, char buff[FN_REFLEN], *s; const char *basename; umask(((~my_umask) & 0666)); - my_decimal_set_zero(&decimal_zero); // set decimal_zero constant; tzset(); // Set tzname max_system_variables.pseudo_thread_id= (ulong)~0; @@ -5183,9 +5193,9 @@ void handle_connection_in_main_thread(THD *thd) safe_mutex_assert_owner(&LOCK_thread_count); thread_cache_size=0; // Safety threads.append(thd); - thd->start_utime= my_micro_time(); + thd->set_time(my_hrtime()); pthread_mutex_unlock(&LOCK_thread_count); - thd->start_utime= my_micro_time(); + thd->start_utime= microsecond_interval_timer(); handle_one_connection(thd); } @@ -5211,7 +5221,7 @@ void create_thread_to_handle_connection(THD *thd) thread_created++; threads.append(thd); DBUG_PRINT("info",(("creating thread %lu"), thd->thread_id)); - thd->prior_thr_create_utime= thd->start_utime= my_micro_time(); + thd->prior_thr_create_utime= thd->start_utime= microsecond_interval_timer(); if ((error=pthread_create(&thd->real_id,&connection_attrib, handle_one_connection, (void*) thd))) @@ -8503,6 +8513,13 @@ static int mysql_init_variables(void) /* set key_cache_hash.default_value = dflt_key_cache */ multi_keycache_init(); + /* Useful MariaDB variables */ + double2decimal((double) TIME_MAX_VALUE_SECONDS + + TIME_MAX_SECOND_PART/(double)TIME_SECOND_PART_FACTOR, + &max_seconds_for_time_type); + longlong2decimal(TIME_SECOND_PART_FACTOR, &time_second_part_factor); + my_decimal_set_zero(&decimal_zero); // set decimal_zero constant; + /* Set directory paths */ strmake(language, LANGUAGE, sizeof(language)-1); strmake(mysql_real_data_home, get_relative_path(MYSQL_DATADIR), diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 13afffd6ba9..fba2fb7d7c0 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -7392,7 +7392,6 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field, SEL_ARG *tree= 0; MEM_ROOT *alloc= param->mem_root; uchar *str; - ulong orig_sql_mode; int err; DBUG_ENTER("get_mm_leaf"); @@ -7560,16 +7559,8 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field, We can't always use indexes when comparing a string index to a number cmp_type() is checked to allow compare of dates to numbers */ - if (field->result_type() == STRING_RESULT && - value->result_type() != STRING_RESULT && - field->cmp_type() != value->result_type()) + if (field->cmp_type() == STRING_RESULT && value->cmp_type() != STRING_RESULT) goto end; - /* For comparison purposes allow invalid dates like 2000-01-32 */ - orig_sql_mode= field->table->in_use->variables.sql_mode; - if (value->real_item()->type() == Item::STRING_ITEM && - (field->type() == MYSQL_TYPE_DATE || - field->type() == MYSQL_TYPE_DATETIME)) - field->table->in_use->variables.sql_mode|= MODE_INVALID_DATES; err= value->save_in_field_no_warnings(field, 1); if (err > 0) { @@ -7581,7 +7572,6 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field, { tree= new (alloc) SEL_ARG(field, 0, 0); tree->type= SEL_ARG::IMPOSSIBLE; - field->table->in_use->variables.sql_mode= orig_sql_mode; goto end; } else @@ -7615,10 +7605,7 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field, */ } else - { - field->table->in_use->variables.sql_mode= orig_sql_mode; goto end; - } } } @@ -7641,12 +7628,10 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field, } else if (err < 0) { - field->table->in_use->variables.sql_mode= orig_sql_mode; /* This happens when we try to insert a NULL field in a not null column */ tree= &null_element; // cmp with NULL is never TRUE goto end; } - field->table->in_use->variables.sql_mode= orig_sql_mode; /* Any sargable predicate except "<=>" involving NULL as a constant is always diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index dfb7e8abfa1..6f980695020 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -356,29 +356,27 @@ bool subquery_types_allow_materialization(Item_in_subselect *in_subs) Item *inner= it++; all_are_fields &= (outer->real_item()->type() == Item::FIELD_ITEM && inner->real_item()->type() == Item::FIELD_ITEM); - if (outer->result_type() != inner->result_type()) + if (outer->cmp_type() != inner->cmp_type()) DBUG_RETURN(FALSE); - switch (outer->result_type()) { + switch (outer->cmp_type()) { case STRING_RESULT: - if (outer->is_datetime() != inner->is_datetime()) + if (!(outer->collation.collation == inner->collation.collation)) DBUG_RETURN(FALSE); - - if (!(outer->collation.collation == inner->collation.collation - /*&& outer->max_length <= inner->max_length */)) + // Materialization does not work with BLOB columns + if (inner->field_type() == MYSQL_TYPE_BLOB || + inner->field_type() == MYSQL_TYPE_GEOMETRY) + DBUG_RETURN(FALSE); + break; + case TIME_RESULT: + if (mysql_type_to_time_type(outer->field_type()) != + mysql_type_to_time_type(outer->field_type())) DBUG_RETURN(FALSE); - /*case INT_RESULT: - if (!(outer->unsigned_flag ^ inner->unsigned_flag)) - DBUG_RETURN(FALSE); */ default: - ;/* suitable for materialization */ + /* suitable for materialization */ + break; } - - // Materialization does not work with BLOB columns - if (inner->field_type() == MYSQL_TYPE_BLOB || - inner->field_type() == MYSQL_TYPE_GEOMETRY) - DBUG_RETURN(FALSE); } - + in_subs->types_allow_materialization= TRUE; in_subs->sjm_scan_allowed= all_are_fields; DBUG_PRINT("info",("subquery_types_allow_materialization: ok, allowed")); @@ -2746,7 +2744,6 @@ TABLE *create_duplicate_weedout_tmp_table(THD *thd, init_tmp_table_share(thd, share, "", 0, tmpname, tmpname); share->blob_field= blob_field; share->blob_ptr_size= portable_sizeof_char_ptr; - share->db_low_byte_first=1; // True for HEAP and MyISAM share->table_charset= NULL; share->primary_key= MAX_KEY; // Indicate no primary key share->keys_for_keyread.init(); diff --git a/sql/protocol.cc b/sql/protocol.cc index 4a3220b96fc..a0e14423b73 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -1041,13 +1041,7 @@ bool Protocol_text::store(Field *field) } -/** - @todo - Second_part format ("%06") needs to change when - we support 0-6 decimals for time. -*/ - -bool Protocol_text::store(MYSQL_TIME *tm) +bool Protocol_text::store(MYSQL_TIME *tm, int decimals) { #ifndef DBUG_OFF DBUG_ASSERT(field_types == 0 || @@ -1055,18 +1049,8 @@ bool Protocol_text::store(MYSQL_TIME *tm) field_types[field_pos] == MYSQL_TYPE_TIMESTAMP); field_pos++; #endif - char buff[40]; - uint length; - length= my_sprintf(buff,(buff, "%04d-%02d-%02d %02d:%02d:%02d", - (int) tm->year, - (int) tm->month, - (int) tm->day, - (int) tm->hour, - (int) tm->minute, - (int) tm->second)); - if (tm->second_part) - length+= my_sprintf(buff+length,(buff+length, ".%06d", - (int)tm->second_part)); + char buff[MAX_DATE_STRING_REP_LENGTH]; + uint length= my_datetime_to_str(tm, buff, decimals); return net_store_data((uchar*) buff, length); } @@ -1084,29 +1068,15 @@ bool Protocol_text::store_date(MYSQL_TIME *tm) } -/** - @todo - Second_part format ("%06") needs to change when - we support 0-6 decimals for time. -*/ - -bool Protocol_text::store_time(MYSQL_TIME *tm) +bool Protocol_text::store_time(MYSQL_TIME *tm, int decimals) { #ifndef DBUG_OFF DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_TIME); field_pos++; #endif - char buff[40]; - uint length; - uint day= (tm->year || tm->month) ? 0 : tm->day; - length= my_sprintf(buff,(buff, "%s%02ld:%02d:%02d", - tm->neg ? "-" : "", - (long) day*24L+(long) tm->hour, - (int) tm->minute, - (int) tm->second)); - if (tm->second_part) - length+= my_sprintf(buff+length,(buff+length, ".%06d", (int)tm->second_part)); + char buff[MAX_DATE_STRING_REP_LENGTH]; + uint length= my_time_to_str(tm, buff, decimals); return net_store_data((uchar*) buff, length); } @@ -1263,7 +1233,7 @@ bool Protocol_binary::store(Field *field) } -bool Protocol_binary::store(MYSQL_TIME *tm) +bool Protocol_binary::store(MYSQL_TIME *tm, int decimals) { char buff[12],*pos; uint length; @@ -1276,6 +1246,10 @@ bool Protocol_binary::store(MYSQL_TIME *tm) pos[4]= (uchar) tm->hour; pos[5]= (uchar) tm->minute; pos[6]= (uchar) tm->second; + DBUG_ASSERT(decimals == AUTO_SEC_PART_DIGITS || + (decimals >= 0 && decimals <= TIME_SECOND_PART_DIGITS)); + if (decimals != AUTO_SEC_PART_DIGITS) + tm->second_part= sec_part_truncate(tm->second_part, decimals); int4store(pos+7, tm->second_part); if (tm->second_part) length=11; @@ -1293,11 +1267,11 @@ bool Protocol_binary::store_date(MYSQL_TIME *tm) { tm->hour= tm->minute= tm->second=0; tm->second_part= 0; - return Protocol_binary::store(tm); + return Protocol_binary::store(tm, 0); } -bool Protocol_binary::store_time(MYSQL_TIME *tm) +bool Protocol_binary::store_time(MYSQL_TIME *tm, int decimals) { char buff[13], *pos; uint length; @@ -1306,7 +1280,6 @@ bool Protocol_binary::store_time(MYSQL_TIME *tm) pos[0]= tm->neg ? 1 : 0; if (tm->hour >= 24) { - /* Fix if we come from Item::send */ uint days= tm->hour/24; tm->hour-= days*24; tm->day+= days; @@ -1315,6 +1288,10 @@ bool Protocol_binary::store_time(MYSQL_TIME *tm) pos[5]= (uchar) tm->hour; pos[6]= (uchar) tm->minute; pos[7]= (uchar) tm->second; + DBUG_ASSERT(decimals == AUTO_SEC_PART_DIGITS || + (decimals >= 0 && decimals <= TIME_SECOND_PART_DIGITS)); + if (decimals != AUTO_SEC_PART_DIGITS) + tm->second_part= sec_part_truncate(tm->second_part, decimals); int4store(pos+8, tm->second_part); if (tm->second_part) length=12; diff --git a/sql/protocol.h b/sql/protocol.h index 3eb5091a7c7..4ab5c169928 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -91,9 +91,9 @@ public: CHARSET_INFO *fromcs, CHARSET_INFO *tocs)=0; virtual bool store(float from, uint32 decimals, String *buffer)=0; virtual bool store(double from, uint32 decimals, String *buffer)=0; - virtual bool store(MYSQL_TIME *time)=0; + virtual bool store(MYSQL_TIME *time, int decimals)=0; virtual bool store_date(MYSQL_TIME *time)=0; - virtual bool store_time(MYSQL_TIME *time)=0; + virtual bool store_time(MYSQL_TIME *time, int decimals)=0; virtual bool store(Field *field)=0; #ifdef EMBEDDED_LIBRARY int begin_dataset(); @@ -130,9 +130,9 @@ public: virtual bool store(const char *from, size_t length, CHARSET_INFO *cs); virtual bool store(const char *from, size_t length, CHARSET_INFO *fromcs, CHARSET_INFO *tocs); - virtual bool store(MYSQL_TIME *time); + virtual bool store(MYSQL_TIME *time, int decimals); virtual bool store_date(MYSQL_TIME *time); - virtual bool store_time(MYSQL_TIME *time); + virtual bool store_time(MYSQL_TIME *time, int decimals); virtual bool store(float nr, uint32 decimals, String *buffer); virtual bool store(double from, uint32 decimals, String *buffer); virtual bool store(Field *field); @@ -165,9 +165,9 @@ public: virtual bool store(const char *from, size_t length, CHARSET_INFO *cs); virtual bool store(const char *from, size_t length, CHARSET_INFO *fromcs, CHARSET_INFO *tocs); - virtual bool store(MYSQL_TIME *time); + virtual bool store(MYSQL_TIME *time, int decimals); virtual bool store_date(MYSQL_TIME *time); - virtual bool store_time(MYSQL_TIME *time); + virtual bool store_time(MYSQL_TIME *time, int decimals); virtual bool store(float nr, uint32 decimals, String *buffer); virtual bool store(double from, uint32 decimals, String *buffer); virtual bool store(Field *field); diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc index 35e283a886e..d07aeddc5b9 100644 --- a/sql/rpl_record.cc +++ b/sql/rpl_record.cc @@ -103,7 +103,7 @@ pack_row(TABLE *table, MY_BITMAP const* cols, const uchar *old_pack_ptr= pack_ptr; #endif pack_ptr= field->pack(pack_ptr, field->ptr + offset, - field->max_data_length(), TRUE); + field->max_data_length()); DBUG_PRINT("debug", ("field: %s; pack_ptr: 0x%lx;" " pack_ptr':0x%lx; bytes: %d", field->field_name, (ulong) old_pack_ptr, @@ -269,7 +269,7 @@ unpack_row(Relay_log_info const *rli, #ifndef DBUG_OFF uchar const *const old_pack_ptr= pack_ptr; #endif - pack_ptr= f->unpack(f->ptr, pack_ptr, metadata, TRUE); + pack_ptr= f->unpack(f->ptr, pack_ptr, metadata); DBUG_PRINT("debug", ("field: %s; metadata: 0x%x;" " pack_ptr: 0x%lx; pack_ptr': 0x%lx; bytes: %d", f->field_name, metadata, diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index c11bd268256..c88a0908356 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -1175,11 +1175,7 @@ void Relay_log_info::stmt_done(my_off_t event_master_log_pos, is that value may take some time to display in Seconds_Behind_Master - not critical). */ -#ifndef DBUG_OFF - if (!(event_creation_time == 0 && debug_not_change_ts_if_art_event > 0)) -#else - if (event_creation_time != 0) -#endif + if (!(event_creation_time == 0 IF_DBUG(&& debug_not_change_ts_if_art_event > 0))) last_master_timestamp= event_creation_time; } } diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc index c301dabcdb7..68dd0e13c25 100644 --- a/sql/rpl_utility.cc +++ b/sql/rpl_utility.cc @@ -135,7 +135,7 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const always read the length in little-endian order. */ Field_blob fb(m_field_metadata[col]); - length= fb.get_packed_size(master_data, TRUE); + length= fb.get_packed_size(master_data); #else /* Compute the length of the data. We cannot use get_length() here diff --git a/sql/set_var.cc b/sql/set_var.cc index 53205c48a1d..aac6746da82 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -2911,38 +2911,37 @@ int set_var_collation_client::update(THD *thd) bool sys_var_timestamp::check(THD *thd, set_var *var) { - longlong val; - var->save_result.ulonglong_value= var->value->val_int(); - val= (longlong) var->save_result.ulonglong_value; - if (val != 0 && // this is how you set the default value - (val < TIMESTAMP_MIN_VALUE || val > TIMESTAMP_MAX_VALUE)) + double val= var->value->val_real(); + if (val < 0 || val > MY_TIME_T_MAX) { char buf[64]; my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "timestamp", llstr(val, buf)); return TRUE; } + var->save_result.ulonglong_value= hrtime_from_time(val); return FALSE; } bool sys_var_timestamp::update(THD *thd, set_var *var) { - thd->set_time((time_t) var->save_result.ulonglong_value); + my_hrtime_t hrtime = { var->save_result.ulonglong_value }; + thd->set_time(hrtime); return FALSE; } void sys_var_timestamp::set_default(THD *thd, enum_var_type type) { - thd->user_time=0; + thd->user_time.val= 0; } uchar *sys_var_timestamp::value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) { - thd->sys_var_tmp.long_value= (long) thd->start_time; - return (uchar*) &thd->sys_var_tmp.long_value; + thd->sys_var_tmp.double_value= thd->start_time + thd->start_time_sec_part/1e6; + return (uchar*) &thd->sys_var_tmp.double_value; } @@ -3195,7 +3194,7 @@ void sys_var_thd_lc_time_names::set_default(THD *thd, enum_var_type type) } /* - Handling of microseoncds given as seconds.part_seconds + Handling of microseconds given as seconds.part_seconds NOTES The argument to long query time is in seconds in decimal @@ -3241,10 +3240,10 @@ void sys_var_microseconds::set_default(THD *thd, enum_var_type type) uchar *sys_var_microseconds::value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) { - thd->tmp_double_value= (double) ((type == OPT_GLOBAL) ? + thd->sys_var_tmp.double_value= (double) ((type == OPT_GLOBAL) ? global_system_variables.*offset : thd->variables.*offset) / 1000000.0; - return (uchar*) &thd->tmp_double_value; + return (uchar*) &thd->sys_var_tmp.double_value; } diff --git a/sql/set_var.h b/sql/set_var.h index d2bb1e94d33..c591ab5b866 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -680,8 +680,12 @@ public: void set_default(THD *thd, enum_var_type type); bool check_type(enum_var_type type) { return type == OPT_GLOBAL; } bool check_default(enum_var_type type) { return 0; } - SHOW_TYPE show_type() { return SHOW_LONG; } + SHOW_TYPE show_type() { return SHOW_DOUBLE; } uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); + virtual bool check_update_type(Item_result type) + { + return type != INT_RESULT && type != REAL_RESULT && type != DECIMAL_RESULT; + } }; diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 4e8d416ac25..b0989388028 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5513,11 +5513,11 @@ ER_SP_NO_RECURSION eng "Recursive stored functions and triggers are not allowed." ger "Rekursive gespeicherte Routinen und Triggers sind nicht erlaubt" ER_TOO_BIG_SCALE 42000 S1009 - eng "Too big scale %d specified for column '%-.192s'. Maximum is %lu." - ger "Zu großer Skalierungsfaktor %d für Feld '%-.192s' angegeben. Maximum ist %lu" + eng "Too big scale %u specified for '%-.192s'. Maximum is %lu." + ger "Zu großer Skalierungsfaktor %u für '%-.192s' angegeben. Maximum ist %lu" ER_TOO_BIG_PRECISION 42000 S1009 - eng "Too big precision %d specified for column '%-.192s'. Maximum is %lu." - ger "Zu große Genauigkeit %d für Feld '%-.192s' angegeben. Maximum ist %lu" + eng "Too big precision %u specified for '%-.192s'. Maximum is %lu." + ger "Zu große Genauigkeit %u für '%-.192s' angegeben. Maximum ist %lu" ER_M_BIGGER_THAN_D 42000 S1009 eng "For float(M,D), double(M,D) or decimal(M,D), M must be >= D (column '%-.192s')." ger "Für FLOAT(M,D), DOUBLE(M,D) oder DECIMAL(M,D) muss M >= D sein (Feld '%-.192s')" @@ -5555,8 +5555,8 @@ ER_WARN_CANT_DROP_DEFAULT_KEYCACHE eng "Cannot drop default keycache" ger "Der vorgabemäßige Schlüssel-Cache kann nicht gelöscht werden" ER_TOO_BIG_DISPLAYWIDTH 42000 S1009 - eng "Display width out of range for column '%-.192s' (max = %lu)" - ger "Anzeigebreite außerhalb des zulässigen Bereichs für Spalte '%-.192s' (Maximum: %lu)" + eng "Display width out of range for '%-.192s' (max = %lu)" + ger "Anzeigebreite außerhalb des zulässigen Bereichs für '%-.192s' (Maximum: %lu)" ER_XAER_DUPID XAE08 eng "XAER_DUPID: The XID already exists" ger "XAER_DUPID: Die XID existiert bereits" diff --git a/sql/slave.cc b/sql/slave.cc index 4f3b0370744..cfa6c9fdc56 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -2260,7 +2260,11 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli) thd->set_time(); // time the query thd->lex->current_select= 0; if (!ev->when) - ev->when= my_time(0); + { + my_hrtime_t hrtime= my_hrtime(); + ev->when= hrtime_to_my_time(hrtime); + ev->when_sec_part= hrtime_sec_part(hrtime); + } ev->thd = thd; // because up to this point, ev->thd == 0 int reason= ev->shall_skip(rli); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index dbaeb9467ae..8f9ff5512c5 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -49,19 +49,7 @@ static void reset_start_time_for_sp(THD *thd) constant during the execution of those. */ if (!thd->in_sub_stmt) - { - /* - First investigate if there is a cached time stamp - */ - if (thd->user_time) - { - thd->start_time= thd->user_time; - } - else - { - my_micro_time_and_time(&thd->start_time); - } - } + thd->set_time(); } Item_result diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 17fffffa227..cf52b4008f3 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -663,7 +663,7 @@ THD::THD() /* statement id */ 0), Open_tables_state(refresh_version), rli_fake(0), lock_id(&main_lock_id), - user_time(0), in_sub_stmt(0), + in_sub_stmt(0), sql_log_bin_toplevel(false), binlog_table_maps(0), binlog_flags(0UL), table_map_for_update(0), @@ -705,7 +705,7 @@ THD::THD() main_security_ctx.init(); security_ctx= &main_security_ctx; some_tables_deleted=no_errors=password= 0; - query_start_used= 0; + query_start_used= query_start_sec_part_used= 0; count_cuted_fields= CHECK_FIELD_IGNORE; killed= NOT_KILLED; col_access=0; @@ -722,7 +722,7 @@ THD::THD() #endif // Must be reset to handle error with THD's created for init of mysqld lex->current_select= 0; - start_time=(time_t) 0; + user_time.val= start_time= start_time_sec_part= 0; start_utime= prior_thr_create_utime= 0L; utime_after_lock= 0L; current_linfo = 0; @@ -986,7 +986,6 @@ void THD::update_stats(void) void THD::update_all_stats() { - time_t save_time; ulonglong end_cpu_time, end_utime; double busy_time, cpu_time; @@ -995,8 +994,8 @@ void THD::update_all_stats() return; end_cpu_time= my_getcputime(); - end_utime= my_micro_time_and_time(&save_time); - busy_time= (end_utime - start_utime) / 1000000.0; + end_utime= microsecond_interval_timer(); + busy_time= (end_utime - start_utime) / 1000000.0; cpu_time= (end_cpu_time - start_cpu_time) / 10000000.0; /* In case there are bad values, 2629743 is the #seconds in a month. */ if (cpu_time > 2629743.0) @@ -1004,7 +1003,8 @@ void THD::update_all_stats() status_var_add(status_var.cpu_time, cpu_time); status_var_add(status_var.busy_time, busy_time); - update_global_user_stats(this, TRUE, save_time); + update_global_user_stats(this, TRUE, my_time(0)); + // Has to be updated after update_global_user_stats() userstat_running= 0; } @@ -2564,6 +2564,7 @@ int select_max_min_finder_subselect::send_data(List<Item> &items) op= &select_max_min_finder_subselect::cmp_decimal; break; case ROW_RESULT: + case TIME_RESULT: case IMPOSSIBLE_RESULT: // This case should never be choosen DBUG_ASSERT(0); diff --git a/sql/sql_class.h b/sql/sql_class.h index 958e16b9361..23541f20b09 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1525,7 +1525,6 @@ public: */ const char *where; - double tmp_double_value; /* Used in set_var.cc */ ulong client_capabilities; /* What the client supports */ ulong max_client_packet_length; @@ -1549,7 +1548,9 @@ public: uint32 file_id; // for LOAD DATA INFILE /* remote (peer) port */ uint16 peer_port; - time_t start_time, user_time; + my_time_t start_time; // start_time and its sec_part + ulong start_time_sec_part; // are almost always used separately + my_hrtime_t user_time; // track down slow pthread_create ulonglong prior_thr_create_utime, thr_create_utime; ulonglong start_utime, utime_after_lock; @@ -1984,6 +1985,7 @@ public: */ bool is_fatal_sub_stmt_error; bool query_start_used, rand_used, time_zone_used; + bool query_start_sec_part_used; /* for IS NULL => = last_insert_id() fix in remove_eq_conds() */ bool substitute_null_with_insert_id; bool in_lock_tables; @@ -2044,6 +2046,7 @@ public: long long_value; ulong ulong_value; ulonglong ulonglong_value; + double double_value; } sys_var_tmp; struct { @@ -2182,30 +2185,41 @@ public: proc_info = old_msg; pthread_mutex_unlock(&mysys_var->mutex); } - inline time_t query_start() { query_start_used=1; return start_time; } + inline my_time_t query_start() { query_start_used=1; return start_time; } + inline ulong query_start_sec_part() + { query_start_sec_part_used=1; return start_time_sec_part; } + inline void set_current_time() + { + my_hrtime_t hrtime= my_hrtime(); + start_time= hrtime_to_my_time(hrtime); + start_time_sec_part= hrtime_sec_part(hrtime); + } inline void set_time() { - if (user_time) + if (user_time.val) { - start_time= user_time; - start_utime= utime_after_lock= my_micro_time(); + start_time= hrtime_to_my_time(user_time); + start_time_sec_part= hrtime_sec_part(user_time); } else - start_utime= utime_after_lock= my_micro_time_and_time(&start_time); + set_current_time(); + start_utime= utime_after_lock= microsecond_interval_timer(); } - inline void set_current_time() { start_time= my_time(MY_WME); } - inline void set_time(time_t t) + inline void set_time(my_hrtime_t t) { - start_time= user_time= t; - start_utime= utime_after_lock= my_micro_time(); + user_time= t; + start_time= hrtime_to_my_time(user_time); + start_time_sec_part= hrtime_sec_part(user_time); + start_utime= utime_after_lock= microsecond_interval_timer(); } - /*TODO: this will be obsolete when we have support for 64 bit my_time_t */ - inline bool is_valid_time() - { - return (IS_TIME_T_VALID_FOR_TIMESTAMP(start_time)); + inline void set_time(my_time_t t, ulong sec_part) + { + my_hrtime_t hrtime= { hrtime_from_time(t) + sec_part }; + set_time(hrtime); } - void set_time_after_lock() { utime_after_lock= my_micro_time(); } - ulonglong current_utime() { return my_micro_time(); } + void set_time_after_lock() { utime_after_lock= microsecond_interval_timer(); } + ulonglong current_utime() { return microsecond_interval_timer(); } + inline ulonglong found_rows(void) { return limit_found_rows; @@ -2642,6 +2656,27 @@ my_eof(THD *thd) /* + These functions are for making it later easy to add strict + checking for all date handling. +*/ + +extern my_bool strict_date_checking; + +inline ulong sql_mode_for_dates(THD *thd) +{ + if (unlikely(strict_date_checking)) + return (thd->variables.sql_mode & + (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | + MODE_INVALID_DATES)); + return (thd->variables.sql_mode & MODE_INVALID_DATES); +} + +inline ulong sql_mode_for_dates() +{ + return sql_mode_for_dates(current_thd); +} + +/* Used to hold information about file and file structure in exchange via non-DB file (...INTO OUTFILE..., ...LOAD DATA...) XXX: We never call destructor for objects of this class. diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index def0c8dd951..05e33826da9 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -1121,7 +1121,7 @@ pthread_handler_t handle_one_connection(void *arg) { THD *thd= (THD*) arg; - thd->thr_create_utime= my_micro_time(); + thd->thr_create_utime= microsecond_interval_timer(); if (thread_scheduler.init_new_connection_thread()) { diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index bd28ab6e9b2..65066a878ee 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1730,10 +1730,11 @@ class delayed_row :public ilink { public: char *record; enum_duplicates dup; - time_t start_time; + my_time_t start_time; + ulong start_time_sec_part; ulong sql_mode; bool auto_increment_field_not_null; - bool query_start_used, ignore, log_query; + bool query_start_used, ignore, log_query, query_start_sec_part_used; bool stmt_depends_on_first_successful_insert_id_in_prev_stmt; ulonglong first_successful_insert_id_in_prev_stmt; ulonglong forced_insert_id; @@ -2234,8 +2235,10 @@ int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic, if (!(row->record= (char*) my_malloc(table->s->reclength, MYF(MY_WME)))) goto err; memcpy(row->record, table->record[0], table->s->reclength); - row->start_time= thd->start_time; - row->query_start_used= thd->query_start_used; + row->start_time= thd->start_time; + row->query_start_used= thd->query_start_used; + row->start_time_sec_part= thd->start_time_sec_part; + row->query_start_sec_part_used= thd->query_start_sec_part_used; /* those are for the binlog: LAST_INSERT_ID() has been evaluated at this time, so record does not need it, but statement-based binlogging of the @@ -2693,6 +2696,8 @@ bool Delayed_insert::handle_inserts(void) thd.start_time=row->start_time; thd.query_start_used=row->query_start_used; + thd.start_time_sec_part=row->start_time_sec_part; + thd.query_start_sec_part_used=row->query_start_sec_part_used; /* To get the exact auto_inc interval to store in the binlog we must not use values from the previous interval (of the previous rows). @@ -3590,9 +3595,6 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, tmp_table.s->db_create_options=0; tmp_table.s->blob_ptr_size= portable_sizeof_char_ptr; - tmp_table.s->db_low_byte_first= - test(create_info->db_type == myisam_hton || - create_info->db_type == heap_hton); tmp_table.null_row= 0; tmp_table.maybe_null= 0; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index a1a654264a5..f73ddf920bf 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1021,18 +1021,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->query_plan_flags= QPLAN_INIT; thd->lex->sql_command= SQLCOM_END; /* to avoid confusing VIEW detectors */ thd->set_time(); - if (!thd->is_valid_time()) - { - /* - If the time has got past 2038 we need to shut this server down - We do this by making sure every command is a shutdown and we - have enough privileges to shut the server down - - TODO: remove this when we have full 64 bit my_time_t support - */ - thd->security_ctx->master_access|= SHUTDOWN_ACL; - command= COM_SHUTDOWN; - } VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query_id= global_query_id; @@ -1435,10 +1423,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, packet[0]. */ enum mysql_enum_shutdown_level level; - if (!thd->is_valid_time()) - level= SHUTDOWN_DEFAULT; - else - level= (enum mysql_enum_shutdown_level) (uchar) packet[0]; + level= (enum mysql_enum_shutdown_level) (uchar) packet[0]; if (level == SHUTDOWN_DEFAULT) level= SHUTDOWN_WAIT_ALL_BUFFERS; // soon default will be configurable else if (level != SHUTDOWN_WAIT_ALL_BUFFERS) @@ -5792,6 +5777,7 @@ void mysql_reset_thd_for_next_command(THD *thd, my_bool calculate_userstat) thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0; thd->query_start_used= 0; + thd->query_start_sec_part_used= 0; thd->is_fatal_error= thd->time_zone_used= 0; /* Clear the status flag that are expected to be cleared at the @@ -6251,17 +6237,6 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type, DBUG_RETURN(1); } - if (type == MYSQL_TYPE_TIMESTAMP && length) - { - /* Display widths are no longer supported for TIMSTAMP as of MySQL 4.1. - In other words, for declarations such as TIMESTAMP(2), TIMESTAMP(4), - and so on, the display width is ignored. - */ - char buf[32]; - my_snprintf(buf, sizeof(buf), "TIMESTAMP(%s)", length); - WARN_DEPRECATED(thd, "6.0", buf, "'TIMESTAMP'"); - } - if (!(new_field= new Create_field()) || new_field->init(thd, field_name->str, type, length, decimals, type_modifier, default_value, on_update_value, comment, change, diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 2b478508170..85a97386712 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -491,8 +491,7 @@ static void set_param_time(Item_param *param, uchar **pos, ulong len) } else set_zero_time(&tm, MYSQL_TIMESTAMP_TIME); - param->set_time(&tm, MYSQL_TIMESTAMP_TIME, - MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN); + param->set_time(&tm, MYSQL_TIMESTAMP_TIME, MAX_TIME_FULL_WIDTH); *pos+= length; } diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc index 7bfb127815d..5d5cb6a764e 100644 --- a/sql/sql_profile.cc +++ b/sql/sql_profile.cc @@ -235,7 +235,7 @@ void PROF_MEASUREMENT::set_label(const char *status_arg, */ void PROF_MEASUREMENT::collect() { - time_usecs= (double) my_getsystime() / 10.0; /* 1 sec was 1e7, now is 1e6 */ + time_usecs= my_interval_timer() / 1e3; /* ns to us */ #ifdef HAVE_GETRUSAGE getrusage(RUSAGE_SELF, &rusage); #elif defined(__WIN__) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 66be0a2e4c2..24f7ff129b1 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3704,30 +3704,21 @@ add_key_field(JOIN *join, (*sargables)->arg_value= value; (*sargables)->num_values= num_values; } + /* - We can't always use indexes when comparing a string index to a - number. cmp_type() is checked to allow compare of dates to numbers. - eq_func is NEVER true when num_values > 1 + We can't use indexes when comparing a string index to a + number or two strings if the effective collation + of the operation differ from the field collation. */ if (!eq_func) return; - if (field->result_type() == STRING_RESULT) + + if (field->cmp_type() == STRING_RESULT) { - if ((*value)->result_type() != STRING_RESULT) - { - if (field->cmp_type() != (*value)->result_type()) + if ((*value)->cmp_type() != STRING_RESULT) return; - } - else - { - /* - We can't use indexes if the effective collation - of the operation differ from the field collation. - */ - if (field->cmp_type() == STRING_RESULT && - ((Field_str*)field)->charset() != cond->compare_collation()) - return; - } + if (((Field_str*)field)->charset() != cond->compare_collation()) + return; } } } @@ -3856,7 +3847,6 @@ is_local_field (Item *field) The primary reason for having and_level attribute is the OR operation which uses and_level to mark KEY_FIELDs that should get into the result of the OR operation - */ static void @@ -11555,36 +11545,36 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) return cond; // Point at next and level } -/* +/** Check if equality can be used in removing components of GROUP BY/DISTINCT - SYNOPSIS - test_if_equality_guarantees_uniqueness() - l the left comparison argument (a field if any) - r the right comparison argument (a const of any) + @param l the left comparison argument (a field if any) + @param r the right comparison argument (a const of any) - DESCRIPTION - Checks if an equality predicate can be used to take away - DISTINCT/GROUP BY because it is known to be true for exactly one - distinct value (e.g. <expr> == <const>). - Arguments must be of the same type because e.g. - <string_field> = <int_const> may match more than 1 distinct value from - the column. - We must take into consideration and the optimization done for various - string constants when compared to dates etc (see Item_int_with_ref) as - well as the collation of the arguments. + @details + Checks if an equality predicate can be used to take away + DISTINCT/GROUP BY because it is known to be true for exactly one + distinct value (e.g. <expr> == <const>). + Arguments must be of the same type because e.g. + <string_field> = <int_const> may match more than 1 distinct value from + the column. + Additionally, strings must have the same collation. + Or the *field* must be a datetime - if the constant is a datetime + and a field is not - this is not enough, consider: + create table t1 (a varchar(100)); + insert t1 values ('2010-01-02'), ('2010-1-2'), ('20100102'); + select distinct t1 from t1 where a=date('2010-01-02'); - RETURN VALUE - TRUE can be used - FALSE cannot be used + @retval true can be used + @retval false cannot be used */ static bool test_if_equality_guarantees_uniqueness(Item *l, Item *r) { return r->const_item() && - /* elements must be compared as dates */ - (Arg_comparator::can_compare_as_dates(l, r, 0) || - /* or of the same result type */ + /* the field is a date (the const will be converted to a date) */ + (l->cmp_type() == TIME_RESULT || + /* or arguments are of the same result type */ (r->result_type() == l->result_type() && /* and must have the same collation if compared as strings */ (l->result_type() != STRING_RESULT || @@ -11770,15 +11760,12 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table, case STRING_RESULT: DBUG_ASSERT(item->collation.collation); - enum enum_field_types type; /* DATE/TIME and GEOMETRY fields have STRING_RESULT result type. To preserve type they needed to be handled separately. */ - if ((type= item->field_type()) == MYSQL_TYPE_DATETIME || - type == MYSQL_TYPE_TIME || type == MYSQL_TYPE_DATE || - type == MYSQL_TYPE_NEWDATE || - type == MYSQL_TYPE_TIMESTAMP || type == MYSQL_TYPE_GEOMETRY) + if (item->cmp_type() == TIME_RESULT || + item->field_type() == MYSQL_TYPE_GEOMETRY) new_field= item->tmp_table_field_from_field_type(table, 1); /* Make sure that the blob fits into a Field_varstring which has @@ -12240,7 +12227,6 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, init_tmp_table_share(thd, share, "", 0, tmpname, tmpname); share->blob_field= blob_field; share->blob_ptr_size= portable_sizeof_char_ptr; - share->db_low_byte_first=1; // True for HEAP, MyISAM and Maria share->table_charset= param->table_charset; share->primary_key= MAX_KEY; // Indicate no primary key share->keys_for_keyread.init(); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 18e5189f3ca..2eecae26506 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2000,7 +2000,7 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) TABLE *table= tables->table; CHARSET_INFO *cs= system_charset_info; char *user; - ulonglong unow= my_micro_time(); + my_hrtime_t unow= my_hrtime(); DBUG_ENTER("fill_process_list"); user= thd->security_ctx->master_access & PROCESS_ACL ? @@ -2058,8 +2058,10 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) table->field[4]->store(command_name[tmp->command].str, command_name[tmp->command].length, cs); /* MYSQL_TIME */ - const ulonglong utime= tmp->start_utime ? unow - tmp->start_utime : 0; - table->field[5]->store(utime / 1000000, TRUE); + const ulonglong utime= (tmp->start_time ? + (unow.val - tmp->start_time * HRTIME_RESOLUTION - + tmp->start_time_sec_part) : 0); + table->field[5]->store(utime / HRTIME_RESOLUTION, TRUE); /* STATE */ #ifndef EMBEDDED_LIBRARY val= (char*) (tmp->net.reading_or_writing ? @@ -2096,7 +2098,7 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) pthread_mutex_unlock(&tmp->LOCK_thd_data); /* TIME_MS */ - table->field[8]->store((double)(utime / 1000.0)); + table->field[8]->store((double)(utime / (HRTIME_RESOLUTION / 1000.0))); if (schema_table_store_record(thd, table)) { @@ -4342,7 +4344,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, end=strmov(end,grant_types.type_names[bitnr]); } } - table->field[17]->store(tmp+1,end == tmp ? 0 : (uint) (end-tmp-1), cs); + table->field[18]->store(tmp+1,end == tmp ? 0 : (uint) (end-tmp-1), cs); #endif table->field[1]->store(db_name->str, db_name->length, cs); @@ -4351,7 +4353,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, cs); table->field[4]->store((longlong) count, TRUE); field->sql_type(type); - table->field[14]->store(type.ptr(), type.length(), cs); + table->field[15]->store(type.ptr(), type.length(), cs); /* MySQL column type has the following format: base_type [(dimension)] [unsigned] [zerofill]. @@ -4398,6 +4400,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, They are set to -1 if they should not be set (we should return NULL) */ + field_length= -1; decimals= field->decimals(); switch (field->type()) { case MYSQL_TYPE_NEWDECIMAL: @@ -4426,8 +4429,13 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, if (decimals == NOT_FIXED_DEC) decimals= -1; // return NULL break; + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATETIME: + table->field[12]->store((longlong) field->decimals(), TRUE); + table->field[12]->set_notnull(); + break; default: - field_length= decimals= -1; break; } @@ -4435,40 +4443,40 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, { table->field[10]->store((longlong) field_length, TRUE); table->field[10]->set_notnull(); - } - if (decimals >= 0) - { - table->field[11]->store((longlong) decimals, TRUE); - table->field[11]->set_notnull(); + if (decimals >= 0) + { + table->field[11]->store((longlong) decimals, TRUE); + table->field[11]->set_notnull(); + } } if (field->has_charset()) { pos=(uchar*) field->charset()->csname; - table->field[12]->store((const char*) pos, - strlen((const char*) pos), cs); - table->field[12]->set_notnull(); - pos=(uchar*) field->charset()->name; table->field[13]->store((const char*) pos, strlen((const char*) pos), cs); table->field[13]->set_notnull(); + pos=(uchar*) field->charset()->name; + table->field[14]->store((const char*) pos, + strlen((const char*) pos), cs); + table->field[14]->set_notnull(); } pos=(uchar*) ((field->flags & PRI_KEY_FLAG) ? "PRI" : (field->flags & UNIQUE_KEY_FLAG) ? "UNI" : (field->flags & MULTIPLE_KEY_FLAG) ? "MUL":""); - table->field[15]->store((const char*) pos, + table->field[16]->store((const char*) pos, strlen((const char*) pos), cs); if (field->unireg_check == Field::NEXT_NUMBER) - table->field[16]->store(STRING_WITH_LEN("auto_increment"), cs); + table->field[17]->store(STRING_WITH_LEN("auto_increment"), cs); if (show_table->timestamp_field == field && field->unireg_check != Field::TIMESTAMP_DN_FIELD) - table->field[16]->store(STRING_WITH_LEN("on update CURRENT_TIMESTAMP"), + table->field[17]->store(STRING_WITH_LEN("on update CURRENT_TIMESTAMP"), cs); if (field->vcol_info) - table->field[16]->store(STRING_WITH_LEN("VIRTUAL"), cs); + table->field[17]->store(STRING_WITH_LEN("VIRTUAL"), cs); - table->field[18]->store(field->comment.str, field->comment.length, cs); + table->field[19]->store(field->comment.str, field->comment.length, cs); if (schema_table_store_record(thd, table)) DBUG_RETURN(1); } @@ -6212,14 +6220,23 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) item->unsigned_flag= (fields_info->field_flags & MY_I_S_UNSIGNED); break; case MYSQL_TYPE_DATE: + if (!(item=new Item_return_date_time(fields_info->field_name, + MAX_DATE_WIDTH, + fields_info->field_type))) + DBUG_RETURN(0); + break; case MYSQL_TYPE_TIME: + if (!(item=new Item_return_date_time(fields_info->field_name, + MAX_TIME_FULL_WIDTH, + fields_info->field_type))) + DBUG_RETURN(0); + break; case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_DATETIME: if (!(item=new Item_return_date_time(fields_info->field_name, + MAX_DATETIME_WIDTH, fields_info->field_type))) - { DBUG_RETURN(0); - } break; case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_DOUBLE: @@ -6405,7 +6422,7 @@ int make_table_names_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) int make_columns_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) { - int fields_arr[]= {3, 14, 13, 6, 15, 5, 16, 17, 18, -1}; + int fields_arr[]= {3, 15, 14, 6, 16, 5, 17, 18, 19, -1}; int *field_num= fields_arr; ST_FIELD_INFO *field_info; Name_resolution_context *context= &thd->lex->select_lex.context; @@ -6413,9 +6430,9 @@ int make_columns_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) for (; *field_num >= 0; field_num++) { field_info= &schema_table->fields_info[*field_num]; - if (!thd->lex->verbose && (*field_num == 13 || - *field_num == 17 || - *field_num == 18)) + if (!thd->lex->verbose && (*field_num == 14 || + *field_num == 18 || + *field_num == 19)) continue; Item_field *field= new Item_field(context, NullS, NullS, field_info->field_name); @@ -6884,6 +6901,8 @@ ST_FIELD_INFO columns_fields_info[]= 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY}, {"NUMERIC_SCALE", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONGLONG, 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY}, + {"DATETIME_PRECISION", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, + 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY}, {"CHARACTER_SET_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FRM_ONLY}, {"COLLATION_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 1, "Collation", diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 6f40d797a5c..56702a2308f 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -577,7 +577,7 @@ uint32 String::numchars() return str_charset->cset->numchars(str_charset, Ptr, Ptr+str_length); } -int String::charpos(int i,uint32 offset) +int String::charpos(longlong i,uint32 offset) { if (i <= 0) return i; diff --git a/sql/sql_string.h b/sql/sql_string.h index e4cce7b5527..61198af1261 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -343,7 +343,7 @@ public: friend String *copy_if_not_alloced(String *a,String *b,uint32 arg_length); friend class Field; uint32 numchars(); - int charpos(int i,uint32 offset=0); + int charpos(longlong i,uint32 offset=0); int reserve(uint32 space_needed) { diff --git a/sql/sql_table.cc b/sql/sql_table.cc index c857fb3de8c..d9d36c260fd 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -36,17 +36,15 @@ const char *primary_key_name="PRIMARY"; static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end); static char *make_unique_key_name(const char *field_name,KEY *start,KEY *end); -static int copy_data_between_tables(TABLE *from,TABLE *to, - List<Create_field> &create, bool ignore, - uint order_num, ORDER *order, - ha_rows *copied,ha_rows *deleted, - enum enum_enable_or_disable keys_onoff, - bool error_if_not_empty); +static int copy_data_between_tables(TABLE *,TABLE *, List<Create_field> &, bool, + uint, ORDER *, ha_rows *,ha_rows *, + enum enum_enable_or_disable, bool); static bool prepare_blob_field(THD *thd, Create_field *sql_field); static bool check_engine(THD *, const char *, HA_CREATE_INFO *); static int mysql_prepare_create_table(THD *, HA_CREATE_INFO *, Alter_info *, - bool, uint *, handler *, KEY **, uint *, int); + bool, uint *, handler *, KEY **, uint *, + int); static bool mysql_prepare_alter_table(THD *, TABLE *, HA_CREATE_INFO *, Alter_info *); static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 71dcd637d64..8173fdd60f2 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -682,10 +682,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %pure_parser /* We have threads */ /* - Currently there are 169 shift/reduce conflicts. + Currently there are 171 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 169 +%expect 171 /* Comments for TOKENS. @@ -1348,6 +1348,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_ev_status opt_ev_on_completion ev_on_completion opt_ev_comment ev_alter_on_schedule_completion opt_ev_rename_to opt_ev_sql_stmt optional_flush_tables_arguments opt_dyncol_type dyncol_type + opt_time_precision %type <ulong_num> ulong_num real_ulong_num merge_insert_types @@ -2083,7 +2084,7 @@ opt_ev_status: ev_starts: /* empty */ { - Item *item= new (YYTHD->mem_root) Item_func_now_local(); + Item *item= new (YYTHD->mem_root) Item_func_now_local(0); if (item == NULL) MYSQL_YYABORT; Lex->event_parse_data->item_starts= item; @@ -5215,7 +5216,7 @@ type: { $$=MYSQL_TYPE_YEAR; } | DATE_SYM { $$=MYSQL_TYPE_DATE; } - | TIME_SYM + | TIME_SYM opt_field_length { $$=MYSQL_TYPE_TIME; } | TIMESTAMP opt_field_length { @@ -5230,7 +5231,7 @@ type: $$=MYSQL_TYPE_TIMESTAMP; } } - | DATETIME + | DATETIME opt_field_length { $$=MYSQL_TYPE_DATETIME; } | TINYBLOB { @@ -5423,9 +5424,9 @@ attribute: NULL_SYM { Lex->type&= ~ NOT_NULL_FLAG; } | not NULL_SYM { Lex->type|= NOT_NULL_FLAG; } | DEFAULT now_or_signed_literal { Lex->default_value=$2; } - | ON UPDATE_SYM NOW_SYM optional_braces + | ON UPDATE_SYM NOW_SYM opt_time_precision { - Item *item= new (YYTHD->mem_root) Item_func_now_local(); + Item *item= new (YYTHD->mem_root) Item_func_now_local($4); if (item == NULL) MYSQL_YYABORT; Lex->on_update_value= item; @@ -5495,9 +5496,9 @@ attribute: ; now_or_signed_literal: - NOW_SYM optional_braces + NOW_SYM opt_time_precision { - $$= new (YYTHD->mem_root) Item_func_now_local(); + $$= new (YYTHD->mem_root) Item_func_now_local($2); if ($$ == NULL) MYSQL_YYABORT; } @@ -7104,6 +7105,12 @@ select_alias: | TEXT_STRING_sys { $$=$1; } ; +opt_time_precision: + /* empty */ { $$= 0; } + | '(' ')' { $$= 0; } + | '(' real_ulong_num ')' { $$= $2; }; + ; + optional_braces: /* empty */ {} | '(' ')' {} @@ -7591,19 +7598,21 @@ dyncol_type: lex->charset= NULL; lex->length= lex->dec= 0; } - | TIME_SYM + | TIME_SYM opt_field_length { LEX *lex= Lex; $$= DYN_COL_TIME; lex->charset= NULL; - lex->length= lex->dec= 0; + lex->dec= lex->length; + lex->length= 0; } - | DATETIME + | DATETIME opt_field_length { LEX *lex= Lex; $$= DYN_COL_DATETIME; lex->charset= NULL; - lex->length= lex->dec= 0; + lex->dec= lex->length; + lex->length= 0; } ; @@ -7907,13 +7916,13 @@ function_call_keyword: } | TIME_SYM '(' expr ')' { - $$= new (YYTHD->mem_root) Item_time_typecast($3); + $$= new (YYTHD->mem_root) Item_time_typecast($3, AUTO_SEC_PART_DIGITS); if ($$ == NULL) MYSQL_YYABORT; } | TIMESTAMP '(' expr ')' { - $$= new (YYTHD->mem_root) Item_datetime_typecast($3); + $$= new (YYTHD->mem_root) Item_datetime_typecast($3, AUTO_SEC_PART_DIGITS); if ($$ == NULL) MYSQL_YYABORT; } @@ -8020,16 +8029,9 @@ function_call_nonkeyword: MYSQL_YYABORT; Lex->safe_to_cache_query=0; } - | CURTIME optional_braces + | CURTIME opt_time_precision { - $$= new (YYTHD->mem_root) Item_func_curtime_local(); - if ($$ == NULL) - MYSQL_YYABORT; - Lex->safe_to_cache_query=0; - } - | CURTIME '(' expr ')' - { - $$= new (YYTHD->mem_root) Item_func_curtime_local($3); + $$= new (YYTHD->mem_root) Item_func_curtime_local($2); if ($$ == NULL) MYSQL_YYABORT; Lex->safe_to_cache_query=0; @@ -8060,16 +8062,9 @@ function_call_nonkeyword: if ($$ == NULL) MYSQL_YYABORT; } - | NOW_SYM optional_braces + | NOW_SYM opt_time_precision { - $$= new (YYTHD->mem_root) Item_func_now_local(); - if ($$ == NULL) - MYSQL_YYABORT; - Lex->safe_to_cache_query=0; - } - | NOW_SYM '(' expr ')' - { - $$= new (YYTHD->mem_root) Item_func_now_local($3); + $$= new (YYTHD->mem_root) Item_func_now_local($2); if ($$ == NULL) MYSQL_YYABORT; Lex->safe_to_cache_query=0; @@ -8117,7 +8112,7 @@ function_call_nonkeyword: if ($$ == NULL) MYSQL_YYABORT; } - | SYSDATE optional_braces + | SYSDATE opt_time_precision { /* Unlike other time-related functions, SYSDATE() is @@ -8128,19 +8123,9 @@ function_call_nonkeyword: */ Lex->set_stmt_unsafe(); if (global_system_variables.sysdate_is_now == 0) - $$= new (YYTHD->mem_root) Item_func_sysdate_local(); - else - $$= new (YYTHD->mem_root) Item_func_now_local(); - if ($$ == NULL) - MYSQL_YYABORT; - Lex->safe_to_cache_query=0; - } - | SYSDATE '(' expr ')' - { - if (global_system_variables.sysdate_is_now == 0) - $$= new (YYTHD->mem_root) Item_func_sysdate_local($3); + $$= new (YYTHD->mem_root) Item_func_sysdate_local($2); else - $$= new (YYTHD->mem_root) Item_func_now_local($3); + $$= new (YYTHD->mem_root) Item_func_now_local($2); if ($$ == NULL) MYSQL_YYABORT; Lex->safe_to_cache_query=0; @@ -8164,16 +8149,16 @@ function_call_nonkeyword: MYSQL_YYABORT; Lex->safe_to_cache_query=0; } - | UTC_TIME_SYM optional_braces + | UTC_TIME_SYM opt_time_precision { - $$= new (YYTHD->mem_root) Item_func_curtime_utc(); + $$= new (YYTHD->mem_root) Item_func_curtime_utc($2); if ($$ == NULL) MYSQL_YYABORT; Lex->safe_to_cache_query=0; } - | UTC_TIMESTAMP_SYM optional_braces + | UTC_TIMESTAMP_SYM opt_time_precision { - $$= new (YYTHD->mem_root) Item_func_now_utc(); + $$= new (YYTHD->mem_root) Item_func_now_utc($2); if ($$ == NULL) MYSQL_YYABORT; Lex->safe_to_cache_query=0; @@ -8836,10 +8821,18 @@ cast_type: { $$=ITEM_CAST_UNSIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } | DATE_SYM { $$=ITEM_CAST_DATE; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } - | TIME_SYM - { $$=ITEM_CAST_TIME; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } - | DATETIME - { $$=ITEM_CAST_DATETIME; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } + | TIME_SYM opt_field_length + { + $$=ITEM_CAST_TIME; + LEX *lex= Lex; + lex->charset= NULL; lex->dec= lex->length; lex->length= (char*)0; + } + | DATETIME opt_field_length + { + $$=ITEM_CAST_DATETIME; + LEX *lex= Lex; + lex->charset= NULL; lex->dec= lex->length; lex->length= (char*)0; + } | DECIMAL_SYM float_options { $$=ITEM_CAST_DECIMAL; Lex->charset= NULL; } | DOUBLE_SYM diff --git a/sql/table.cc b/sql/table.cc index 96a64cdeba2..0451c47eeb8 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -776,8 +776,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, share->db_record_offset= 1; if (db_create_options & HA_OPTION_LONG_BLOB_PTR) share->blob_ptr_size= portable_sizeof_char_ptr; - /* Set temporarily a good value for db_low_byte_first */ - share->db_low_byte_first= test(legacy_db_type != DB_TYPE_ISAM); error=4; share->max_rows= uint4korr(head+18); share->min_rows= uint4korr(head+22); @@ -1696,7 +1694,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, share->can_cmp_whole_record= (share->blob_fields == 0 && share->varchar_fields == 0); - share->db_low_byte_first= handler_file->low_byte_first(); share->column_bitmap_size= bitmap_buffer_size(share->fields); if (!(bitmaps= (my_bitmap_map*) alloc_root(&share->mem_root, @@ -1994,7 +1991,12 @@ bool unpack_vcol_info_from_frm(THD *thd, vcol_arena= table->expr_arena; if (!vcol_arena) { - Query_arena expr_arena(&table->mem_root, Query_arena::INITIALIZED); + /* + We need to use CONVENTIONAL_EXECUTION here to ensure that + any new items created by fix_fields() are not reverted. + */ + Query_arena expr_arena(&table->mem_root, + Query_arena::CONVENTIONAL_EXECUTION); if (!(vcol_arena= (Query_arena *) alloc_root(&table->mem_root, sizeof(Query_arena)))) goto err; diff --git a/sql/table.h b/sql/table.h index ca4e50d99e1..4278c3c9c35 100644 --- a/sql/table.h +++ b/sql/table.h @@ -440,7 +440,6 @@ typedef struct st_table_share bool null_field_first; bool system; /* Set if system table (one record) */ bool crypted; /* If .frm file is crypted */ - bool db_low_byte_first; /* Portable row format */ bool crashed; bool is_view; bool name_lock, replace_with_name_lock; diff --git a/sql/time.cc b/sql/time.cc index a3b78eb5e62..60cf4631b6f 100644 --- a/sql/time.cc +++ b/sql/time.cc @@ -1,4 +1,5 @@ /* Copyright (C) 2000-2006 MySQL AB + 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 @@ -230,14 +231,12 @@ str_to_datetime_with_warn(const char *str, uint length, MYSQL_TIME *l_time, timestamp_type ts_type; ts_type= str_to_datetime(str, length, l_time, - (flags | (thd->variables.sql_mode & - (MODE_INVALID_DATES | - MODE_NO_ZERO_IN_DATE | - MODE_NO_ZERO_DATE))), + (flags | (sql_mode_for_dates(thd))), &was_cut); if (was_cut || ts_type <= MYSQL_TIMESTAMP_ERROR) make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - str, length, ts_type, NullS); + str, length, flags & TIME_TIME_ONLY ? + MYSQL_TIMESTAMP_TIME : ts_type, NullS); return ts_type; } @@ -269,37 +268,45 @@ bool decimal_to_datetime_with_warn(decimal_t *value, MYSQL_TIME *ltime, } /* - Convert a datetime from broken-down MYSQL_TIME representation to corresponding - TIMESTAMP value. + Convert a datetime from broken-down MYSQL_TIME representation to + corresponding TIMESTAMP value. SYNOPSIS TIME_to_timestamp() thd - current thread t - datetime in broken-down representation, - in_dst_time_gap - pointer to bool which is set to true if t represents - value which doesn't exists (falls into the spring - time-gap) or to false otherwise. + error_code - 0, if the conversion was successful; + ER_WARN_DATA_OUT_OF_RANGE, if t contains datetime value + which is out of TIMESTAMP range; + ER_WARN_INVALID_TIMESTAMP, if t represents value which + doesn't exists (falls into the spring time-gap). RETURN Number seconds in UTC since start of Unix Epoch corresponding to t. - 0 - t contains datetime value which is out of TIMESTAMP range. - + 0 - in case of ER_WARN_DATA_OUT_OF_RANGE */ -my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, my_bool *in_dst_time_gap) + +my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error_code) { my_time_t timestamp; + my_bool in_dst_time_gap= 0; - *in_dst_time_gap= 0; + *error_code= 0; thd->time_zone_used= 1; - timestamp= thd->variables.time_zone->TIME_to_gmt_sec(t, in_dst_time_gap); - if (timestamp) + timestamp= thd->variables.time_zone->TIME_to_gmt_sec(t, &in_dst_time_gap); + + /* In case of error, timestamp == 0 and in_dst_time_gap is != 0 */ + if (timestamp || ! in_dst_time_gap) { + if (in_dst_time_gap) + *error_code= ER_WARN_INVALID_TIMESTAMP; return timestamp; } /* If we are here we have range error. */ - return(0); + *error_code= ER_WARN_DATA_OUT_OF_RANGE; + return 0; } @@ -309,13 +316,19 @@ my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, my_bool *in_dst_time_ NOTE See str_to_time() for more info. + + RETURN + 0 ok + 1 wrong time value */ + bool str_to_time_with_warn(const char *str, uint length, MYSQL_TIME *l_time, ulong fuzzydate) { int warning; - bool ret_val= str_to_time(str, length, l_time, fuzzydate, &warning); + bool ret_val= (str_to_time(str, length, l_time, fuzzydate, &warning) == + MYSQL_TIMESTAMP_ERROR); if (ret_val || warning) make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, str, length, MYSQL_TIMESTAMP_TIME, NullS); @@ -715,15 +728,14 @@ const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format, context) This functions don't check that given MYSQL_TIME structure members are in valid range. If they are not, return value won't reflect any - valid date either. Additionally, make_time doesn't take into - account time->day member: it's assumed that days have been converted - to hours already. + valid date either. ****************************************************************************/ void make_time(const DATE_TIME_FORMAT *format __attribute__((unused)), const MYSQL_TIME *l_time, String *str) { - uint length= (uint) my_time_to_str(l_time, (char*) str->ptr()); + str->alloc(MAX_DATE_STRING_REP_LENGTH); + uint length= (uint) my_time_to_str(l_time, (char*) str->ptr(), 0); str->length(length); str->set_charset(&my_charset_bin); } @@ -732,6 +744,7 @@ void make_time(const DATE_TIME_FORMAT *format __attribute__((unused)), void make_date(const DATE_TIME_FORMAT *format __attribute__((unused)), const MYSQL_TIME *l_time, String *str) { + str->alloc(MAX_DATE_STRING_REP_LENGTH); uint length= (uint) my_date_to_str(l_time, (char*) str->ptr()); str->length(length); str->set_charset(&my_charset_bin); @@ -741,7 +754,8 @@ void make_date(const DATE_TIME_FORMAT *format __attribute__((unused)), void make_datetime(const DATE_TIME_FORMAT *format __attribute__((unused)), const MYSQL_TIME *l_time, String *str) { - uint length= (uint) my_datetime_to_str(l_time, (char*) str->ptr()); + str->alloc(MAX_DATE_STRING_REP_LENGTH); + uint length= (uint) my_datetime_to_str(l_time, (char*) str->ptr(), 0); str->length(length); str->set_charset(&my_charset_bin); } @@ -749,8 +763,8 @@ void make_datetime(const DATE_TIME_FORMAT *format __attribute__((unused)), void make_truncated_value_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, - const char *str_val, - uint str_length, timestamp_type time_type, + const Lazy_string *sval, + timestamp_type time_type, const char *field_name) { char warn_buff[MYSQL_ERRMSG_SIZE]; @@ -758,8 +772,7 @@ void make_truncated_value_warning(THD *thd, CHARSET_INFO *cs= &my_charset_latin1; char buff[128]; String str(buff,(uint32) sizeof(buff), system_charset_info); - str.copy(str_val, str_length, system_charset_info); - str[str_length]= 0; // Ensure we have end 0 for snprintf + sval->copy_to(&str); switch (time_type) { case MYSQL_TIMESTAMP_DATE: @@ -776,17 +789,17 @@ void make_truncated_value_warning(THD *thd, if (field_name) cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff), ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), - type_str, str.c_ptr(), field_name, + type_str, str.c_ptr_safe(), field_name, (ulong) thd->row_count); else { if (time_type > MYSQL_TIMESTAMP_ERROR) cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff), ER(ER_TRUNCATED_WRONG_VALUE), - type_str, str.c_ptr()); + type_str, str.c_ptr_safe()); else cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff), - ER(ER_WRONG_VALUE), type_str, str.c_ptr()); + ER(ER_WRONG_VALUE), type_str, str.c_ptr_safe()); } push_warning(thd, level, ER_TRUNCATED_WRONG_VALUE, warn_buff); @@ -795,14 +808,17 @@ void make_truncated_value_warning(THD *thd, /* Daynumber from year 0 to 9999-12-31 */ #define MAX_DAY_NUMBER 3652424L +#define COMBINE(X) \ + (((((X)->day * 24LL + (X)->hour) * 60LL + \ + (X)->minute) * 60LL + (X)->second)*1000000LL + \ + (X)->second_part) -bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL interval) +bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, + INTERVAL interval) { long period, sign; - ltime->neg= 0; - - sign= (interval.neg ? -1 : 1); + sign= (interval.neg == ltime->neg ? 1 : -1); switch (int_type) { case INTERVAL_SECOND: @@ -819,35 +835,29 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL inter case INTERVAL_DAY_SECOND: case INTERVAL_DAY_MINUTE: case INTERVAL_DAY_HOUR: + case INTERVAL_DAY: { - longlong sec, days, daynr, microseconds, extra_sec; - ltime->time_type= MYSQL_TIMESTAMP_DATETIME; // Return full date - microseconds= ltime->second_part + sign*interval.second_part; - extra_sec= microseconds/1000000L; - microseconds= microseconds%1000000L; - - sec=((ltime->day-1)*3600*24L+ltime->hour*3600+ltime->minute*60+ - ltime->second + - sign* (longlong) (interval.day*3600*24L + - interval.hour*LL(3600)+interval.minute*LL(60)+ - interval.second))+ extra_sec; - if (microseconds < 0) - { - microseconds+= LL(1000000); - sec--; - } - days= sec/(3600*LL(24)); - sec-= days*3600*LL(24); - if (sec < 0) - { - days--; - sec+= 3600*LL(24); - } - ltime->second_part= (uint) microseconds; - ltime->second= (uint) (sec % 60); - ltime->minute= (uint) (sec/60 % 60); - ltime->hour= (uint) (sec/3600); - daynr= calc_daynr(ltime->year,ltime->month,1) + days; + longlong usec, daynr; + my_bool neg= ltime->neg; + enum enum_mysql_timestamp_type time_type= ltime->time_type; + + if (time_type != MYSQL_TIMESTAMP_TIME) + ltime->day+= calc_daynr(ltime->year, ltime->month, 1) - 1; + + usec= COMBINE(ltime) + sign*COMBINE(&interval); + + unpack_time(usec, ltime); + ltime->time_type= time_type; + ltime->neg^= neg; + + if (time_type == MYSQL_TIMESTAMP_TIME) + break; + + if (int_type != INTERVAL_DAY) + ltime->time_type= MYSQL_TIMESTAMP_DATETIME; // Return full date + + daynr= usec/1000000/24/60/60; + /* Day number from year 0 to 9999-12-31 */ if ((ulonglong) daynr > MAX_DAY_NUMBER) goto invalid_date; @@ -855,7 +865,6 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL inter <ime->day); break; } - case INTERVAL_DAY: case INTERVAL_WEEK: period= (calc_daynr(ltime->year,ltime->month,ltime->day) + sign * (long) interval.day); @@ -999,19 +1008,14 @@ calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, longlong *s int my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b) { - ulonglong a_t= TIME_to_ulonglong_datetime(a); - ulonglong b_t= TIME_to_ulonglong_datetime(b); + ulonglong a_t= pack_time(a); + ulonglong b_t= pack_time(b); if (a_t < b_t) return -1; if (a_t > b_t) return 1; - if (a->second_part < b->second_part) - return -1; - if (a->second_part > b->second_part) - return 1; - return 0; } diff --git a/sql/tztime.cc b/sql/tztime.cc index 25b3b0c8994..1334d872510 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -214,7 +214,7 @@ tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage) ALIGN_SIZE(sp->typecnt * sizeof(TRAN_TYPE_INFO)) + #ifdef ABBR_ARE_USED - ALIGN_SIZE(sp->charcnt) + + ALIGN_SIZE(sp->charcnt+1) + #endif sp->leapcnt * sizeof(LS_INFO)))) return 1; @@ -227,7 +227,7 @@ tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage) tzinfo_buf+= ALIGN_SIZE(sp->typecnt * sizeof(TRAN_TYPE_INFO)); #ifdef ABBR_ARE_USED sp->chars= tzinfo_buf; - tzinfo_buf+= ALIGN_SIZE(sp->charcnt); + tzinfo_buf+= ALIGN_SIZE(sp->charcnt+1); #endif sp->lsis= (LS_INFO *)tzinfo_buf; @@ -877,8 +877,9 @@ sec_since_epoch(int year, int mon, int mday, int hour, int min ,int sec) RETURN VALUE Seconds in UTC since Epoch. - 0 in case of error. + 0 in case of error. In this case *in_dst_time_gap is also set */ + static my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t, const TIME_ZONE_INFO *sp, my_bool *in_dst_time_gap) @@ -887,12 +888,14 @@ TIME_to_gmt_sec(const MYSQL_TIME *t, const TIME_ZONE_INFO *sp, uint saved_seconds; uint i; int shift= 0; - DBUG_ENTER("TIME_to_gmt_sec"); + *in_dst_time_gap= 0; if (!validate_timestamp_range(t)) + { + *in_dst_time_gap= 1; // Mark error DBUG_RETURN(0); - + } /* We need this for correct leap seconds handling */ if (t->second < SECS_PER_MIN) @@ -936,6 +939,7 @@ TIME_to_gmt_sec(const MYSQL_TIME *t, const TIME_ZONE_INFO *sp, This means that source time can't be represented as my_time_t due to limited my_time_t range. */ + *in_dst_time_gap= 1; // Mark error DBUG_RETURN(0); } @@ -952,6 +956,7 @@ TIME_to_gmt_sec(const MYSQL_TIME *t, const TIME_ZONE_INFO *sp, if (local_t > (my_time_t) (TIMESTAMP_MAX_VALUE - shift * SECS_PER_DAY + sp->revtis[i].rt_offset - saved_seconds)) { + *in_dst_time_gap= 1; // Mark error DBUG_RETURN(0); /* my_time_t overflow */ } local_t+= shift * SECS_PER_DAY; @@ -973,7 +978,10 @@ TIME_to_gmt_sec(const MYSQL_TIME *t, const TIME_ZONE_INFO *sp, /* check for TIMESTAMP_MAX_VALUE was already done above */ if (local_t < TIMESTAMP_MIN_VALUE) + { local_t= 0; + *in_dst_time_gap= 1; // Mark error + } DBUG_RETURN(local_t); } @@ -1336,24 +1344,31 @@ Time_zone_offset::Time_zone_offset(long tz_offset_arg): datetime value passed doesn't really exist (i.e. falls into spring time-gap) and is not touched otherwise. - It is not really used in this class. + It is not really used in this class, except + for indicating error RETURN VALUE - Corresponding my_time_t value or 0 in case of error + Corresponding my_time_t value or 0 in case of error. In case of error + *in_dst_time_gap is also set. */ + my_time_t -Time_zone_offset::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const +Time_zone_offset::TIME_to_gmt_sec(const MYSQL_TIME *t, + my_bool *in_dst_time_gap) const { my_time_t local_t; int shift= 0; + *in_dst_time_gap= 0; // Reset /* Check timestamp range.we have to do this as calling function relies on us to make all validation checks here. */ if (!validate_timestamp_range(t)) + { + *in_dst_time_gap= 1; // Mark error return 0; - + } /* Do a temporary shift of the boundary dates to avoid overflow of my_time_t if the time value is near it's @@ -1376,6 +1391,7 @@ Time_zone_offset::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) return local_t; /* range error*/ + *in_dst_time_gap= 1; // Mark error return 0; } diff --git a/sql/unireg.cc b/sql/unireg.cc index 9859d27dd3b..acd3c8bdd7c 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -1043,7 +1043,6 @@ static bool make_empty_rec(THD *thd, File file,enum legacy_db_type table_type, } table.in_use= thd; - table.s->db_low_byte_first= handler->low_byte_first(); table.s->blob_ptr_size= portable_sizeof_char_ptr; null_count=0; diff --git a/sql/unireg.h b/sql/unireg.h index ccdbb650485..8fe37040196 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -72,10 +72,13 @@ #define MAX_BIT_FIELD_LENGTH 64 /* Max length in bits for bit fields */ #define MAX_DATE_WIDTH 10 /* YYYY-MM-DD */ -#define MAX_TIME_WIDTH 23 /* -DDDDDD HH:MM:SS.###### */ +#define MIN_TIME_WIDTH 9 /* HHH:MM:SS */ +#define MAX_TIME_WIDTH 16 /* -DDDDDD HH:MM:SS */ +#define MAX_TIME_FULL_WIDTH 23 /* -DDDDDD HH:MM:SS.###### */ #define MAX_DATETIME_FULL_WIDTH 29 /* YYYY-MM-DD HH:MM:SS.###### AM */ #define MAX_DATETIME_WIDTH 19 /* YYYY-MM-DD HH:MM:SS */ #define MAX_DATETIME_COMPRESSED_WIDTH 14 /* YYYYMMDDHHMMSS */ +#define MAX_DATETIME_PRECISION 6 #define MAX_TABLES (sizeof(table_map)*8-3) /* Max tables in join */ #define PARAM_TABLE_BIT (((table_map) 1) << (sizeof(table_map)*8-3)) |