diff options
author | Sergei Golubchik <sergii@pisem.net> | 2011-03-01 13:24:36 +0100 |
---|---|---|
committer | Sergei Golubchik <sergii@pisem.net> | 2011-03-01 13:24:36 +0100 |
commit | a8a757c6bb32bbf291afdf33df861127489889ab (patch) | |
tree | cee7858869789964ad21b2b90b420f1128d9a10d /sql | |
parent | 7e0e4b00c971b81b5ffd18db4f1ea977233e98be (diff) | |
download | mariadb-git-a8a757c6bb32bbf291afdf33df861127489889ab.tar.gz |
wl#173 - temporal types with sub-second resolution
and collateral changes.
* introduce my_hrtime_t, my_timediff_t, and conversion macros
* inroduce TIME_RESULT, but it can only be returned from Item::cmp_type(),
never from Item::result_type()
* pack_time/unpack_time function for "packed" representation of
MYSQL_TIME in a longlong that can be compared
* ADDTIME()/SUBTIME()/+- INTERVAL now work with TIME values
* numbers aren't quoted in EXPLAIN EXTENDED
* new column I_S.COLUMNS.DATETIME_PRECISION
* date/time values are compares to anything as date/time, not as strings or numbers.
* old timestamp(X) is no longer supported
* MYSQL_TIME to string conversion functions take precision as an argument
* unified the warnings from Field_timestamp/datetime/time/date/newdate store methods
* Field_timestamp_hires, Field_datetime_hires, Field_time_hires
* Field_temporal
* Lazy_string class to pass a value (string, number, time) polymorphically down the stack
* make_truncated_value_warning and Field::set_datetime_warning use Lazy_string as an argument, removed char*/int/double variants
* removed Field::can_be_compared_as_longlong(). Use Field::cmp_type() == INT_RESULT instead
* introduced Item::cmp_result() instead of Item::is_datetime() and Item::result_as_longlong()
* in many cases date/time types are treated like other types, not as special cases
* greatly simplified Arg_comparator (regarding date/time/year code)
* SEC_TO_TIME is real function, not integer.
* microsecond precision in NOW, CURTIME, etc
* Item_temporal. All items derived from it only provide get_date, but no val* methods
* replication of NOW(6)
* Protocol::store(time) now takes the precision as an argument
* @@TIMESTAMP is a double
client/mysqlbinlog.cc:
remove unneded casts
include/my_sys.h:
introduce my_hrtime_t, my_timediff_t, and conversion macros
include/my_time.h:
pack_time/unpack_time, etc.
convenience functions to work with MYSQL_TIME::second_part
libmysql/libmysql.c:
str_to_time() is gone. str_to_datetime() does it now.
my_TIME_to_str() takes the precision as an argument
mysql-test/include/ps_conv.inc:
time is not equal to datetime anymore
mysql-test/r/distinct.result:
a test for an old MySQL bug
mysql-test/r/explain.result:
numbers aren't quoted in EXPLAIN EXTENDED
mysql-test/r/func_default.result:
numbers aren't quoted in EXPLAIN EXTENDED
mysql-test/r/func_sapdb.result:
when decimals=NOT_FIXED_DEC it means "not fixed" indeed
mysql-test/r/func_test.result:
numbers aren't quoted in EXPLAIN EXTENDED
mysql-test/r/func_time.result:
ADDTIME()/SUBTIME()/+- INTERVAL now work with TIME values
mysql-test/r/having.result:
numbers aren't quoted in EXPLAIN EXTENDED
mysql-test/r/information_schema.result:
new column I_S.COLUMNS.DATETIME_PRECISION
mysql-test/r/join_outer.result:
numbers aren't quoted in EXPLAIN EXTENDED
mysql-test/r/metadata.result:
TIMESTAMP no longer has zerofill flag
mysql-test/r/range.result:
invalid datetime is not compared with as a string
mysql-test/r/select.result:
NO_ZERO_IN_DATE, etc only affect storage - according to the manual
numbers aren't quoted in EXPLAIN EXTENDED
mysql-test/r/subselect.result:
numbers aren't quoted in EXPLAIN EXTENDED
mysql-test/r/sysdate_is_now.result:
when decimals=NOT_FIXED_DEC it means "not fixed" indeed
mysql-test/r/type_blob.result:
TIMESTAMP(N) is not deprecated
mysql-test/r/type_timestamp.result:
old TIMESTAMP(X) semantics is not supported anymore
mysql-test/r/union.result:
numbers aren't quoted in EXPLAIN EXTENDED
mysql-test/r/varbinary.result:
numbers aren't quoted in EXPLAIN EXTENDED
mysql-test/t/distinct.test:
test for an old MySQL bug
mysql-test/t/func_time.test:
+- INTERVAL now works with TIME values
mysql-test/t/select.test:
typo
mysql-test/t/subselect.test:
only one error per statement, please
mysql-test/t/system_mysql_db_fix40123.test:
old timestamp(X) is no longer supported
mysql-test/t/system_mysql_db_fix50030.test:
old timestamp(X) is no longer supported
mysql-test/t/system_mysql_db_fix50117.test:
old timestamp(X) is no longer supported
mysql-test/t/type_blob.test:
old timestamp(X) is no longer supported
mysql-test/t/type_timestamp.test:
old timestamp(X) is no longer supported
mysys/my_getsystime.c:
functions to get the time with microsecond precision
mysys/my_init.c:
move the my_getsystime.c initialization code to my_getsystime.c
mysys/my_static.c:
no need to make these variables extern
mysys/my_static.h:
no need to make these variables extern
scripts/mysql_system_tables.sql:
old timestamp(X) is no longer supported
scripts/mysql_system_tables_fix.sql:
old timestamp(X) is no longer supported
scripts/mysqlhotcopy.sh:
old timestamp(X) is no longer supported
sql-common/my_time.c:
* call str_to_time from str_to_datetime, as appropriate
* date/time to string conversions take precision as an argument
* number_to_time()
* TIME_to_double()
* pack_time() and unpack_time()
sql/event_data_objects.cc:
cast is not needed
my_datetime_to_str() takes precision as an argument
sql/event_db_repository.cc:
avoid dangerous downcast (because the pointer is
not always Field_timestamp, see events_1.test)
sql/event_queue.cc:
avoid silly double-work for cond_wait
(having an endpoint of wait, subtract the current time to get the timeout,
and use set_timespec() macro to fill in struct timespec, by adding the current
time to the timeout)
sql/field.cc:
* remove virtual Field::get_time(), everyone should use only Field::get_date()
* remove lots of #ifdef WORDS_BIGENDIAN
* unified the warnings from Field_timestamp/datetime/time/date/newdate store methods
* Field_timestamp_hires, Field_datetime_hires, Field_time_hires
* Field_temporal
* make_truncated_value_warning and Field::set_datetime_warning use Lazy_string as an argument, removed char*/int/double variants
sql/field.h:
* remove virtual Field::get_time(), everyone should use only Field::get_date()
* remove lots of #ifdef WORDS_BIGENDIAN
* unified the warnings from Field_timestamp/datetime/time/date/newdate store methods
* Field_timestamp_hires, Field_datetime_hires, Field_time_hires
* Field_temporal
* make_truncated_value_warning and Field::set_datetime_warning use Lazy_string as an argument, removed char*/int/double variants
* removed Field::can_be_compared_as_longlong(). Use Field::cmp_type() == INT_RESULT instead
sql/filesort.cc:
TIME_RESULT, cmp_time()
sql/item.cc:
* numbers aren't quoted in EXPLAIN EXTENDED
* Item::cmp_result() instead of Item::is_datetime() and Item::result_as_longlong()
* virtual Item::get_time() is gone
* Item_param::field_type() is set correctly
* Item_datetime, for a datetime constant
* time to anything is compared as a time
* Item_cache::print() prints the value is available
* bug fixed in Item_cache_int::val_str()
sql/item.h:
* Item::print_value(), to be used from Item_xxx::print() when needed
* Item::cmp_result() instead of Item::is_datetime() and Item::result_as_longlong()
* virtual Item::get_time() is gone
* Item_datetime, for a datetime constant
* better default for cast_to_int_type()
* Item_cache objects now *always* have the field_type() set
sql/item_cmpfunc.cc:
* get_year_value, get_time_value are gone. get_datetime_value does it all
* get_value_a_func, get_value_b_func are gone
* can_compare_as_dates() is gone too, TIME_RESULT is used instead
* cmp_type() instead or result_type() when doing a comparison
* compare_datetime and compate_e_datetime in the comparator_matrix, is_nulls_eq is gone
* Item::cmp_result() instead of Item::is_datetime() and Item::result_as_longlong()
sql/item_cmpfunc.h:
greatly simplified Arg_comparator
sql/item_create.cc:
* fix a bug in error messages in CAST
sql/item_func.cc:
Item::cmp_result() instead of Item::is_datetime() and Item::result_as_longlong()
mention all possibitiles in switch over Item_result values, or use default:
sql/item_row.h:
overwrite the default cmp_type() for Item_row,
as no MYSQL_TYPE_xxx value corresponds to ROW_RESULT
sql/item_timefunc.cc:
rewrite make_datetime to support precision argument
SEC_TO_TIME is real function, not integer.
many functions that returned temporal values had duplicate code in val_* methods,
some of them did not have get_date() which resulted in unnecessary date->str->date conversions.
Now they all are derived from Item_temporal_func and *only* provide get_date, not val* methods.
many fixes to set decimals (datetime precision) correctly.
sql/item_timefunc.h:
SEC_TO_TIME is real function, not integer.
many functions that returned temporal values had duplicate code in val_* methods,
some of them did not have get_date() which resulted in unnecessary date->str->date conversions.
Now they all are derived from Item_temporal_func and *only* provide get_date, not val* methods.
many fixes to set decimals (datetime precision) correctly.
sql/log_event.cc:
replication of NOW(6)
sql/log_event.h:
replication of NOW(6)
sql/mysql_priv.h:
Lazy_string class to pass a value (string, number, time) polymorphically down the stack.
make_truncated_value_warning() that uses it.
sql/mysqld.cc:
datetime in Arg_comparator::comparator_matrix
sql/opt_range.cc:
cleanup: don't disable warnings before calling save_in_field_no_warnings()
sql/protocol.cc:
Protocol::store(time) now takes the precision as an argument
sql/protocol.h:
Protocol::store(time) now takes the precision as an argument
sql/rpl_rli.cc:
small cleanup
sql/set_var.cc:
SET TIMESTAMP=double
sql/set_var.h:
@@TIMESTAMP is a double
sql/share/errmsg.txt:
precision and scale are unsigned
sql/slave.cc:
replication of NOW(6)
sql/sp_head.cc:
cleanup
sql/sql_class.cc:
support for NOW(6)
sql/sql_class.h:
support for NOW(6)
sql/sql_insert.cc:
support for NOW(6)
sql/sql_select.cc:
use item->cmp_type().
move a comment where it belongs
sql/sql_show.cc:
new column I_S.COLUMNS.DATETIME_PRECISION
sql/sql_yacc.yy:
TIME(X), DATETIME(X), cast, NOW(X), CURTIME(X), etc
sql/time.cc:
fix date_add_interval() to support MYSQL_TIMESTAMP_TIME argument
storage/myisam/ha_myisam.cc:
TIMESTAMP no longer carries ZEROFIELD flag, still we keep MYI file compatible.
strings/my_vsnprintf.c:
warnings
tests/mysql_client_test.c:
old timestamp(X) does not work anymore
datetime is no longer equal to time
Diffstat (limited to 'sql')
45 files changed, 2295 insertions, 3319 deletions
diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 6119a97dbf9..da502b9e639 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -928,7 +928,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)); @@ -1131,7 +1131,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++; @@ -1191,7 +1191,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 753e9d21b65..bf17c94df3e 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -231,6 +231,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. @@ -317,7 +320,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) { @@ -671,8 +674,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..04416232eb4 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; /* diff --git a/sql/event_queue.cc b/sql/event_queue.cc index 04d4f858b43..9ea5ff6e79f 100644 --- a/sql/event_queue.cc +++ b/sql/event_queue.cc @@ -513,9 +513,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"); @@ -592,14 +593,13 @@ Event_queue::get_top_for_execution_if_time(THD *thd, thd->set_current_time(); /* Get current time */ next_activation_at= top->execute_at; - if (next_activation_at > thd->query_start()) + if (next_activation_at >thd->query_start()) { /* Not yet time for top event, wait on condition with time or until signaled. Release LOCK_queue while waiting. */ - struct timespec top_time; - set_timespec(top_time, next_activation_at - thd->query_start()); + struct timespec top_time= { next_activation_at, 0 }; 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 724f8e0af73..43e210d1bc9 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1735,18 +1735,8 @@ bool Field::get_date(MYSQL_TIME *ltime,uint fuzzydate) char buff[40]; String tmp(buff,sizeof(buff),&my_charset_bin),*res; if (!(res=val_str(&tmp)) || - str_to_datetime_with_warn(res->ptr(), res->length(), - ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR) - return 1; - 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)) + str_to_datetime_with_warn(res->ptr(), res->length(), + ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR) return 1; return 0; } @@ -1762,7 +1752,7 @@ int Field::store_time(MYSQL_TIME *ltime, timestamp_type type_arg) { ASSERT_COLUMN_MARKED_FOR_WRITE; 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); } @@ -3151,13 +3141,9 @@ 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) - { + if (BIGENDIAN && table->s->db_low_byte_first) int2store(ptr, store_tmp); - } else -#endif shortstore(ptr, (short) store_tmp); return error; } @@ -3203,13 +3189,9 @@ int Field_short::store(double nr) else res=(int16) (int) nr; } -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { + if (BIGENDIAN && table->s->db_low_byte_first) int2store(ptr,res); - } else -#endif shortstore(ptr,res); return error; } @@ -3258,13 +3240,9 @@ int Field_short::store(longlong nr, bool unsigned_val) else res=(int16) nr; } -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { + if (BIGENDIAN && table->s->db_low_byte_first) int2store(ptr,res); - } else -#endif shortstore(ptr,res); return error; } @@ -3274,11 +3252,9 @@ double Field_short::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; short j; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) + if (BIGENDIAN && table->s->db_low_byte_first) j=sint2korr(ptr); else -#endif shortget(j,ptr); return unsigned_flag ? (double) (unsigned short) j : (double) j; } @@ -3287,11 +3263,9 @@ longlong Field_short::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; short j; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) + if (BIGENDIAN && table->s->db_low_byte_first) j=sint2korr(ptr); else -#endif shortget(j,ptr); return unsigned_flag ? (longlong) (unsigned short) j : (longlong) j; } @@ -3307,11 +3281,9 @@ 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) + if (BIGENDIAN && table->s->db_low_byte_first) j=sint2korr(ptr); else -#endif shortget(j,ptr); if (unsigned_flag) @@ -3335,14 +3307,12 @@ 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) + if (BIGENDIAN && table->s->db_low_byte_first) { a=sint2korr(a_ptr); b=sint2korr(b_ptr); } else -#endif { shortget(a,a_ptr); shortget(b,b_ptr); @@ -3356,8 +3326,7 @@ 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 (BIGENDIAN && !table->s->db_low_byte_first) { if (unsigned_flag) to[0] = ptr[0]; @@ -3366,7 +3335,6 @@ void Field_short::sort_string(uchar *to,uint length __attribute__((unused))) to[1] = ptr[1]; } else -#endif { if (unsigned_flag) to[0] = ptr[1]; @@ -3588,13 +3556,9 @@ 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) - { + if (BIGENDIAN && table->s->db_low_byte_first) int4store(ptr, store_tmp); - } else -#endif longstore(ptr, store_tmp); return error; } @@ -3640,13 +3604,9 @@ 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) - { + if (BIGENDIAN && table->s->db_low_byte_first) int4store(ptr,res); - } else -#endif longstore(ptr,res); return error; } @@ -3693,13 +3653,9 @@ 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) - { + if (BIGENDIAN && table->s->db_low_byte_first) int4store(ptr,res); - } else -#endif longstore(ptr,res); return error; } @@ -3709,11 +3665,9 @@ double Field_long::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; int32 j; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) + if (BIGENDIAN && table->s->db_low_byte_first) j=sint4korr(ptr); else -#endif longget(j,ptr); return unsigned_flag ? (double) (uint32) j : (double) j; } @@ -3724,11 +3678,9 @@ 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) + if (BIGENDIAN && table->s->db_low_byte_first) j=sint4korr(ptr); else -#endif longget(j,ptr); return unsigned_flag ? (longlong) (uint32) j : (longlong) j; } @@ -3743,11 +3695,9 @@ 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) + if (BIGENDIAN && table->s->db_low_byte_first) j=sint4korr(ptr); else -#endif longget(j,ptr); if (unsigned_flag) @@ -3770,14 +3720,12 @@ 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) + if (BIGENDIAN && table->s->db_low_byte_first) { a=sint4korr(a_ptr); b=sint4korr(b_ptr); } else -#endif { longget(a,a_ptr); longget(b,b_ptr); @@ -3789,8 +3737,7 @@ 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 (BIGENDIAN && !table->s->db_low_byte_first) { if (unsigned_flag) to[0] = ptr[0]; @@ -3801,7 +3748,6 @@ void Field_long::sort_string(uchar *to,uint length __attribute__((unused))) to[3] = ptr[3]; } else -#endif { if (unsigned_flag) to[0] = ptr[3]; @@ -3844,13 +3790,9 @@ 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) - { + if (BIGENDIAN && table->s->db_low_byte_first) int8store(ptr,tmp); - } else -#endif longlongstore(ptr,tmp); return error; } @@ -3896,13 +3838,9 @@ 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) - { + if (BIGENDIAN && table->s->db_low_byte_first) int8store(ptr,res); - } else -#endif longlongstore(ptr,res); return error; } @@ -3927,13 +3865,9 @@ int Field_longlong::store(longlong nr, bool unsigned_val) } } -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { + if (BIGENDIAN && table->s->db_low_byte_first) int8store(ptr,nr); - } else -#endif longlongstore(ptr,nr); return error; } @@ -3943,13 +3877,9 @@ double Field_longlong::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; longlong j; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { + if (BIGENDIAN && table->s->db_low_byte_first) j=sint8korr(ptr); - } else -#endif longlongget(j,ptr); /* The following is open coded to avoid a bug in gcc 3.3 */ if (unsigned_flag) @@ -3965,11 +3895,9 @@ longlong Field_longlong::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; longlong j; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) + if (BIGENDIAN && table->s->db_low_byte_first) j=sint8korr(ptr); else -#endif longlongget(j,ptr); return j; } @@ -3984,11 +3912,9 @@ 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) + if (BIGENDIAN && table->s->db_low_byte_first) j=sint8korr(ptr); else -#endif longlongget(j,ptr); length=(uint) (cs->cset->longlong10_to_str)(cs,to,mlength, @@ -4010,14 +3936,12 @@ 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) + if (BIGENDIAN && table->s->db_low_byte_first) { a=sint8korr(a_ptr); b=sint8korr(b_ptr); } else -#endif { longlongget(a,a_ptr); longlongget(b,b_ptr); @@ -4030,8 +3954,7 @@ 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 (BIGENDIAN && !table->s->db_low_byte_first) { if (unsigned_flag) to[0] = ptr[0]; @@ -4046,7 +3969,6 @@ void Field_longlong::sort_string(uchar *to,uint length __attribute__((unused))) to[7] = ptr[7]; } else -#endif { if (unsigned_flag) to[0] = ptr[7]; @@ -4082,8 +4004,7 @@ Field_real::pack(uchar *to, const uchar *from, { DBUG_ENTER("Field_real::pack"); DBUG_ASSERT(max_length >= pack_length()); -#ifdef WORDS_BIGENDIAN - if (low_byte_first != table->s->db_low_byte_first) + if (BIGENDIAN && low_byte_first != table->s->db_low_byte_first) { const uchar *dptr= from + pack_length(); while (dptr-- > from) @@ -4091,7 +4012,6 @@ Field_real::pack(uchar *to, const uchar *from, DBUG_RETURN(to); } else -#endif DBUG_RETURN(Field::pack(to, from, max_length, low_byte_first)); } @@ -4100,8 +4020,7 @@ 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) + if (BIGENDIAN && low_byte_first != table->s->db_low_byte_first) { const uchar *dptr= from + pack_length(); while (dptr-- > from) @@ -4109,7 +4028,6 @@ Field_real::unpack(uchar *to, const uchar *from, DBUG_RETURN(from + pack_length()); } else -#endif DBUG_RETURN(Field::unpack(to, from, param_data, low_byte_first)); } @@ -4140,13 +4058,9 @@ int Field_float::store(double nr) int error= truncate(&nr, FLT_MAX); float j= (float)nr; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { + if (BIGENDIAN && table->s->db_low_byte_first) float4store(ptr,j); - } else -#endif memcpy_fixed(ptr,(uchar*) &j,sizeof(j)); return error; } @@ -4163,13 +4077,9 @@ double Field_float::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; float j; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { + if (BIGENDIAN && table->s->db_low_byte_first) float4get(j,ptr); - } else -#endif memcpy_fixed((uchar*) &j,ptr,sizeof(j)); return ((double) j); } @@ -4177,13 +4087,9 @@ double Field_float::val_real(void) longlong Field_float::val_int(void) { float j; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { + if (BIGENDIAN && table->s->db_low_byte_first) float4get(j,ptr); - } else -#endif memcpy_fixed((uchar*) &j,ptr,sizeof(j)); return (longlong) rint(j); } @@ -4194,13 +4100,9 @@ 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) - { + if (BIGENDIAN && table->s->db_low_byte_first) float4get(nr,ptr); - } else -#endif memcpy_fixed((uchar*) &nr,ptr,sizeof(nr)); uint to_length=max(field_length,70); @@ -4276,14 +4178,12 @@ 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) + if (BIGENDIAN && 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)); @@ -4296,13 +4196,9 @@ 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) - { + if (BIGENDIAN && table->s->db_low_byte_first) float4get(nr,ptr); - } else -#endif memcpy_fixed(&nr,ptr,sizeof(float)); uchar *tmp= to; @@ -4401,13 +4297,9 @@ int Field_double::store(double nr) ASSERT_COLUMN_MARKED_FOR_WRITE; int error= truncate(&nr, DBL_MAX); -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { + if (BIGENDIAN && table->s->db_low_byte_first) float8store(ptr,nr); - } else -#endif doublestore(ptr,nr); return error; } @@ -4488,13 +4380,9 @@ double Field_double::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; double j; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { + if (BIGENDIAN && table->s->db_low_byte_first) float8get(j,ptr); - } else -#endif doubleget(j,ptr); return j; } @@ -4504,13 +4392,9 @@ longlong Field_double::val_int(void) ASSERT_COLUMN_MARKED_FOR_READ; double j; longlong res; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - { + if (BIGENDIAN && table->s->db_low_byte_first) float8get(j,ptr); - } else -#endif doubleget(j,ptr); /* Check whether we fit into longlong range */ if (j <= (double) LONGLONG_MIN) @@ -4552,13 +4436,9 @@ 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) - { + if (BIGENDIAN && table->s->db_low_byte_first) float8get(nr,ptr); - } else -#endif doubleget(nr,ptr); uint to_length= DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE; @@ -4640,14 +4520,12 @@ 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) + if (BIGENDIAN && table->s->db_low_byte_first) { float8get(a,a_ptr); float8get(b,b_ptr); } else -#endif { doubleget(a, a_ptr); doubleget(b, b_ptr); @@ -4663,13 +4541,9 @@ 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) - { + if (BIGENDIAN && table->s->db_low_byte_first) float8get(nr,ptr); - } else -#endif doubleget(nr,ptr); change_double_for_sort(nr, to); } @@ -4758,12 +4632,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; @@ -4774,20 +4648,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. @@ -4822,136 +4682,136 @@ timestamp_auto_set_type Field_timestamp::get_auto_set_type() const } } +long Field_timestamp::get_timestamp(ulong *sec_part) const +{ + ASSERT_COLUMN_MARKED_FOR_READ; + *sec_part= 0; + if (BIGENDIAN && table && table->s->db_low_byte_first) + return sint4korr(ptr); + long tmp; + longget(tmp,ptr); + return tmp; +} -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; - MYSQL_TIME l_time; - my_time_t tmp= 0; - int error; - bool have_smth_to_conv; + uint error = 0; + my_time_t timestamp; my_bool in_dst_time_gap; - THD *thd= table ? table->in_use : current_thd; - /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ - have_smth_to_conv= (str_to_datetime(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))) + 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, - from, len, MYSQL_TIMESTAMP_DATETIME, !error); + str, MYSQL_TIMESTAMP_DATETIME, !error); error= 1; } else if (in_dst_time_gap) { set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_INVALID_TIMESTAMP, - from, len, MYSQL_TIMESTAMP_DATETIME, !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 ? table->in_use : current_thd; + 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 ? table->in_use : current_thd; + + /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ + have_smth_to_conv= (str_to_datetime(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_dbl str(nr); + THD *thd= table ? table->in_use : current_thd; + + /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ + longlong 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))*1e6); + return store_TIME_with_warning(thd, &l_time, &str, error, tmp != LL(-1)); } int Field_timestamp::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; MYSQL_TIME l_time; - my_time_t timestamp= 0; int error; - my_bool in_dst_time_gap; + Lazy_string_num str(nr); THD *thd= table ? table->in_use : current_thd; /* 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->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; + uint32 temp= get_timestamp(&sec_part); - if (temp == 0L) // No time - return(0); /* purecov: inspected */ + if (temp == 0 && sec_part == 0) + return(0); thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, (my_time_t)temp); @@ -4960,13 +4820,12 @@ longlong Field_timestamp::val_int(void) time_tmp.minute * 100 + time_tmp.second; } - +static const char *zero_timestamp="0000-00-00 00:00:00.000000"; String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) { - ASSERT_COLUMN_MARKED_FOR_READ; - uint32 temp, temp2; - MYSQL_TIME time_tmp; + uint32 temp2; THD *thd= table ? table->in_use : current_thd; + MYSQL_TIME time_tmp; char *to; val_buffer->alloc(field_length+1); @@ -4974,16 +4833,12 @@ 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; + uint32 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 @@ -5036,16 +4891,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->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; + uint32 temp= get_timestamp(&sec_part); + if (temp == 0 && sec_part == 0) { /* Zero time is "000000" */ if (fuzzydate & TIME_NO_ZERO_DATE) return 1; @@ -5054,38 +4904,31 @@ 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) + if (BIGENDIAN && 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); + longget(a,a_ptr); + longget(b,b_ptr); } return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0; } @@ -5093,8 +4936,7 @@ int Field_timestamp::cmp(const uchar *a_ptr, const uchar *b_ptr) void Field_timestamp::sort_string(uchar *to,uint length __attribute__((unused))) { -#ifdef WORDS_BIGENDIAN - if (!table || !table->s->db_low_byte_first) + if (BIGENDIAN && !(table && table->s->db_low_byte_first)) { to[0] = ptr[0]; to[1] = ptr[1]; @@ -5102,7 +4944,6 @@ void Field_timestamp::sort_string(uchar *to,uint length __attribute__((unused))) to[3] = ptr[3]; } else -#endif { to[0] = ptr[3]; to[1] = ptr[2]; @@ -5118,146 +4959,421 @@ 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(); 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) +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, &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); +} + +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; + } +} + +static uint sec_part_bytes[MAX_DATETIME_PRECISION+1]= { 0, 1, 1, 2, 2, 3, 3 }; + +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]); +} + +long Field_timestamp_hires::get_timestamp(ulong *sec_part) const +{ + ASSERT_COLUMN_MARKED_FOR_READ; + *sec_part= 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 ? table->in_use : current_thd; + + thd->time_zone_used= 1; + ulong sec_part; + uint32 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= 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]= (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 ? table->in_use : current_thd; + 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= read_bigendian(a_ptr+4, sec_part_bytes[dec]); + b= mi_uint4korr(b_ptr); + b_sec_part= 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; - long tmp; - int error= 0; - if (nr > (double)TIME_MAX_VALUE) - { - tmp= TIME_MAX_VALUE; - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_TIME); - error= 1; - } - else 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 + else if (temporal_type() == MYSQL_TIMESTAMP_DATE && + (ltime->hour || ltime->minute || ltime->second || ltime->second_part)) { - 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; - } + DBUG_ASSERT((was_cut & MYSQL_TIME_WARN_TRUNCATED) == 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. + QQ Why don't we do the same when storing DATETIME in TIME? + */ + 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 ? table->in_use : current_thd; + 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; - long tmp; int error= 0; - if (nr < (longlong) -TIME_MAX_VALUE && !unsigned_val) + MYSQL_TIME ltime; + longlong tmp; + THD *thd= table ? table->in_use : current_thd; + Lazy_string_dbl str(nr); + + if (nr < 0.0 || nr > 99991231235959.0) { - tmp= -TIME_MAX_VALUE; - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_DATA_OUT_OF_RANGE, nr, - MYSQL_TIMESTAMP_TIME, 1); + tmp= -1; error= 1; } - else if (nr > (longlong) TIME_MAX_VALUE || (nr < 0 && unsigned_val)) + else + 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))*1e6); + 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 ? table->in_use : current_thd; + 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_datetime(from, len, <ime, TIME_TIME_ONLY, + &was_cut) > MYSQL_TIMESTAMP_ERROR; + + if (ltime.month) + ltime.day=0; + ltime.month= ltime.year= 0; + + 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; + + if (l_time.month) + l_time.day=0; + l_time.year= 0; + l_time.month= 0; + + int have_smth_to_conv= !check_time_range(&l_time, &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_dbl 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(nr, <ime, &was_cut); + + return store_TIME_with_warning(<ime, &str, was_cut, have_smth_to_conv); } @@ -5313,7 +5429,7 @@ 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)) + if (!(fuzzydate & (TIME_FUZZY_DATE|TIME_TIME_ONLY))) { push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, @@ -5321,12 +5437,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) @@ -5347,11 +5457,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); } @@ -5375,6 +5483,87 @@ 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 3 + sec_part_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) +{ + ulonglong packed= read_bigendian(ptr, Field_time_hires::pack_length()); + 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 @@ -5502,102 +5691,15 @@ void Field_year::sql_type(String &res) const ** Stored as a 4 byte unsigned int ****************************************************************************/ -int Field_date::store(const char *from, uint len,CHARSET_INFO *cs) +void Field_date::store_TIME(MYSQL_TIME *ltime) { - ASSERT_COLUMN_MARKED_FOR_WRITE; - 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) - { + uint tmp= ltime->year*10000L + ltime->month*100+ltime->day; + if (BIGENDIAN && table && table->s->db_low_byte_first) int4store(ptr,tmp); - } else -#endif longstore(ptr,tmp); - return error; } - -int Field_date::store(double nr) -{ - longlong tmp; - if (nr >= 19000000000000.0 && nr <= 99991231235959.0) - nr=floor(nr/1000000.0); // Timestamp to date - if (nr < 0.0 || nr > 99991231.0) - { - tmp= LL(0); - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_DATA_OUT_OF_RANGE, - nr, MYSQL_TIMESTAMP_DATE); - } - else - tmp= (longlong) rint(nr); - - return Field_date::store(tmp, TRUE); -} - - -int Field_date::store(longlong nr, bool unsigned_val) -{ - ASSERT_COLUMN_MARKED_FOR_WRITE; - MYSQL_TIME not_used; - int error; - longlong initial_nr= nr; - THD *thd= table ? table->in_use : current_thd; - - nr= number_to_datetime(nr, ¬_used, (TIME_FUZZY_DATE | - (thd->variables.sql_mode & - (MODE_NO_ZERO_IN_DATE | - MODE_NO_ZERO_DATE | - MODE_INVALID_DATES))), &error); - - if (nr == LL(-1)) - { - nr= 0; - error= 2; - } - - if (nr >= 19000000000000.0 && nr <= 99991231235959.0) - nr= (longlong) floor(nr/1000000.0); // Timestamp to date - - if (error) - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - error == 2 ? ER_WARN_DATA_OUT_OF_RANGE : - WARN_DATA_TRUNCATED, initial_nr, - MYSQL_TIMESTAMP_DATETIME, 1); - -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) - { - int4store(ptr, nr); - } - else -#endif - longstore(ptr, nr); - return error; -} - - bool Field_date::send_binary(Protocol *protocol) { longlong tmp= Field_date::val_int(); @@ -5613,11 +5715,9 @@ double Field_date::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; int32 j; -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) + if (BIGENDIAN && table && table->s->db_low_byte_first) j=sint4korr(ptr); else -#endif longget(j,ptr); return (double) (uint32) j; } @@ -5627,11 +5727,9 @@ longlong Field_date::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; int32 j; -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) + if (BIGENDIAN && table && table->s->db_low_byte_first) j=sint4korr(ptr); else -#endif longget(j,ptr); return (longlong) (uint32) j; } @@ -5644,11 +5742,9 @@ String *Field_date::val_str(String *val_buffer, MYSQL_TIME ltime; val_buffer->alloc(field_length); int32 tmp; -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) + if (BIGENDIAN && table && table->s->db_low_byte_first) tmp=sint4korr(ptr); else -#endif longget(tmp,ptr); ltime.neg= 0; ltime.year= (int) ((uint32) tmp/10000L % 10000); @@ -5659,24 +5755,15 @@ 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) + if (BIGENDIAN && 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); @@ -5687,8 +5774,7 @@ int Field_date::cmp(const uchar *a_ptr, const uchar *b_ptr) void Field_date::sort_string(uchar *to,uint length __attribute__((unused))) { -#ifdef WORDS_BIGENDIAN - if (!table || !table->s->db_low_byte_first) + if (BIGENDIAN && !(table && table->s->db_low_byte_first)) { to[0] = ptr[0]; to[1] = ptr[1]; @@ -5696,7 +5782,6 @@ void Field_date::sort_string(uchar *to,uint length __attribute__((unused))) to[3] = ptr[3]; } else -#endif { to[0] = ptr[3]; to[1] = ptr[2]; @@ -5717,156 +5802,13 @@ 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; - 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; - 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; - 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; -} - - bool Field_newdate::send_binary(Protocol *protocol) { MYSQL_TIME tm; @@ -5933,12 +5875,6 @@ bool Field_newdate::get_date(MYSQL_TIME *ltime,uint fuzzydate) } -bool Field_newdate::get_time(MYSQL_TIME *ltime) -{ - return Field_newdate::get_date(ltime,0); -} - - int Field_newdate::cmp(const uchar *a_ptr, const uchar *b_ptr) { uint32 a,b; @@ -5969,147 +5905,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; - 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) - { + ulonglong tmp= TIME_to_ulonglong_datetime(ltime); + if (BIGENDIAN && 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; - 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; - 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; } 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); } @@ -6122,11 +5931,9 @@ longlong Field_datetime::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; longlong j; -#ifdef WORDS_BIGENDIAN - if (table && table->s->db_low_byte_first) + if (BIGENDIAN && table && table->s->db_low_byte_first) j=sint8korr(ptr); else -#endif longlongget(j,ptr); return j; } @@ -6135,20 +5942,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 @@ -6200,22 +6003,15 @@ bool Field_datetime::get_date(MYSQL_TIME *ltime, uint fuzzydate) 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); -} - 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) + if (BIGENDIAN && 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); @@ -6226,8 +6022,7 @@ int Field_datetime::cmp(const uchar *a_ptr, const uchar *b_ptr) void Field_datetime::sort_string(uchar *to,uint length __attribute__((unused))) { -#ifdef WORDS_BIGENDIAN - if (!table || !table->s->db_low_byte_first) + if (BIGENDIAN && !(table && table->s->db_low_byte_first)) { to[0] = ptr[0]; to[1] = ptr[1]; @@ -6239,7 +6034,6 @@ void Field_datetime::sort_string(uchar *to,uint length __attribute__((unused))) to[7] = ptr[7]; } else -#endif { to[0] = ptr[7]; to[1] = ptr[6]; @@ -6258,6 +6052,96 @@ void Field_datetime::sql_type(String &res) const res.set_ascii(STRING_WITH_LEN("datetime")); } +void Field_datetime_hires::store_TIME(MYSQL_TIME *ltime) +{ + ulonglong packed= sec_part_shift(pack_time(ltime), dec); + store_bigendian(packed, ptr, Field_datetime_hires::pack_length()); +} + +int Field_datetime_hires::store_decimal(const my_decimal *d) +{ + 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, Field_datetime_hires::pack_length()); + unpack_time(sec_part_unshift(packed, dec), ltime); + return (!(fuzzydate & TIME_FUZZY_DATE) && (!ltime->month || !ltime->day)) ? 1 : 0; +} + +uint32 Field_datetime_hires::pack_length() const +{ + return 5 + sec_part_bytes[dec]; +} + +int Field_datetime_hires::cmp(const uchar *a_ptr, const uchar *b_ptr) +{ + ulonglong a=read_bigendian(a_ptr, Field_datetime_hires::pack_length()); + ulonglong b=read_bigendian(b_ptr, Field_datetime_hires::pack_length()); + return a < b ? -1 : a > b ? 1 : 0; +} + +void Field_datetime_hires::sort_string(uchar *to,uint length __attribute__((unused))) +{ + DBUG_ASSERT(length == Field_datetime_hires::pack_length()); + memcpy(to, ptr, length); +} + + +void Field_datetime_hires::sql_type(String &res) const +{ + CHARSET_INFO *cs=res.charset(); + res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(), + "datetime(%u)", dec)); +} + +void Field_datetime_hires::make_field(Send_field *field) +{ + Field::make_field(field); + field->decimals= dec; +} + /**************************************************************************** ** string type ** A string may be varchar or binary @@ -7571,98 +7455,19 @@ void Field_blob::store_length(uchar *i_ptr, uint32 i_number, bool low_byte_first) { - 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); - } + if (BIGENDIAN && low_byte_first) + store_lowendian(i_number, i_ptr, i_packlength); + else + store_native(i_number, i_ptr, i_packlength); } uint32 Field_blob::get_length(const uchar *pos, uint packlength_arg, bool low_byte_first) { - 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; - } + if (BIGENDIAN && table->s->db_low_byte_first) + return read_lowendian(pos, packlength_arg); + else + return read_native(pos, packlength_arg); } @@ -8001,20 +7806,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*)); @@ -8217,7 +8009,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, table->s->db_low_byte_first); /* put the address of the blob buffer or NULL */ if (length) @@ -8385,39 +8177,10 @@ 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); - } + if (BIGENDIAN && table->s->db_low_byte_first) + store_lowendian(value, ptr, packlength); else -#endif - longlongstore(ptr,value); break; - } + store_native(value, ptr, packlength); } @@ -8503,46 +8266,10 @@ 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 + if (BIGENDIAN && table->s->db_low_byte_first) + return read_lowendian(ptr, packlength); + else + return read_native(ptr, packlength); } @@ -9765,28 +9492,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) { @@ -9835,10 +9548,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: { @@ -9958,14 +9683,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 + ? 3 + sec_part_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 + ? 5 + sec_part_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; @@ -10138,9 +9871,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); @@ -10151,11 +9887,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); @@ -10337,7 +10081,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 @@ -10345,80 +10088,19 @@ 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, +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; 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); - } + make_truncated_value_warning(thd, level, str, 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= sprintf(str_nr, "%g", nr); - make_truncated_value_warning(thd, level, str_nr, str_len, ts_type, - field_name); - } -} diff --git a/sql/field.h b/sql/field.h index cbdfa686ff8..9475e28bc0f 100644 --- a/sql/field.h +++ b/sql/field.h @@ -22,8 +22,13 @@ #pragma interface /* gcc class implementation */ #endif +#ifdef WORDS_BIGENDIAN +#define BIGENDIAN 1 +#else +#define BIGENDIAN 0 +#endif + #define NOT_FIXED_DEC 31 -#define DATETIME_DEC 6 const uint32 max_field_size= (uint32) 4294967295U; class Send_field; @@ -299,14 +304,6 @@ 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 void free() {} virtual Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type); @@ -447,7 +444,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; } @@ -455,16 +452,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); @@ -555,18 +548,14 @@ protected: bool low_byte_first_from, bool low_byte_first_to) { int32 val; -#ifdef WORDS_BIGENDIAN - if (low_byte_first_from) + if (BIGENDIAN && low_byte_first_from) val = sint4korr(from); else -#endif longget(val, from); -#ifdef WORDS_BIGENDIAN - if (low_byte_first_to) + if (BIGENDIAN && low_byte_first_to) int4store(to, val); else -#endif longstore(to, val); } @@ -577,18 +566,14 @@ protected: bool low_byte_first_from, bool low_byte_first_to) { int64 val; -#ifdef WORDS_BIGENDIAN - if (low_byte_first_from) + if (BIGENDIAN && low_byte_first_from) val = sint8korr(from); else -#endif longlongget(val, from); -#ifdef WORDS_BIGENDIAN - if (low_byte_first_to) + if (BIGENDIAN && low_byte_first_to) int8store(to, val); else -#endif longlongstore(to, val); } @@ -681,7 +666,6 @@ public: uint is_equal(Create_field *new_field); }; - /* base class for Field_string, Field_varstring and Field_blob */ class Field_longstr :public Field_str @@ -899,18 +883,14 @@ public: uint max_length, bool low_byte_first) { int16 val; -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) + if (BIGENDIAN && table->s->db_low_byte_first) val = sint2korr(from); else -#endif shortget(val, from); -#ifdef WORDS_BIGENDIAN - if (low_byte_first) + if (BIGENDIAN && low_byte_first) int2store(to, val); else -#endif shortstore(to, val); return to + sizeof(val); } @@ -919,18 +899,14 @@ public: uint param_data, bool low_byte_first) { int16 val; -#ifdef WORDS_BIGENDIAN - if (low_byte_first) + if (BIGENDIAN && low_byte_first) val = sint2korr(from); else -#endif shortget(val, from); -#ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) + if (BIGENDIAN && table->s->db_low_byte_first) int2store(to, val); else -#endif shortstore(to, val); return from + sizeof(val); } @@ -1025,7 +1001,6 @@ public: }; -#ifdef HAVE_LONG_LONG class Field_longlong :public Field_num { public: Field_longlong(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, @@ -1062,7 +1037,6 @@ 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)), @@ -1077,7 +1051,6 @@ public: return unpack_int64(to, from, low_byte_first); } }; -#endif class Field_float :public Field_real { @@ -1188,6 +1161,8 @@ public: 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, @@ -1197,11 +1172,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 *); @@ -1210,9 +1185,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 && @@ -1222,31 +1197,15 @@ public: Field::set_default(); } /* Get TIMESTAMP field value as seconds since begging of Unix Epoch */ - inline long get_timestamp(my_bool *null_value) + virtual long 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) - { + if (BIGENDIAN && table && table->s->db_low_byte_first) int4store(ptr,timestamp); - } else -#endif longstore(ptr,(uint32) 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) @@ -1262,6 +1221,43 @@ public: }; +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 <= MAX_SEC_PART_DIGITS); + } + void sql_type(String &str) const; + long 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, bool low_byte_first) + { return Field::pack(to, from, max_length, low_byte_first); } + const uchar *unpack(uchar* to, const uchar *from, uint param_data, + bool low_byte_first) + { return Field::unpack(to, from, param_data, low_byte_first); } +}; + + class Field_year :public Field_tiny { public: Field_year(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, @@ -1279,39 +1275,51 @@ public: String *val_str(String*,String *); bool send_binary(Protocol *protocol); 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); +}; + +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) @@ -1327,27 +1335,21 @@ public: }; -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 *); @@ -1356,75 +1358,84 @@ 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 <= MAX_SEC_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 *); +}; -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; - } + enum Item_result cmp_type () const { return TIME_RESULT; } + uint decimals() const { return 0; } double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -1433,10 +1444,8 @@ 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) { @@ -1451,6 +1460,81 @@ public: }; +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 <= MAX_SEC_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, bool low_byte_first) + { return Field::pack(to, from, max_length, low_byte_first); } + const uchar *unpack(uchar* to, const uchar *from, uint param_data, + bool low_byte_first) + { return Field::unpack(to, from, param_data, low_byte_first); } +}; + +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 || dec == NOT_FIXED_DEC) + return new Field_timestamp(ptr, MAX_DATETIME_WIDTH, null_ptr, null_bit, + unireg_check, field_name, share, cs); + else + 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 || dec == NOT_FIXED_DEC) + return new Field_time(ptr, MIN_TIME_WIDTH, null_ptr, null_bit, + unireg_check, field_name, cs); + else + 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 || dec == NOT_FIXED_DEC) + return new Field_datetime(ptr, MAX_DATETIME_WIDTH, null_ptr, null_bit, + unireg_check, field_name, cs); + else + return new Field_datetime_hires(ptr, null_ptr, null_bit, + unireg_check, field_name, dec, cs); +} + class Field_string :public Field_longstr { public: bool can_alter_field_type; @@ -1698,9 +1782,6 @@ 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) { @@ -1727,7 +1808,6 @@ public: uint32 get_length(const uchar *ptr, uint packlength, bool low_byte_first); 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); inline void get_ptr(uchar **str) { memcpy_fixed((uchar*) str,ptr+packlength,sizeof(uchar*)); diff --git a/sql/filesort.cc b/sql/filesort.cc index 021cbdd2aad..236a628cce6 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -842,25 +842,26 @@ 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; + item->get_date_result(&buf, TIME_FUZZY_DATE | TIME_INVALID_DATES); + 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); @@ -872,15 +873,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: @@ -890,8 +882,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; @@ -1462,9 +1453,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; @@ -1482,12 +1471,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= @@ -1562,6 +1548,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/item.cc b/sql/item.cc index d88a6e80bfe..7f7e39c9dac 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -461,6 +461,34 @@ 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()) + { + default: + DBUG_ASSERT(0); + case STRING_RESULT: + str->append('\''); + str->append(*ptr); + str->append('\''); + break; + case DECIMAL_RESULT: + case REAL_RESULT: + case INT_RESULT: + str->append(*ptr); + break; + } + } +} + + void Item::cleanup() { DBUG_ENTER("Item::cleanup"); @@ -503,6 +531,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: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + 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_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). @@ -915,13 +982,15 @@ bool Item_string::eq(const Item *item, bool binary_cmp) const bool Item::get_date(MYSQL_TIME *ltime,uint fuzzydate) { - if (result_type() == STRING_RESULT) + if (field_type() == MYSQL_TYPE_TIME) + fuzzydate|= TIME_TIME_ONLY; + if (result_type() == STRING_RESULT || fuzzydate & TIME_TIME_ONLY) { char buff[40]; String tmp(buff,sizeof(buff), &my_charset_bin),*res; if (!(res=val_str(&tmp)) || - str_to_datetime_with_warn(res->ptr(), res->length(), - ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR) + str_to_datetime_with_warn(res->ptr(), res->length(), ltime, + fuzzydate) <= MYSQL_TIMESTAMP_ERROR) goto err; } else @@ -946,22 +1015,14 @@ err: } /** - Get time of first argument.\ + Get time of first argument. As a extra convenience the time structure is reset on error! */ bool Item::get_time(MYSQL_TIME *ltime) { - char buff[40]; - String tmp(buff,sizeof(buff),&my_charset_bin),*res; - if (!(res=val_str(&tmp)) || - str_to_time_with_warn(res->ptr(), res->length(), ltime)) - { - bzero((char*) ltime,sizeof(*ltime)); - return 1; - } - return 0; + return get_date(ltime, TIME_TIME_ONLY); } CHARSET_INFO *Item::default_charset() @@ -987,6 +1048,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; @@ -2085,16 +2147,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; -} - double Item_field::val_result() { if ((null_value=result_field->is_null())) @@ -2698,13 +2750,16 @@ void Item_param::set_time(MYSQL_TIME *tm, timestamp_type time_type, value.time= *tm; value.time.time_type= time_type; + decimals= value.time.second_part > 0 ? MAX_SEC_PART_DIGITS : 0; + 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 >= MAX_SEC_PART_VALUE) { char buff[MAX_DATE_STRING_REP_LENGTH]; - uint length= my_TIME_to_str(&value.time, buff); + uint length= my_TIME_to_str(&value.time, buff, decimals); make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, buff, length, time_type, 0); set_zero_time(&value.time, MYSQL_TIMESTAMP_ERROR); @@ -2713,7 +2768,6 @@ void Item_param::set_time(MYSQL_TIME *tm, timestamp_type time_type, state= TIME_VALUE; maybe_null= 0; max_length= max_length_arg; - decimals= 0; DBUG_VOID_RETURN; } @@ -2790,10 +2844,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: { @@ -2816,6 +2872,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); @@ -2831,6 +2888,7 @@ 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: @@ -2911,21 +2969,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) @@ -3055,7 +3098,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; } @@ -3107,7 +3151,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; @@ -3387,8 +3431,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) { @@ -4651,12 +4694,7 @@ 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 && (cmp_context == STRING_RESULT || cmp_context == (Item_result)-1)) convert_zerofill_number_to_string(&item, (Field_num *)field); else item= this; @@ -4782,21 +4820,6 @@ enum_field_types Item::field_type() const } -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; -} - - String *Item::check_well_formed_result(String *str, bool send_error) { /* Check whether we got a well-formed string */ @@ -4973,16 +4996,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, @@ -5197,12 +5223,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) { @@ -5219,6 +5239,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(); @@ -5643,7 +5679,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; } @@ -5652,7 +5688,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; } } @@ -5733,17 +5769,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); @@ -6839,6 +6865,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; @@ -6952,6 +6980,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) @@ -7009,6 +7040,21 @@ 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); } + 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; @@ -7048,6 +7094,8 @@ 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(); + case TIME_RESULT: + return new Item_cache_int(MYSQL_TYPE_DATETIME); default: // should never be in real life DBUG_ASSERT(0); @@ -7065,6 +7113,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); @@ -7100,7 +7153,7 @@ String *Item_cache_int::val_str(String *str) DBUG_ASSERT(fixed == 1); if (!value_cached && !cache_value()) return NULL; - str->set(value, default_charset()); + str->set_int(value, unsigned_flag, default_charset()); return str; } @@ -7466,7 +7519,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; diff --git a/sql/item.h b/sql/item.h index 57abb43010e..0be8312750d 100644 --- a/sql/item.h +++ b/sql/item.h @@ -569,7 +569,8 @@ public: virtual bool send(Protocol *protocol, String *str); virtual bool eq(const Item *, bool binary_cmp) const; virtual Item_result result_type() const { return REAL_RESULT; } - virtual Item_result cast_to_int_type() const { return result_type(); } + 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; @@ -801,6 +802,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) {} @@ -808,7 +810,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); } /* @@ -1037,15 +1039,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(); virtual Field::geometry_type get_geometry_type() const { return Field::GEOM_GEOMETRY; }; String *check_well_formed_result(String *str, bool send_error= 0); @@ -1514,7 +1507,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); @@ -1523,10 +1515,6 @@ public: bool register_field_in_read_map(uchar *arg); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} void cleanup(); - bool result_as_longlong() - { - return field->can_be_compared_as_longlong(); - } Item_equal *find_item_equal(COND_EQUAL *cond_equal); bool subst_argument_checker(uchar **arg); Item *equal_fields_propagator(uchar *arg); @@ -1671,6 +1659,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; } @@ -1678,7 +1667,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); @@ -1777,13 +1765,12 @@ 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; } @@ -1791,6 +1778,19 @@ public: }; +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 val_int(); } + void set(longlong packed); +}; + + /* decimal (fixed point) constant */ class Item_decimal :public Item_num { @@ -2071,8 +2071,9 @@ 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) { } enum_field_types field_type() const { return date_time_field_type; } @@ -2286,10 +2287,6 @@ public: (this->*processor)(arg); } 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(); } @@ -2322,12 +2319,6 @@ public: if (ref && result_type() == ROW_RESULT) (*ref)->bring_value(); } - bool get_time(MYSQL_TIME *ltime) - { - DBUG_ASSERT(fixed); - return (*ref)->get_time(ltime); - } - }; @@ -3031,7 +3022,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) {} @@ -3043,7 +3034,6 @@ 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(); }; @@ -3052,7 +3042,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(); @@ -3069,7 +3059,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 5302406e270..f2935137de7 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 @@ -488,48 +485,27 @@ 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->is_context_analysis_only()) + if (functype() != LIKE_FUNC && !thd->is_context_analysis_only()) { - 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 && + convert_constant_item(thd, field_item, &args[!field])) + args[0]->cmp_context= args[1]->cmp_context= INT_RESULT; } } set_cmp_func(); @@ -543,6 +519,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(); @@ -669,16 +647,17 @@ 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; + + flags|= thd->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE); + + 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,164 +705,7 @@ 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->is_context_analysis_only() && - 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; -} - - -/* - 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 - - 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. - - RETURN - obtained value -*/ - -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)) - { - Item_cache_int *cache= new Item_cache_int(); - /* Mark the cache as non-const to prevent re-caching. */ - cache->set_used_tables(1); - cache->store(item, value); - *cache_arg= cache; - *item_arg= cache_arg; - } - return value; + return pack_time(&l_time); } @@ -891,65 +713,15 @@ int Arg_comparator::set_cmp_func(Item_result_field *owner_arg, Item **a1, Item **a2, Item_result type) { - enum enum_date_cmp_type cmp_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 ((cmp_type= 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. - */ - Item_cache_int *cache= new Item_cache_int(); - /* Mark the cache as non-const to prevent re-caching. */ - cache->set_used_tables(1); - if (!(*a)->is_datetime()) - { - cache->store((*a), const_value); - a_cache= cache; - a= (Item **)&a_cache; - } - else - { - cache->store((*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); @@ -957,8 +729,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); @@ -966,45 +740,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. @@ -1026,9 +761,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_arg->is_context_analysis_only() && - (*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); @@ -1046,14 +786,9 @@ 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()]; } @@ -1071,81 +806,113 @@ void Arg_comparator::set_datetime_cmp_func(Item_result_field *owner_arg, DESCRIPTION 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). + + 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) RETURN - obtained value + 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; - - if (item->result_as_longlong()) - { + enum_field_types f_type= warn_item->field_type(); + timestamp_type t_type= + f_type == MYSQL_TYPE_DATE ? MYSQL_TIMESTAMP_DATE : + f_type == MYSQL_TYPE_TIME ? MYSQL_TIMESTAMP_TIME : + MYSQL_TIMESTAMP_DATETIME; + + 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(); + else + { + MYSQL_TIME buf; + item->get_date_result(&buf, TIME_FUZZY_DATE | TIME_INVALID_DATES); + 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) + { + Item *real_item= item->real_item(); + if (!(real_item->type() == Item::FIELD_ITEM && + ((Item_field *)real_item)->field->type() == MYSQL_TYPE_YEAR && + ((Item_field *)real_item)->field->field_length == 4)) + { + if (value < 70) + value+= 100; + if (value <= 1900) + value+= 1900; + } + value*= 13ULL * 32ULL * 24ULL * 60ULL * 60ULL * 1000000ULL; + } + else + { + MYSQL_TIME buf; + int was_cut; + + if (number_to_datetime(value, &buf, TIME_INVALID_DATES|TIME_FUZZY_DATE, + &was_cut) == -1) + { + const Lazy_string_num str(value); + make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + &str, t_type, warn_item->name); + 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 can't 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, 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. + */ + } + break; + } + default: 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) - { - 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. - */ - } - /* - 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)) + if (cache_arg && item->const_item() && item->type() != Item::CACHE_ITEM) { - Item_cache_int *cache= new Item_cache_int(MYSQL_TYPE_DATETIME); - /* Mark the cache as non-const to prevent re-caching. */ - cache->set_used_tables(1); + Item_cache_int *cache= new Item_cache_int(f_type); cache->store(item, value); *cache_arg= cache; *item_arg= cache_arg; @@ -1155,64 +922,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(); - if (!(real_item->type() == Item::FIELD_ITEM && - ((Item_field *)real_item)->field->type() == MYSQL_TYPE_YEAR && - ((Item_field *)real_item)->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 @@ -1224,18 +933,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() @@ -1243,34 +943,43 @@ 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; + + if (set_null) + owner->null_value= 0; + + /* 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() { @@ -2247,9 +1956,7 @@ 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; + compare_as_dates= 0; THD *thd= current_thd; /* @@ -2265,43 +1972,33 @@ 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() doees 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++) { - 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->is_context_analysis_only()) { 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) { /* The following can't be recoded with || as convert_constant_item @@ -2319,27 +2016,46 @@ 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); @@ -2363,8 +2079,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)) @@ -2383,8 +2100,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; @@ -2401,8 +2119,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)) @@ -2421,6 +2140,12 @@ longlong Item_func_between::val_int() { null_value= value >= a; } + break; + } + default: + DBUG_ASSERT(0); + null_value= 1; + return 0; } return (longlong) (!null_value && negated); } @@ -3890,7 +3615,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; /* @@ -3946,14 +3671,14 @@ 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 && - cmp_type != INT_RESULT) + !thd->is_context_analysis_only() && 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 all_converted= TRUE; for (arg=args+1, arg_end=args+arg_count; arg != arg_end ; arg++) @@ -5359,7 +5084,7 @@ Item_equal::Item_equal(Item *c, Item_field *f) const_item_cache= 0; fields.push_back(f); const_item= c; - compare_as_dates= f->is_datetime(); + compare_as_dates= f->cmp_type() == TIME_RESULT; } @@ -5406,7 +5131,7 @@ void Item_equal::add(Item *c, Item_field *f) { DBUG_ASSERT(f); const_item= c; - compare_as_dates= f->is_datetime(); + compare_as_dates= f->cmp_type() == TIME_RESULT; return; } compare_const(c); diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 506de05f0ea..ec5ba8bb616 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -33,32 +33,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) @@ -75,8 +65,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)(); } @@ -99,14 +89,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 && @@ -614,9 +602,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(); @@ -894,6 +880,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 5726e987ef6..ac1102fabbb 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -5044,6 +5044,14 @@ find_qualified_function_builder(THD *thd) return & Create_sp_func::s_singleton; } +static inline 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(); +} Item * create_func_cast(THD *thd, Item *a, Cast_target cast_type, @@ -5051,6 +5059,8 @@ create_func_cast(THD *thd, Item *a, Cast_target cast_type, CHARSET_INFO *cs) { Item *UNINIT_VAR(res); + char buff[1024]; + String buf(buff, sizeof(buff), system_charset_info); switch (cast_type) { case ITEM_CAST_BINARY: @@ -5066,11 +5076,29 @@ 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); - break; case ITEM_CAST_DATETIME: - res= new (thd->mem_root) Item_datetime_typecast(a); + { + uint len; + if (c_len) + { + errno= 0; + len= strtoul(c_len, NULL, 10); + if (errno != 0 || len > MAX_DATETIME_PRECISION) + { + my_error(ER_TOO_BIG_PRECISION, MYF(0), len, + item_name(a, &buf), MAX_DATETIME_PRECISION); + return NULL; + } + } + else + len= NOT_FIXED_DEC; + + if (cast_type == ITEM_CAST_TIME) + res= new (thd->mem_root) Item_time_typecast(a, len); + else + res= new (thd->mem_root) Item_datetime_typecast(a, len); break; + } case ITEM_CAST_DECIMAL: { ulong len= 0; @@ -5083,8 +5111,8 @@ create_func_cast(THD *thd, Item *a, Cast_target cast_type, decoded_size= strtoul(c_len, NULL, 10); if (errno != 0) { - my_error(ER_TOO_BIG_PRECISION, MYF(0), c_len, a->name, - DECIMAL_MAX_PRECISION); + my_error(ER_TOO_BIG_PRECISION, MYF(0), decoded_size, + item_name(a, &buf), DECIMAL_MAX_PRECISION); return NULL; } len= decoded_size; @@ -5097,8 +5125,8 @@ create_func_cast(THD *thd, Item *a, Cast_target cast_type, decoded_size= strtoul(c_dec, NULL, 10); if ((errno != 0) || (decoded_size > UINT_MAX)) { - my_error(ER_TOO_BIG_SCALE, MYF(0), c_dec, a->name, - DECIMAL_MAX_SCALE); + my_error(ER_TOO_BIG_SCALE, MYF(0), decoded_size, + item_name(a, &buf), DECIMAL_MAX_SCALE); return NULL; } dec= decoded_size; @@ -5111,13 +5139,13 @@ create_func_cast(THD *thd, Item *a, Cast_target cast_type, } if (len > DECIMAL_MAX_PRECISION) { - my_error(ER_TOO_BIG_PRECISION, MYF(0), len, a->name, - DECIMAL_MAX_PRECISION); + my_error(ER_TOO_BIG_PRECISION, MYF(0), len, + item_name(a, &buf), DECIMAL_MAX_PRECISION); return 0; } if (dec > DECIMAL_MAX_SCALE) { - my_error(ER_TOO_BIG_SCALE, MYF(0), dec, a->name, + my_error(ER_TOO_BIG_SCALE, MYF(0), dec, item_name(a, &buf), DECIMAL_MAX_SCALE); return 0; } @@ -5135,7 +5163,7 @@ create_func_cast(THD *thd, Item *a, Cast_target cast_type, decoded_size= strtoul(c_len, NULL, 10); if ((errno != 0) || (decoded_size > MAX_FIELD_BLOBLENGTH)) { - my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), "cast as char", MAX_FIELD_BLOBLENGTH); + my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), "cast as char", MAX_FIELD_BLOBLENGTH); return NULL; } len= (int) decoded_size; diff --git a/sql/item_func.cc b/sql/item_func.cc index 8bb1009ac2c..b37156efa02 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -940,8 +940,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; @@ -982,8 +981,7 @@ longlong Item_func_unsigned::val_int() value= 0; 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; @@ -2220,7 +2218,6 @@ 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; @@ -2234,21 +2231,17 @@ void Item_func_min_max::fix_length_and_dec() 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()) + 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) - { + if (compare_as_dates) 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 + @@ -2256,7 +2249,11 @@ 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); } @@ -2265,52 +2262,45 @@ void Item_func_min_max::fix_length_and_dec() 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::cmp_datetimes(MYSQL_TIME *ltime) { longlong UNINIT_VAR(min_max); - uint min_max_idx= 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()) { null_value= 1; - return 0; + return 1; } if ((null_value= args[i]->null_value)) - return 0; + return 1; if (i == 0 || (res < min_max ? cmp_sign : -cmp_sign) > 0) - { min_max= res; - min_max_idx= i; - } } - if (value) - { - *value= min_max; - if (datetime_item->field_type() == MYSQL_TYPE_DATE) - *value/= 1000000L; - } - return min_max_idx; + unpack_time(min_max, ltime); + if (compare_as_dates->field_type() == MYSQL_TYPE_DATE) + ltime->time_type= MYSQL_TIMESTAMP_DATE; + + return 0; } @@ -2319,19 +2309,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 (cmp_datetimes(<ime)) 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; + + char buf[MAX_DATE_STRING_REP_LENGTH]; + int len= my_TIME_to_str(<ime, buf, decimals); + str->copy(buf, len, collation.collation); + return str; } switch (cmp_type) { case INT_RESULT: @@ -2398,9 +2383,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 (cmp_datetimes(<ime)) + return 0; + + return TIME_to_double(<ime); } for (uint i=0; i < arg_count ; i++) { @@ -2425,9 +2412,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 (cmp_datetimes(<ime)) + return 0; + + return TIME_to_ulonglong(<ime); } for (uint i=0; i < arg_count ; i++) { @@ -2453,10 +2442,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 (cmp_datetimes(<ime)) + return 0; + + return date2my_decimal(<ime, dec); } for (uint i=0; i < arg_count ; i++) { @@ -3979,7 +3969,7 @@ double user_var_entry::val_real(my_bool *null_value) } case STRING_RESULT: return my_atof(value); // This is null terminated - case ROW_RESULT: + default: DBUG_ASSERT(1); // Impossible break; } @@ -4010,7 +4000,7 @@ longlong user_var_entry::val_int(my_bool *null_value) const int error; return my_strtoll10(value, (char**) 0, &error);// String is null terminated } - case ROW_RESULT: + default: DBUG_ASSERT(1); // Impossible break; } @@ -4042,7 +4032,7 @@ String *user_var_entry::val_str(my_bool *null_value, String *str, case STRING_RESULT: if (str->copy(value, length, collation.collation)) str= 0; // EOM error - case ROW_RESULT: + default: DBUG_ASSERT(1); // Impossible break; } @@ -4069,7 +4059,7 @@ my_decimal *user_var_entry::val_decimal(my_bool *null_value, my_decimal *val) case STRING_RESULT: str2my_decimal(E_DEC_FATAL_ERROR, value, length, collation.collation, val); break; - case ROW_RESULT: + default: DBUG_ASSERT(1); // Impossible break; } diff --git a/sql/item_func.h b/sql/item_func.h index 26a7e033692..d0f5d8d8d8f 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -757,25 +757,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 *); 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); + bool cmp_datetimes(MYSQL_TIME *ltime); enum_field_types field_type() const { return cached_field_type; } }; diff --git a/sql/item_row.h b/sql/item_row.h index 76d1c875e7d..0b8c09f6c5d 100644 --- a/sql/item_row.h +++ b/sql/item_row.h @@ -65,6 +65,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_sum.cc b/sql/item_sum.cc index 65f8222d38b..d2fd0c4b0eb 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -692,14 +692,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 6335199b8de..aa42d869df5 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) -{ - 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) +static bool make_datetime(MYSQL_TIME *ltime, String *str, uint decimals) { - 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,16 +64,16 @@ 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) +static bool sec_to_time(double seconds, MYSQL_TIME *ltime) { uint sec; bzero((char *)ltime, sizeof(*ltime)); + + ltime->time_type= MYSQL_TIMESTAMP_TIME; if (seconds < 0) { - if (unsigned_flag) - goto overflow; ltime->neg= 1; if (seconds < -3020399) goto overflow; @@ -203,6 +86,7 @@ static bool sec_to_time(longlong seconds, bool unsigned_flag, MYSQL_TIME *ltime) ltime->hour= (uint) (seconds/3600); ltime->minute= sec/60; ltime->second= sec % 60; + ltime->second_part= (ulong)((seconds - trunc(seconds))*1e6); return 0; @@ -211,9 +95,8 @@ overflow: 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); + char buf[100]; + uint len= snprintf(buf, sizeof(buf), "%.80g", ltime->neg ? -seconds: seconds); make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, buf, len, MYSQL_TIMESTAMP_TIME, NullS); @@ -270,7 +153,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 +175,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 +350,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 +358,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; @@ -578,6 +464,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 @@ -1299,7 +1189,12 @@ longlong Item_func_unix_timestamp::val_int() { // 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 0; + ulong unused; + return ((Field_timestamp*) field)->get_timestamp(&unused); + } } if (get_arg0_date(<ime, 0)) @@ -1317,13 +1212,13 @@ longlong Item_func_unix_timestamp::val_int() } -longlong Item_func_time_to_sec::val_int() +double Item_func_time_to_sec::val_real() { DBUG_ASSERT(fixed == 1); MYSQL_TIME ltime; - longlong seconds; + double seconds; (void) get_arg0_time(<ime); - seconds=ltime.hour*3600L+ltime.minute*60+ltime.second; + seconds=ltime.hour*3600L+ltime.minute*60+ltime.second+ltime.second_part/1e6; return ltime.neg ? -seconds : seconds; } @@ -1486,29 +1381,38 @@ 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)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 (longlong) (ltime.year*10000L+ltime.month*100+ltime.day); + return TIME_to_double(<ime); } @@ -1535,19 +1439,6 @@ void Item_func_curdate::fix_length_and_dec() /* 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; } /** @@ -1557,8 +1448,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; } @@ -1569,8 +1459,9 @@ 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()); + thd->time_zone_used= 1; /* We are not flagging this query as using time zone, since it uses fixed UTC-SYSTEM time-zone. @@ -1586,26 +1477,42 @@ 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 > MAX_SEC_PART_DIGITS) + { + my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name()); + return 1; + } + return Item_timefunc::fix_fields(thd, items); } - void Item_func_curtime::fix_length_and_dec() { - 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; + max_length= MAX_TIME_WIDTH + + (decimals ? min(decimals, MAX_SEC_PART_DIGITS)+1 : 0); } +bool Item_func_curtime::get_date(MYSQL_TIME *res, + uint fuzzy_date __attribute__((unused))) +{ + *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 <= MAX_SEC_PART_DIGITS); + if (item->decimals) + { + ltime->second_part= sec_part; + if (item->decimals < MAX_SEC_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 @@ -1614,8 +1521,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; } @@ -1626,33 +1535,33 @@ 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; + if (decimals > MAX_SEC_PART_DIGITS) + { + my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name()); + return 1; + } + return Item_temporal_func::fix_fields(thd, items); } - 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; + max_length= MAX_DATETIME_WIDTH + + (decimals ? min(decimals, MAX_SEC_PART_DIGITS)+1 : 0); } @@ -1663,8 +1572,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; } @@ -1675,8 +1584,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. @@ -1692,13 +1602,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. @@ -1706,99 +1609,36 @@ 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_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))) { + MYSQL_TIME ltime; 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); - 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); + double arg_val= args[0]->val_real(); - 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(arg_val, 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; @@ -1934,23 +1774,11 @@ 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))) - 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; @@ -1990,46 +1818,13 @@ void Item_func_from_unixtime::fix_length_and_dec() { thd= current_thd; collation.set(&my_charset_bin); - decimals= DATETIME_DEC; + decimals= 0; max_length=MAX_DATETIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; maybe_null= 1; thd->time_zone_used= 1; } -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))) { @@ -2050,42 +1845,14 @@ bool Item_func_from_unixtime::get_date(MYSQL_TIME *ltime, 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; + max_length= MAX_DATETIME_WIDTH; + decimals= args[0]->decimals; + if (decimals && decimals != NOT_FIXED_DEC) + max_length+= min(decimals, MAX_SEC_PART_DIGITS) + 1; 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); -} - - bool Item_func_convert_tz::get_date(MYSQL_TIME *ltime, uint fuzzy_date __attribute__((unused))) { @@ -2114,9 +1881,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; @@ -2127,7 +1897,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(); } @@ -2137,8 +1907,7 @@ void Item_date_add_interval::fix_length_and_dec() collation.set(&my_charset_bin); maybe_null=1; - max_length=MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; - value.alloc(max_length); + max_length=MAX_DATETIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; /* The field type for the result of an Item_date function is defined as @@ -2146,7 +1915,11 @@ void Item_date_add_interval::fix_length_and_dec() - 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) @@ -2163,6 +1936,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; + if (decimals) + max_length+= min(decimals, MAX_SEC_PART_DIGITS) + 1; + value.alloc(max_length); } @@ -2172,7 +1959,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); @@ -2185,44 +1972,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; @@ -2304,25 +2053,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)) - { - 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; @@ -2405,7 +2141,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); @@ -2555,75 +2291,20 @@ void Item_char_typecast::fix_length_and_dec() } -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); /* - 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; } -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_arg0_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); @@ -2632,39 +2313,27 @@ bool Item_date_typecast::get_date(MYSQL_TIME *ltime, uint fuzzy_date) return res; } - -bool Item_date_typecast::get_time(MYSQL_TIME *ltime) -{ - bzero((char *)ltime, sizeof(MYSQL_TIME)); - return args[0]->null_value; -} - - -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, TIME_FUZZY_DATE)) + 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. @@ -2675,10 +2344,9 @@ longlong Item_date_typecast::val_int() 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; @@ -2694,65 +2362,22 @@ 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; + max_length= MAX_DATETIME_WIDTH; + decimals= NOT_FIXED_DEC; maybe_null= 1; /* @@ -2773,6 +2398,8 @@ 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; + if (decimals) + max_length+= min(decimals, MAX_SEC_PART_DIGITS) + 1; } /** @@ -2785,38 +2412,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); /* @@ -2824,35 +2451,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= MAX_SEC_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, &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); } @@ -2885,13 +2517,14 @@ 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); null_value= 0; if (args[0]->get_time(&l_time1) || @@ -2917,14 +2550,16 @@ String *Item_func_timediff::val_str(String *str) 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; + *ltime= l_time3; + check_time_range(ltime, &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); } /** @@ -2933,10 +2568,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(); @@ -2947,12 +2581,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)))) + second < 0 || second > 59))) return 0; - 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) @@ -2960,22 +2593,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) + @@ -2985,12 +2618,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; } @@ -3272,9 +2900,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) @@ -3292,36 +2920,45 @@ 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_field_type= MYSQL_TYPE_DATETIME; - max_length= MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; - cached_timestamp_type= MYSQL_TIMESTAMP_NONE; + max_length= MAX_DATETIME_WIDTH+MAX_SEC_PART_DIGITS; + cached_timestamp_type= MYSQL_TIMESTAMP_DATETIME; + decimals= AUTO_SEC_PART_DIGITS; 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: + decimals= 6; + /* fall through */ + case TIME_ONLY: cached_timestamp_type= MYSQL_TIMESTAMP_TIME; cached_field_type= MYSQL_TYPE_TIME; max_length= MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN; break; - default: + case DATE_TIME_MICROSECOND: + decimals= 6; + /* fall through */ + case DATE_TIME: cached_timestamp_type= MYSQL_TIMESTAMP_DATETIME; cached_field_type= MYSQL_TYPE_DATETIME; + max_length= MAX_DATETIME_WIDTH; break; } + if (decimals) + max_length+= decimals + 1; } } } @@ -3339,14 +2976,11 @@ bool Item_func_str_to_date::get_date(MYSQL_TIME *ltime, uint fuzzy_date) 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) { @@ -3358,6 +2992,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: @@ -3365,22 +3000,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 47bb9509582..e2e65142902 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -323,147 +323,100 @@ public: }; -class Item_func_time_to_sec :public Item_int_func +class Item_func_time_to_sec :public Item_real_func { public: - Item_func_time_to_sec(Item *item) :Item_int_func(item) {} - longlong val_int(); + Item_func_time_to_sec(Item *item) :Item_real_func(item) {} const char *func_name() const { return "time_to_sec"; } + double val_real(); void fix_length_and_dec() { maybe_null= TRUE; - decimals=0; - max_length=10*MY_CHARSET_BIN_MB_MAXLEN; + decimals=args[0]->decimals; + max_length=17; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} }; -/* - 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; } 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); } }; - -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); } + void fix_length_and_dec() + { + collation.set(&my_charset_bin); + decimals=0; + max_length=MAX_DATE_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; } }; -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); + max_length= MAX_TIME_WIDTH + + (decimals ? min(decimals, MAX_SEC_PART_DIGITS)+1 : 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); + Item_func_curtime(uint dec) :Item_timefunc() { decimals= dec; } + bool fix_fields(THD *, Item **); void fix_length_and_dec(); + 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; } }; 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); }; @@ -472,8 +425,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); }; @@ -481,14 +433,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; @@ -515,20 +464,12 @@ 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); + Item_func_now(uint dec) :Item_temporal_func() { decimals= dec; } + bool fix_fields(THD *, Item **); 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; @@ -538,8 +479,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; } @@ -549,8 +489,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); }; @@ -563,16 +502,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() { @@ -582,10 +515,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;} @@ -609,13 +542,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); @@ -636,7 +567,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 @@ -648,9 +579,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); @@ -658,29 +587,25 @@ 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 { 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; + if (decimals != NOT_FIXED_DEC && decimals > MAX_SEC_PART_DIGITS) + decimals= MAX_SEC_PART_DIGITS; + 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; @@ -689,12 +614,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); @@ -718,43 +641,7 @@ 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; CHARSET_INFO *cast_cs, *from_cs; @@ -762,119 +649,85 @@ class Item_char_typecast :public Item_typecast 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_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_date_typecast(Item *a) :Item_typecast_maybe_null(a) {} + 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); +}; + +class Item_date_typecast :public Item_temporal_typecast +{ +public: + 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; + decimals= 0; + max_length= MAX_DATE_WIDTH; 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"; } - String *val_str(String *str); - bool get_time(MYSQL_TIME *ltime); + bool get_date(MYSQL_TIME *ltime, uint fuzzy_date); 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); - } - 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) + void fix_length_and_dec() { - return save_time_in_field(field); + collation.set(&my_charset_bin); + maybe_null= 1; + max_length= MAX_TIME_WIDTH; + if (decimals && decimals != NOT_FIXED_DEC) + max_length+= min(decimals, MAX_SEC_PART_DIGITS) + 1; } }; -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); - } + bool get_date(MYSQL_TIME *ltime, uint fuzzy_date); 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); + max_length= MAX_DATETIME_WIDTH; + if (decimals && decimals != NOT_FIXED_DEC) + max_length+= min(decimals, MAX_SEC_PART_DIGITS) + 1; } }; -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() @@ -884,11 +737,11 @@ public: /* 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; @@ -896,61 +749,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(); + decimals= NOT_FIXED_DEC; + Item_timefunc::fix_length_and_dec(); maybe_null= 1; } + 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) + :Item_timefunc(a, b, c) { maybe_null= TRUE; } - String *val_str(String *str); const char *func_name() const { return "maketime"; } + bool get_date(MYSQL_TIME *ltime, uint fuzzy_date); }; class Item_func_microsecond :public Item_int_func @@ -1009,32 +840,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 38f4677f06f..6699e42c7f6 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -436,8 +436,7 @@ 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((my_time_t) event_time, 0); /* do a write */ if (table->field[1]->store(user_host, user_host_len, client_cs) || @@ -579,8 +578,7 @@ 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((my_time_t) current_time, 0); if (table->field[1]->store(user_host, user_host_len, client_cs)) goto err; @@ -992,15 +990,16 @@ 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); + current_time= thd->start_time + query_utime/1000000; } else { query_utime= lock_utime= 0; + current_time= my_time(0); } if (!query) @@ -1011,7 +1010,8 @@ 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, + thd->start_time, user_host_buff, user_host_len, query_utime, lock_utime, is_command, query, query_length) || error; diff --git a/sql/log_event.cc b/sql/log_event.cc index d0635ddac1a..4abf56f70f3 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -668,7 +668,8 @@ Log_event::Log_event(THD* thd_arg, uint16 flags_arg, bool using_trans) :log_pos(0), temp_buf(0), exec_time(0), flags(flags_arg), thd(thd_arg) { 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; } @@ -689,7 +690,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 */ @@ -707,6 +709,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) @@ -795,21 +798,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 } @@ -945,7 +940,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 @@ -2070,13 +2065,10 @@ void Log_event::print_timestamp(IO_CACHE* file, time_t* ts) struct tm *res; 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 + } res=localtime(ts); -#endif my_b_printf(file,"%02d%02d%02d %2d:%02d:%02d", res->tm_year % 100, @@ -2360,6 +2352,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 @@ -2452,7 +2453,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 @@ -2587,6 +2588,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; @@ -2803,6 +2805,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 */ @@ -2882,7 +2892,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; @@ -2904,6 +2914,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)); @@ -3165,7 +3180,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(); @@ -3598,7 +3613,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(); int4store(buff + ST_CREATED_OFFSET,created); return (write_header(file, sizeof(buff)) || my_b_safe_write(file, (uchar*) buff, sizeof(buff))); @@ -3997,7 +4012,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, @@ -4703,7 +4718,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)); @@ -7531,7 +7546,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 75f2015a684..9331d7be4b1 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -265,6 +265,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 */ \ @@ -336,6 +337,8 @@ struct sql_ex_info #define Q_INVOKER 11 +#define Q_HRNOW 128 + /* Intvar event post-header */ /* Intvar event data */ @@ -889,7 +892,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 */ @@ -1000,16 +1004,27 @@ 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; + } 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_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 e901f44286c..fce8e5ab09e 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 @@ -1655,7 +1655,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 709a49b2036..2ac2318f242 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -699,6 +699,62 @@ typedef my_bool (*qc_engine_callback)(THD *thd, char *table_key, uint key_length, ulonglong *engine_data); #include "sql_string.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(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(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(String *dst) const { dst->set(num, &my_charset_bin); } +}; + +class Lazy_string_dbl: public Lazy_string +{ + double num; +public: + Lazy_string_dbl(double num_arg) : Lazy_string(), num(num_arg) {} + void copy(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(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); + } +}; + #include "sql_list.h" #include "sql_map.h" #include "my_decimal.h" @@ -2157,17 +2213,25 @@ ulong convert_month_to_period(ulong month); void get_date_from_daynr(long daynr,uint *year, uint *month, uint *day); my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, my_bool *not_exist); -bool str_to_time_with_warn(const char *str,uint length,MYSQL_TIME *l_time); timestamp_type str_to_datetime_with_warn(const char *str, uint length, MYSQL_TIME *l_time, uint flags); void localtime_to_TIME(MYSQL_TIME *to, struct tm *from); void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds); void make_truncated_value_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, - const char *str_val, - uint str_length, timestamp_type time_type, + const Lazy_string *str_val, + timestamp_type time_type, const char *field_name); +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 d17ccc47abb..98cf221b679 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -392,12 +392,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 }; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index d4a2bc3b138..349983682ee 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -692,10 +692,7 @@ static ha_rows check_quick_keys(PARAM *param,uint index,SEL_ARG *key_tree, QUICK_RANGE_SELECT *get_quick_select(PARAM *param,uint index, SEL_ARG *key_tree, MEM_ROOT *alloc = NULL); -static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree, - bool index_read_must_be_used, - bool update_tbl_stats, - double read_time); +static TRP_RANGE *get_key_scans_params(PARAM *, SEL_TREE *, bool, bool, double); static TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, double read_time, @@ -713,11 +710,9 @@ static double get_index_only_read_time(const PARAM* param, ha_rows records, int keynr); #ifndef DBUG_OFF -static void print_sel_tree(PARAM *param, SEL_TREE *tree, key_map *tree_map, - const char *msg); -static void print_ror_scans_arr(TABLE *table, const char *msg, - struct st_ror_scan_info **start, - struct st_ror_scan_info **end); +static void print_sel_tree(PARAM *, SEL_TREE *, key_map *, const char *); +static void print_ror_scans_arr(TABLE *, const char *, struct st_ror_scan_info **, + struct st_ror_scan_info **); static void print_quick(QUICK_SELECT_I *quick, const key_map *needed_reg); #endif @@ -5656,7 +5651,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"); @@ -5824,16 +5818,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) { @@ -5845,7 +5831,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 @@ -5879,10 +5864,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; - } } } @@ -5905,12 +5887,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/protocol.cc b/sql/protocol.cc index db5e15b2acd..273e3b1fd28 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -994,13 +994,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 || @@ -1008,14 +1002,8 @@ bool Protocol_text::store(MYSQL_TIME *tm) field_types[field_pos] == MYSQL_TYPE_TIMESTAMP); field_pos++; #endif - char buff[40]; - uint length; - length= sprintf(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+= sprintf(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); } @@ -1033,27 +1021,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= sprintf(buff, "%s%02ld:%02d:%02d", tm->neg ? "-" : "", - (long) day*24L+(long) tm->hour, (int) tm->minute, - (int) tm->second); - if (tm->second_part) - length+= sprintf(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); } @@ -1210,7 +1186,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; @@ -1223,6 +1199,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 <= MAX_SEC_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; @@ -1240,11 +1220,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; @@ -1253,7 +1233,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; @@ -1262,6 +1241,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 <= MAX_SEC_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 251ba6fbc33..93c730c3a7e 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -89,9 +89,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(); @@ -128,9 +128,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); @@ -163,9 +163,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_rli.cc b/sql/rpl_rli.cc index 99a42bbe818..1031451898d 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -1174,11 +1174,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/set_var.cc b/sql/set_var.cc index 9fbce870dc4..4ce85452f3e 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -2715,38 +2715,38 @@ int set_var_collation_client::update(THD *thd) bool sys_var_timestamp::check(THD *thd, set_var *var) { - time_t val; - var->save_result.ulonglong_value= var->value->val_int(); - val= (time_t) var->save_result.ulonglong_value; - if (val < (time_t) MY_TIME_T_MIN || val > (time_t) MY_TIME_T_MAX) + double val= var->value->val_real(); + if (val < 0 || val > MY_TIME_T_MAX) { my_message(ER_UNKNOWN_ERROR, "This version of MySQL doesn't support dates later than 2038", MYF(0)); return TRUE; } + var->save_result.ulonglong_value= hrtime_from_time(var->value->val_real()); 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; } @@ -3045,10 +3045,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 68cd94a5670..4f982890a63 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -667,8 +667,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 bbae17c4327..c2fa3d12829 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 column '%-.192s'. Maximum is %lu." + ger "Zu großer Skalierungsfaktor %u für Feld '%-.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 column '%-.192s'. Maximum is %lu." + ger "Zu große Genauigkeit %u für Feld '%-.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')" diff --git a/sql/slave.cc b/sql/slave.cc index 644aade517c..ebf0ba22f85 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -2152,7 +2152,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_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 2473abea3c7..d68ec1a0968 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 a61ce7bfd14..7c7751f922f 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -602,7 +602,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), @@ -641,7 +641,7 @@ THD::THD() main_security_ctx.init(); security_ctx= &main_security_ctx; locked=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; @@ -658,7 +658,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; @@ -2338,7 +2338,7 @@ bool select_max_min_finder_subselect::send_data(List<Item> &items) case DECIMAL_RESULT: op= &select_max_min_finder_subselect::cmp_decimal; break; - case ROW_RESULT: + default: // This case should never be choosen DBUG_ASSERT(0); op= 0; diff --git a/sql/sql_class.h b/sql/sql_class.h index 774ae4abac4..1298dd734c9 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1393,7 +1393,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; @@ -1417,7 +1416,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; @@ -1836,6 +1837,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; @@ -1881,6 +1883,7 @@ public: long long_value; ulong ulong_value; ulonglong ulonglong_value; + double double_value; } sys_var_tmp; struct { @@ -2016,27 +2019,44 @@ 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_time() { - if (user_time) + if (user_time.val) { - start_time= user_time; + start_time= hrtime_to_time(user_time); + start_time_sec_part= hrtime_sec_part(user_time); start_utime= utime_after_lock= my_micro_time(); } else - start_utime= utime_after_lock= my_micro_time_and_time(&start_time); + { + my_hrtime_t hrtime; + my_timediff_t timediff; + my_micro_and_hrtime(&timediff, &hrtime); + start_time= hrtime_to_time(hrtime); + start_time_sec_part= hrtime_sec_part(hrtime); + utime_after_lock= start_utime= timediff.val; + } } - inline void set_current_time() { start_time= my_time(MY_WME); } - inline void set_time(time_t t) + inline void set_current_time() { - start_time= user_time= t; + my_hrtime_t hrtime= my_hrtime(); + start_time= hrtime_to_time(hrtime); + start_time_sec_part= hrtime_sec_part(hrtime); + } + inline void set_time(my_hrtime_t t) + { + user_time= t; + start_time= hrtime_to_time(user_time); + start_time_sec_part= hrtime_sec_part(user_time); start_utime= utime_after_lock= my_micro_time(); } - /*TODO: this will be obsolete when we have support for 64 bit my_time_t */ - inline bool is_valid_time() - { - return (start_time < (time_t) MY_TIME_T_MAX); + 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(); } diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index f0735a9e093..12c5478b0ae 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1694,10 +1694,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; @@ -2185,8 +2186,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 @@ -2640,6 +2643,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). diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 7cf64134d70..c45c4b7dd83 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1001,18 +1001,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->enable_slow_log= TRUE; 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; @@ -1518,10 +1506,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) @@ -5767,6 +5752,7 @@ void mysql_reset_thd_for_next_command(THD *thd) 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 @@ -6219,17 +6205,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 5ba375f9710..32e7a9dfcc6 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -487,8 +487,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_select.cc b/sql/sql_select.cc index c17cb946fa3..e02c22c3250 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3342,12 +3342,8 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, (*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 - */ - if (!eq_func) + + if (!eq_func) // eq_func is NEVER true when num_values > 1 { /* Additional optimization: if we're processing @@ -3368,23 +3364,17 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, eq_func= TRUE; } - if (field->result_type() == STRING_RESULT) + /* + 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 (field->cmp_type() == STRING_RESULT) { - if ((*value)->result_type() != STRING_RESULT) - { - if (field->cmp_type() != (*value)->result_type()) - 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()) + if ((*value)->cmp_type() != STRING_RESULT) return; - } + if (((Field_str*)field)->charset() != cond->compare_collation()) + return; } } } @@ -9455,7 +9445,7 @@ 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) || + (l->cmp_type() == TIME_RESULT || /* or of the same result type */ (r->result_type() == l->result_type() && /* and must have the same collation if compared as strings */ @@ -9640,15 +9630,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 diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 9b344204d64..c5df62c09cf 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1930,6 +1930,7 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) Security_context *tmp_sctx= tmp->security_ctx; struct st_my_thread_var *mysys_var; const char *val; + time_t start_time; if ((!tmp->vio_ok() && !tmp->system_thread) || (user && (!tmp_sctx->user || strcmp(tmp_sctx->user, user)))) @@ -1970,8 +1971,9 @@ 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 */ - table->field[5]->store((longlong)(tmp->start_time ? - now - tmp->start_time : 0), FALSE); + + start_time= tmp->start_time; + table->field[5]->store((longlong)(start_time ? now-start_time : 0), 0); /* STATE */ #ifndef EMBEDDED_LIBRARY val= (char*) (tmp->locked ? "Locked" : @@ -3902,7 +3904,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); @@ -3911,7 +3913,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]. @@ -3958,6 +3960,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: @@ -3986,8 +3989,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; } @@ -3995,38 +4003,38 @@ 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); - 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); } @@ -5706,14 +5714,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: @@ -5893,7 +5910,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; @@ -5901,9 +5918,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); @@ -6288,6 +6305,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_table.cc b/sql/sql_table.cc index b919ea9eae7..f11a4e683f1 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -34,26 +34,16 @@ 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 *thd, HA_CREATE_INFO *create_info, - Alter_info *alter_info, - bool tmp_table, - uint *db_options, - handler *file, KEY **key_info_buffer, - uint *key_count, int select_field_count); -static bool -mysql_prepare_alter_table(THD *thd, TABLE *table, - HA_CREATE_INFO *create_info, - Alter_info *alter_info); +static int mysql_prepare_create_table(THD *, HA_CREATE_INFO *, Alter_info *, + bool, uint *, handler *, KEY **, uint *, int); +static bool mysql_prepare_alter_table(THD *, TABLE *, HA_CREATE_INFO *, + Alter_info *); #ifndef DBUG_OFF diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 4e24e69af42..46ba53e7e3e 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -675,10 +675,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. @@ -1317,6 +1317,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_natural_language_mode opt_query_expansion opt_ev_status opt_ev_on_completion ev_on_completion opt_ev_comment ev_alter_on_schedule_completion opt_ev_rename_to opt_ev_sql_stmt + opt_time_precision %type <ulong_num> ulong_num real_ulong_num merge_insert_types @@ -2041,7 +2042,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; @@ -5026,7 +5027,7 @@ type: { $$=MYSQL_TYPE_YEAR; } | DATE_SYM { $$=MYSQL_TYPE_DATE; } - | TIME_SYM + | TIME_SYM opt_field_length { $$=MYSQL_TYPE_TIME; } | TIMESTAMP opt_field_length { @@ -5041,7 +5042,7 @@ type: $$=MYSQL_TYPE_TIMESTAMP; } } - | DATETIME + | DATETIME opt_field_length { $$=MYSQL_TYPE_DATETIME; } | TINYBLOB { @@ -5234,9 +5235,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; @@ -5283,9 +5284,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; } @@ -6849,6 +6850,12 @@ select_alias: | TEXT_STRING_sys { $$=$1; } ; +opt_time_precision: + /* empty */ { $$= 0; } + | '(' ')' { $$= 0; } + | '(' real_ulong_num ')' { $$= $2; }; + ; + optional_braces: /* empty */ {} | '(' ')' {} @@ -7527,13 +7534,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; } @@ -7640,16 +7647,9 @@ function_call_nonkeyword: MYSQL_YYABORT; Lex->safe_to_cache_query=0; } - | CURTIME optional_braces - { - $$= new (YYTHD->mem_root) Item_func_curtime_local(); - if ($$ == NULL) - MYSQL_YYABORT; - Lex->safe_to_cache_query=0; - } - | CURTIME '(' expr ')' + | CURTIME opt_time_precision { - $$= 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; @@ -7680,16 +7680,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; @@ -7737,7 +7730,7 @@ function_call_nonkeyword: if ($$ == NULL) MYSQL_YYABORT; } - | SYSDATE optional_braces + | SYSDATE opt_time_precision { /* Unlike other time-related functions, SYSDATE() is @@ -7748,19 +7741,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; @@ -7784,16 +7767,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; @@ -8409,10 +8392,10 @@ 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->charset= NULL; Lex->dec= (char*)0; } + | DATETIME opt_field_length + { $$=ITEM_CAST_DATETIME; Lex->charset= NULL; Lex->dec= (char*)0; } | DECIMAL_SYM float_options { $$=ITEM_CAST_DECIMAL; Lex->charset= NULL; } ; diff --git a/sql/time.cc b/sql/time.cc index 8b554beb94b..7d23a795413 100644 --- a/sql/time.cc +++ b/sql/time.cc @@ -236,7 +236,8 @@ str_to_datetime_with_warn(const char *str, uint length, MYSQL_TIME *l_time, &was_cut); if (was_cut || ts_type <= MYSQL_TIMESTAMP_ERROR) make_truncated_value_warning(current_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; } @@ -277,25 +278,6 @@ my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, my_bool *in_dst_time_ /* - Convert a time string to a MYSQL_TIME struct and produce a warning - if string was cut during conversion. - - NOTE - See str_to_time() for more info. -*/ -bool -str_to_time_with_warn(const char *str, uint length, MYSQL_TIME *l_time) -{ - int warning; - bool ret_val= str_to_time(str, length, l_time, &warning); - if (ret_val || warning) - make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, - str, length, MYSQL_TIMESTAMP_TIME, NullS); - return ret_val; -} - - -/* Convert a system time structure to TIME */ @@ -687,15 +669,13 @@ 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()); + uint length= (uint) my_time_to_str(l_time, (char*) str->ptr(), 0); str->length(length); str->set_charset(&my_charset_bin); } @@ -713,15 +693,15 @@ 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()); + uint length= (uint) my_datetime_to_str(l_time, (char*) str->ptr(), 0); str->length(length); str->set_charset(&my_charset_bin); } 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]; @@ -729,8 +709,7 @@ void make_truncated_value_warning(THD *thd, MYSQL_ERROR::enum_warning_level leve 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(&str); switch (time_type) { case MYSQL_TIMESTAMP_DATE: @@ -765,14 +744,16 @@ void make_truncated_value_warning(THD *thd, MYSQL_ERROR::enum_warning_level leve /* 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) { long period, sign; - ltime->neg= 0; - - sign= (interval.neg ? -1 : 1); + sign= (interval.neg == ltime->neg ? 1 : -1); switch (int_type) { case INTERVAL_SECOND: @@ -789,35 +770,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 (ltime->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= ltime->day + 32*(ltime->month + 13*ltime->year); + /* Day number from year 0 to 9999-12-31 */ if ((ulonglong) daynr > MAX_DAY_NUMBER) goto invalid_date; @@ -825,7 +800,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); @@ -969,19 +943,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/unireg.h b/sql/unireg.h index 4f6b647964d..50facf51d02 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)) |