maria-developers team mailing list archive
-
maria-developers team
-
Mailing list archive
-
Message #11364
MDEV-13995 MAX(timestamp) returns a wrong result near DST change
Hi Sergei,
Please review a patch for MDEV-13995.
Thanks!
diff --git a/mysql-test/main/timezone2.result b/mysql-test/main/timezone2.result
index 096e996..7b14990 100644
--- a/mysql-test/main/timezone2.result
+++ b/mysql-test/main/timezone2.result
@@ -332,3 +332,198 @@ NULL
#
# End of 5.3 tests
#
+#
+# Start of 10.4 tests
+#
+#
+# MDEV-13995 MAX(timestamp) returns a wrong result near DST change
+#
+SET time_zone='+00:00';
+CREATE TABLE t1 (a TIMESTAMP);
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288477526) /*summer time in Moscow*/);
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288477526+3599) /*winter time in Moscow*/);
+SET time_zone='Europe/Moscow';
+SELECT a, UNIX_TIMESTAMP(a) FROM t1;
+a UNIX_TIMESTAMP(a)
+2010-10-31 02:25:26 1288477526
+2010-10-31 02:25:25 1288481125
+SELECT UNIX_TIMESTAMP(MAX(a)) AS a FROM t1;
+a
+1288481125
+CREATE TABLE t2 (a TIMESTAMP);
+INSERT INTO t2 SELECT MAX(a) AS a FROM t1;
+SELECT a, UNIX_TIMESTAMP(a) FROM t2;
+a UNIX_TIMESTAMP(a)
+2010-10-31 02:25:25 1288481125
+DROP TABLE t2;
+DROP TABLE t1;
+SET time_zone='+00:00';
+CREATE TABLE t1 (a TIMESTAMP);
+CREATE TABLE t2 (a TIMESTAMP);
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288477526) /*summer time in Moscow*/);
+INSERT INTO t2 VALUES (FROM_UNIXTIME(1288477526+3599) /*winter time in Moscow*/);
+SET time_zone='Europe/Moscow';
+SELECT UNIX_TIMESTAMP(t1.a), UNIX_TIMESTAMP(t2.a) FROM t1,t2;
+UNIX_TIMESTAMP(t1.a) UNIX_TIMESTAMP(t2.a)
+1288477526 1288481125
+SELECT * FROM t1,t2 WHERE t1.a < t2.a;
+a a
+2010-10-31 02:25:26 2010-10-31 02:25:25
+DROP TABLE t1,t2;
+BEGIN NOT ATOMIC
+DECLARE a,b TIMESTAMP;
+SET time_zone='+00:00';
+SET a=FROM_UNIXTIME(1288477526);
+SET b=FROM_UNIXTIME(1288481125);
+SELECT a < b;
+SET time_zone='Europe/Moscow';
+SELECT a < b;
+END;
+$$
+a < b
+1
+a < b
+1
+CREATE OR REPLACE FUNCTION f1(uts INT) RETURNS TIMESTAMP
+BEGIN
+DECLARE ts TIMESTAMP;
+DECLARE tz VARCHAR(64) DEFAULT @@time_zone;
+SET time_zone='+00:00';
+SET ts=FROM_UNIXTIME(uts);
+SET time_zone=tz;
+RETURN ts;
+END;
+$$
+SET time_zone='+00:00';
+SELECT f1(1288477526) < f1(1288481125);
+f1(1288477526) < f1(1288481125)
+1
+SET time_zone='Europe/Moscow';
+SELECT f1(1288477526) < f1(1288481125);
+f1(1288477526) < f1(1288481125)
+1
+DROP FUNCTION f1;
+CREATE TABLE t1 (a TIMESTAMP,b TIMESTAMP);
+SET time_zone='+00:00';
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288477526) /*summer time in Mowcow*/,
+FROM_UNIXTIME(1288481125) /*winter time in Moscow*/);
+SELECT *, LEAST(a,b) FROM t1;
+a b LEAST(a,b)
+2010-10-30 22:25:26 2010-10-30 23:25:25 2010-10-30 22:25:26
+SET time_zone='Europe/Moscow';
+SELECT *, LEAST(a,b) FROM t1;
+a b LEAST(a,b)
+2010-10-31 02:25:26 2010-10-31 02:25:25 2010-10-31 02:25:26
+SELECT UNIX_TIMESTAMP(a), UNIX_TIMESTAMP(b), UNIX_TIMESTAMP(LEAST(a,b)) FROM t1;
+UNIX_TIMESTAMP(a) UNIX_TIMESTAMP(b) UNIX_TIMESTAMP(LEAST(a,b))
+1288477526 1288481125 1288477526
+DROP TABLE t1;
+CREATE TABLE t1 (a TIMESTAMP,b TIMESTAMP,c TIMESTAMP);
+SET time_zone='+00:00';
+INSERT INTO t1 VALUES (
+FROM_UNIXTIME(1288477526) /*summer time in Moscow*/,
+FROM_UNIXTIME(1288481125) /*winter time in Moscow*/,
+FROM_UNIXTIME(1288481126) /*winter time in Moscow*/);
+SELECT b BETWEEN a AND c FROM t1;
+b BETWEEN a AND c
+1
+SET time_zone='Europe/Moscow';
+SELECT b BETWEEN a AND c FROM t1;
+b BETWEEN a AND c
+1
+DROP TABLE t1;
+SET time_zone='+00:00';
+CREATE TABLE t1 (a TIMESTAMP);
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288477526) /*summer time in Mowcow*/);
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288481125) /*winter time in Moscow*/);
+SELECT a, UNIX_TIMESTAMP(a) FROM t1 ORDER BY a;
+a UNIX_TIMESTAMP(a)
+2010-10-30 22:25:26 1288477526
+2010-10-30 23:25:25 1288481125
+SELECT COALESCE(a) AS a, UNIX_TIMESTAMP(a) FROM t1 ORDER BY a;
+a UNIX_TIMESTAMP(a)
+2010-10-30 22:25:26 1288477526
+2010-10-30 23:25:25 1288481125
+SET time_zone='Europe/Moscow';
+SELECT a, UNIX_TIMESTAMP(a) FROM t1 ORDER BY a;
+a UNIX_TIMESTAMP(a)
+2010-10-31 02:25:26 1288477526
+2010-10-31 02:25:25 1288481125
+SELECT COALESCE(a) AS a, UNIX_TIMESTAMP(a) FROM t1 ORDER BY a;
+a UNIX_TIMESTAMP(a)
+2010-10-31 02:25:26 1288477526
+2010-10-31 02:25:25 1288481125
+DROP TABLE t1;
+SET time_zone='+00:00';
+CREATE TABLE t1 (a TIMESTAMP);
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288477526) /*summer time in Mowcow*/);
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288481126) /*winter time in Moscow*/);
+SET time_zone='Europe/Moscow';
+SELECT a, UNIX_TIMESTAMP(a) FROM t1 GROUP BY a;
+a UNIX_TIMESTAMP(a)
+2010-10-31 02:25:26 1288477526
+2010-10-31 02:25:26 1288481126
+DROP TABLE t1;
+SET time_zone='+00:00';
+CREATE TABLE t1 (a TIMESTAMP, b TIMESTAMP);
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288477526),FROM_UNIXTIME(1288481126));
+SELECT UNIX_TIMESTAMP(a),UNIX_TIMESTAMP(b),CASE a WHEN b THEN 'eq' ELSE 'ne' END AS x FROM t1;
+UNIX_TIMESTAMP(a) UNIX_TIMESTAMP(b) x
+1288477526 1288481126 ne
+SET time_zone='Europe/Moscow';
+SELECT UNIX_TIMESTAMP(a),UNIX_TIMESTAMP(b),CASE a WHEN b THEN 'eq' ELSE 'ne' END AS x FROM t1;
+UNIX_TIMESTAMP(a) UNIX_TIMESTAMP(b) x
+1288477526 1288481126 ne
+DROP TABLE t1;
+SET time_zone='+00:00';
+CREATE TABLE t1 (a TIMESTAMP, b TIMESTAMP,c TIMESTAMP);
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288477526),FROM_UNIXTIME(1288481126),FROM_UNIXTIME(1288481127));
+SELECT UNIX_TIMESTAMP(a),UNIX_TIMESTAMP(b),a IN (b,c) AS x FROM t1;
+UNIX_TIMESTAMP(a) UNIX_TIMESTAMP(b) x
+1288477526 1288481126 0
+SET time_zone='Europe/Moscow';
+SELECT UNIX_TIMESTAMP(a),UNIX_TIMESTAMP(b),a IN (b,c) AS x FROM t1;
+UNIX_TIMESTAMP(a) UNIX_TIMESTAMP(b) x
+1288477526 1288481126 0
+DROP TABLE t1;
+SET time_zone='+00:00';
+CREATE TABLE t1 (a TIMESTAMP, b TIMESTAMP);
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288477526),FROM_UNIXTIME(1288481126));
+SELECT * FROM t1 WHERE a = (SELECT MAX(b) FROM t1);
+a b
+SELECT * FROM t1 WHERE a = (SELECT MIN(b) FROM t1);
+a b
+SELECT * FROM t1 WHERE a IN ((SELECT MAX(b) FROM t1), (SELECT MIN(b) FROM t1));
+a b
+SET time_zone='Europe/Moscow';
+SELECT * FROM t1 WHERE a = (SELECT MAX(b) FROM t1);
+a b
+SELECT * FROM t1 WHERE a = (SELECT MIN(b) FROM t1);
+a b
+SELECT * FROM t1 WHERE a IN ((SELECT MAX(b) FROM t1), (SELECT MIN(b) FROM t1));
+a b
+DROP TABLE t1;
+SET time_zone='+00:00';
+CREATE TABLE t1 (a TIMESTAMP, b TIMESTAMP);
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1100000000),FROM_UNIXTIME(1200000000));
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1100000001),FROM_UNIXTIME(1200000001));
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288477526),FROM_UNIXTIME(1288481126));
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1300000000),FROM_UNIXTIME(1400000000));
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1300000001),FROM_UNIXTIME(1400000001));
+SELECT * FROM t1 WHERE a = (SELECT MAX(b) FROM t1);
+a b
+SELECT * FROM t1 WHERE a = (SELECT MIN(b) FROM t1);
+a b
+SELECT * FROM t1 WHERE a IN ((SELECT MAX(b) FROM t1), (SELECT MIN(b) FROM t1));
+a b
+SET time_zone='Europe/Moscow';
+SELECT * FROM t1 WHERE a = (SELECT MAX(b) FROM t1);
+a b
+SELECT * FROM t1 WHERE a = (SELECT MIN(b) FROM t1);
+a b
+SELECT * FROM t1 WHERE a IN ((SELECT MAX(b) FROM t1), (SELECT MIN(b) FROM t1));
+a b
+DROP TABLE t1;
+#
+# End of 10.4 tests
+#
diff --git a/mysql-test/main/timezone2.test b/mysql-test/main/timezone2.test
index 7a38610..4103959 100644
--- a/mysql-test/main/timezone2.test
+++ b/mysql-test/main/timezone2.test
@@ -308,3 +308,180 @@ SELECT CONVERT_TZ('2001-10-08 00:00:00', MAKE_SET(0,'+01:00'), '+00:00' );
--echo #
--echo # End of 5.3 tests
--echo #
+
+
+--echo #
+--echo # Start of 10.4 tests
+--echo #
+
+
+--echo #
+--echo # MDEV-13995 MAX(timestamp) returns a wrong result near DST change
+--echo #
+
+# MAX()
+SET time_zone='+00:00';
+CREATE TABLE t1 (a TIMESTAMP);
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288477526) /*summer time in Moscow*/);
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288477526+3599) /*winter time in Moscow*/);
+SET time_zone='Europe/Moscow';
+SELECT a, UNIX_TIMESTAMP(a) FROM t1;
+SELECT UNIX_TIMESTAMP(MAX(a)) AS a FROM t1;
+CREATE TABLE t2 (a TIMESTAMP);
+INSERT INTO t2 SELECT MAX(a) AS a FROM t1;
+SELECT a, UNIX_TIMESTAMP(a) FROM t2;
+DROP TABLE t2;
+DROP TABLE t1;
+
+
+# Comparison
+SET time_zone='+00:00';
+CREATE TABLE t1 (a TIMESTAMP);
+CREATE TABLE t2 (a TIMESTAMP);
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288477526) /*summer time in Moscow*/);
+INSERT INTO t2 VALUES (FROM_UNIXTIME(1288477526+3599) /*winter time in Moscow*/);
+SET time_zone='Europe/Moscow';
+SELECT UNIX_TIMESTAMP(t1.a), UNIX_TIMESTAMP(t2.a) FROM t1,t2;
+SELECT * FROM t1,t2 WHERE t1.a < t2.a;
+DROP TABLE t1,t2;
+
+
+# SP variable comparison
+DELIMITER $$;
+BEGIN NOT ATOMIC
+ DECLARE a,b TIMESTAMP;
+ SET time_zone='+00:00';
+ SET a=FROM_UNIXTIME(1288477526);
+ SET b=FROM_UNIXTIME(1288481125);
+ SELECT a < b;
+ SET time_zone='Europe/Moscow';
+ SELECT a < b;
+END;
+$$
+DELIMITER ;$$
+
+
+# SP function comparison
+DELIMITER $$;
+CREATE OR REPLACE FUNCTION f1(uts INT) RETURNS TIMESTAMP
+BEGIN
+ DECLARE ts TIMESTAMP;
+ DECLARE tz VARCHAR(64) DEFAULT @@time_zone;
+ SET time_zone='+00:00';
+ SET ts=FROM_UNIXTIME(uts);
+ SET time_zone=tz;
+ RETURN ts;
+END;
+$$
+DELIMITER ;$$
+SET time_zone='+00:00';
+SELECT f1(1288477526) < f1(1288481125);
+SET time_zone='Europe/Moscow';
+SELECT f1(1288477526) < f1(1288481125);
+DROP FUNCTION f1;
+
+
+# LEAST()
+CREATE TABLE t1 (a TIMESTAMP,b TIMESTAMP);
+SET time_zone='+00:00';
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288477526) /*summer time in Mowcow*/,
+ FROM_UNIXTIME(1288481125) /*winter time in Moscow*/);
+SELECT *, LEAST(a,b) FROM t1;
+SET time_zone='Europe/Moscow';
+SELECT *, LEAST(a,b) FROM t1;
+SELECT UNIX_TIMESTAMP(a), UNIX_TIMESTAMP(b), UNIX_TIMESTAMP(LEAST(a,b)) FROM t1;
+DROP TABLE t1;
+
+
+# BETWEEN
+CREATE TABLE t1 (a TIMESTAMP,b TIMESTAMP,c TIMESTAMP);
+SET time_zone='+00:00';
+INSERT INTO t1 VALUES (
+ FROM_UNIXTIME(1288477526) /*summer time in Moscow*/,
+ FROM_UNIXTIME(1288481125) /*winter time in Moscow*/,
+ FROM_UNIXTIME(1288481126) /*winter time in Moscow*/);
+SELECT b BETWEEN a AND c FROM t1;
+SET time_zone='Europe/Moscow';
+SELECT b BETWEEN a AND c FROM t1;
+DROP TABLE t1;
+
+
+# ORDER BY
+SET time_zone='+00:00';
+CREATE TABLE t1 (a TIMESTAMP);
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288477526) /*summer time in Mowcow*/);
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288481125) /*winter time in Moscow*/);
+SELECT a, UNIX_TIMESTAMP(a) FROM t1 ORDER BY a;
+SELECT COALESCE(a) AS a, UNIX_TIMESTAMP(a) FROM t1 ORDER BY a;
+SET time_zone='Europe/Moscow';
+SELECT a, UNIX_TIMESTAMP(a) FROM t1 ORDER BY a;
+SELECT COALESCE(a) AS a, UNIX_TIMESTAMP(a) FROM t1 ORDER BY a;
+DROP TABLE t1;
+
+
+# GROUP BY
+SET time_zone='+00:00';
+CREATE TABLE t1 (a TIMESTAMP);
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288477526) /*summer time in Mowcow*/);
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288481126) /*winter time in Moscow*/);
+SET time_zone='Europe/Moscow';
+SELECT a, UNIX_TIMESTAMP(a) FROM t1 GROUP BY a;
+DROP TABLE t1;
+
+
+# CASE
+SET time_zone='+00:00';
+CREATE TABLE t1 (a TIMESTAMP, b TIMESTAMP);
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288477526),FROM_UNIXTIME(1288481126));
+SELECT UNIX_TIMESTAMP(a),UNIX_TIMESTAMP(b),CASE a WHEN b THEN 'eq' ELSE 'ne' END AS x FROM t1;
+SET time_zone='Europe/Moscow';
+SELECT UNIX_TIMESTAMP(a),UNIX_TIMESTAMP(b),CASE a WHEN b THEN 'eq' ELSE 'ne' END AS x FROM t1;
+DROP TABLE t1;
+
+
+# IN
+SET time_zone='+00:00';
+CREATE TABLE t1 (a TIMESTAMP, b TIMESTAMP,c TIMESTAMP);
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288477526),FROM_UNIXTIME(1288481126),FROM_UNIXTIME(1288481127));
+SELECT UNIX_TIMESTAMP(a),UNIX_TIMESTAMP(b),a IN (b,c) AS x FROM t1;
+SET time_zone='Europe/Moscow';
+SELECT UNIX_TIMESTAMP(a),UNIX_TIMESTAMP(b),a IN (b,c) AS x FROM t1;
+DROP TABLE t1;
+
+# Comparison and IN in combination with a subquery (with one row)
+
+SET time_zone='+00:00';
+CREATE TABLE t1 (a TIMESTAMP, b TIMESTAMP);
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288477526),FROM_UNIXTIME(1288481126));
+SELECT * FROM t1 WHERE a = (SELECT MAX(b) FROM t1);
+SELECT * FROM t1 WHERE a = (SELECT MIN(b) FROM t1);
+SELECT * FROM t1 WHERE a IN ((SELECT MAX(b) FROM t1), (SELECT MIN(b) FROM t1));
+
+SET time_zone='Europe/Moscow';
+SELECT * FROM t1 WHERE a = (SELECT MAX(b) FROM t1);
+SELECT * FROM t1 WHERE a = (SELECT MIN(b) FROM t1);
+SELECT * FROM t1 WHERE a IN ((SELECT MAX(b) FROM t1), (SELECT MIN(b) FROM t1));
+DROP TABLE t1;
+
+# Comparison and IN in combinarion with a subquery (with multiple rows)
+SET time_zone='+00:00';
+CREATE TABLE t1 (a TIMESTAMP, b TIMESTAMP);
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1100000000),FROM_UNIXTIME(1200000000));
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1100000001),FROM_UNIXTIME(1200000001));
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1288477526),FROM_UNIXTIME(1288481126));
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1300000000),FROM_UNIXTIME(1400000000));
+INSERT INTO t1 VALUES (FROM_UNIXTIME(1300000001),FROM_UNIXTIME(1400000001));
+SELECT * FROM t1 WHERE a = (SELECT MAX(b) FROM t1);
+SELECT * FROM t1 WHERE a = (SELECT MIN(b) FROM t1);
+SELECT * FROM t1 WHERE a IN ((SELECT MAX(b) FROM t1), (SELECT MIN(b) FROM t1));
+
+SET time_zone='Europe/Moscow';
+SELECT * FROM t1 WHERE a = (SELECT MAX(b) FROM t1);
+SELECT * FROM t1 WHERE a = (SELECT MIN(b) FROM t1);
+SELECT * FROM t1 WHERE a IN ((SELECT MAX(b) FROM t1), (SELECT MIN(b) FROM t1));
+DROP TABLE t1;
+
+
+--echo #
+--echo # End of 10.4 tests
+--echo #
diff --git a/mysql-test/main/type_timestamp.result b/mysql-test/main/type_timestamp.result
index b0405bc..564f8d7 100644
--- a/mysql-test/main/type_timestamp.result
+++ b/mysql-test/main/type_timestamp.result
@@ -1014,3 +1014,29 @@ DROP TABLE t1;
#
# End of 10.3 tests
#
+#
+# Start of 10.4 tests
+#
+#
+# MDEV-13995 MAX(timestamp) returns a wrong result near DST change
+#
+# Testing Item_func_rollup_const::val_raw_native()
+# There is a bug in the below output (MDEV-16612)
+# Please remove this comment when MDEV-16612 is fixed and results are re-recorded
+CREATE TABLE t1 (id INT);
+INSERT INTO t1 VALUES (1),(2);
+BEGIN NOT ATOMIC
+DECLARE v TIMESTAMP DEFAULT '2001-01-01 10:20:30'; -- "v" will be wrapped into Item_func_rollup_const
+SELECT id, v AS v, COUNT(*) FROM t1 GROUP BY id,v WITH ROLLUP;
+END;
+$$
+id v COUNT(*)
+1 2001-01-01 10:20:30 1
+1 2001-01-01 10:20:30 1
+2 2001-01-01 10:20:30 1
+2 2001-01-01 10:20:30 1
+NULL 2001-01-01 10:20:30 2
+DROP TABLE t1;
+#
+# End of 10.4 tests
+#
diff --git a/mysql-test/main/type_timestamp.test b/mysql-test/main/type_timestamp.test
index 6d81a86..6cd3b77 100644
--- a/mysql-test/main/type_timestamp.test
+++ b/mysql-test/main/type_timestamp.test
@@ -602,7 +602,35 @@ SHOW CREATE TABLE t2;
DROP TABLE t2;
DROP TABLE t1;
-
--echo #
--echo # End of 10.3 tests
--echo #
+
+
+--echo #
+--echo # Start of 10.4 tests
+--echo #
+
+--echo #
+--echo # MDEV-13995 MAX(timestamp) returns a wrong result near DST change
+--echo #
+
+--echo # Testing Item_func_rollup_const::val_raw_native()
+
+--echo # There is a bug in the below output (MDEV-16612)
+--echo # Please remove this comment when MDEV-16612 is fixed and results are re-recorded
+
+CREATE TABLE t1 (id INT);
+INSERT INTO t1 VALUES (1),(2);
+DELIMITER $$;
+BEGIN NOT ATOMIC
+ DECLARE v TIMESTAMP DEFAULT '2001-01-01 10:20:30'; -- "v" will be wrapped into Item_func_rollup_const
+ SELECT id, v AS v, COUNT(*) FROM t1 GROUP BY id,v WITH ROLLUP;
+END;
+$$
+DELIMITER ;$$
+DROP TABLE t1;
+
+--echo #
+--echo # End of 10.4 tests
+--echo #
diff --git a/sql/compat56.h b/sql/compat56.h
index bb5e267..5cd150f 100644
--- a/sql/compat56.h
+++ b/sql/compat56.h
@@ -19,6 +19,15 @@
/** MySQL56 routines and macros **/
+
+/*
+ Buffer size for a raw TIMESTAMP representation, for use with StringBuffer.
+ 4 bytes for seconds
+ 3 bytes for microseconds
+ 1 byte for the trailing '\0' (as String always reserves extra 1 byte for '\0')
+*/
+#define STRING_BUFFER_TIMESTAMP_BINARY_SIZE 8 /* 4 + 3 + 1 */
+
#define MY_PACKED_TIME_GET_INT_PART(x) ((x) >> 24)
#define MY_PACKED_TIME_GET_FRAC_PART(x) ((x) % (1LL << 24))
#define MY_PACKED_TIME_MAKE(i, f) ((((longlong) (i)) << 24) + (f))
diff --git a/sql/field.cc b/sql/field.cc
index dc85482..38ebe67 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -5060,6 +5060,22 @@ my_time_t Field_timestamp::get_timestamp(const uchar *pos,
}
+bool Field_timestamp::val_raw_native(String *to)
+{
+ int32 nr= sint4korr(ptr);
+ if (!nr)
+ {
+ to->length(0); // Zero datetime '0000-00-00 00:00:00'
+ return false;
+ }
+ if (to->alloc(4))
+ return true;
+ mi_int4store((uchar *) to->ptr(), nr);
+ to->length(4);
+ return false;
+}
+
+
int Field_timestamp::store_TIME_with_warning(THD *thd, MYSQL_TIME *l_time,
const ErrConv *str,
int was_cut,
@@ -5448,6 +5464,17 @@ my_time_t Field_timestamp_hires::get_timestamp(const uchar *pos,
return mi_uint4korr(pos);
}
+
+bool Field_timestamp_hires::val_raw_native(String *to)
+{
+ ASSERT_COLUMN_MARKED_FOR_READ;
+ struct timeval tm;
+ tm.tv_sec= mi_uint4korr(ptr);
+ tm.tv_usec= sec_part_unshift(read_bigendian(ptr + 4, sec_part_bytes(dec)), dec);
+ return Timestamp_or_zero_datetime(tm.tv_sec == 0, tm).to_raw(to, dec);
+}
+
+
double Field_timestamp_with_dec::val_real(void)
{
MYSQL_TIME ltime;
diff --git a/sql/field.h b/sql/field.h
index b6f2880..cc9fa9e 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -779,6 +779,15 @@ class Field: public Value_source
virtual int store_decimal(const my_decimal *d)=0;
virtual int store_time_dec(const MYSQL_TIME *ltime, uint dec);
virtual int store_timestamp(my_time_t timestamp, ulong sec_part);
+ /**
+ Store a value represented in native raw format
+ */
+ virtual int store_raw_native(const char *str, uint length)
+ {
+ DBUG_ASSERT(0);
+ reset();
+ return 0;
+ }
int store_time(const MYSQL_TIME *ltime)
{ return store_time_dec(ltime, TIME_SECOND_PART_DIGITS); }
int store(const char *to, size_t length, CHARSET_INFO *cs,
@@ -820,6 +829,11 @@ class Field: public Value_source
This trickery is used to decrease a number of malloc calls.
*/
virtual String *val_str(String*,String *)=0;
+ virtual bool val_raw_native(String *to)
+ {
+ DBUG_ASSERT(!is_null());
+ return to->copy((const char *) ptr, pack_length(), &my_charset_bin);
+ }
String *val_int_as_str(String *val_buffer, bool unsigned_flag);
/*
Return the field value as a LEX_CSTRING, without padding to full length
@@ -2607,6 +2621,20 @@ class Field_timestamp :public Field_temporal {
{
int4store(ptr,timestamp);
}
+ void store_timeval(const struct timeval &tm)
+ {
+ store_TIME(tm.tv_sec, tm.tv_usec);
+ }
+ int store_raw_native(const char *str, uint length)
+ {
+ Timestamp_or_zero_datetime tm(str, length);
+ if (tm.is_zero_datetime())
+ reset();
+ else
+ store_timeval(tm.tm());
+ return 0;
+ }
+ bool val_raw_native(String *to);
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
uchar *pack(uchar *to, const uchar *from,
uint max_length __attribute__((unused)))
@@ -2686,6 +2714,7 @@ class Field_timestamp_hires :public Field_timestamp_with_dec {
{
DBUG_ASSERT(dec);
}
+ bool val_raw_native(String *to);
my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const;
void store_TIME(my_time_t timestamp, ulong sec_part);
int cmp(const uchar *,const uchar *);
@@ -2737,6 +2766,16 @@ class Field_timestampf :public Field_timestamp_with_dec {
{
return get_timestamp(ptr, sec_part);
}
+ bool val_raw_native(String *to)
+ {
+ // Check if it's '0000-00-00 00:00:00' rather than a real timestamp
+ if (ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0)
+ {
+ to->length(0);
+ return false;
+ }
+ return Field::val_raw_native(to);
+ }
uint size_of() const { return sizeof(*this); }
};
diff --git a/sql/filesort.cc b/sql/filesort.cc
index e62c4a9..99cdf11 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -1069,6 +1069,28 @@ Type_handler_temporal_result::make_sort_key(uchar *to, Item *item,
void
+Type_handler_timestamp_common::make_sort_key(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const
+{
+ StringBuffer<STRING_BUFFER_TIMESTAMP_BINARY_SIZE> raw;
+ uint binlen= my_timestamp_binary_length(item->decimals);
+ if (item->val_raw_native(&raw) || raw.length() == 0)
+ {
+ // NULL or '0000-00-00 00:00:00'
+ bzero(to, item->maybe_null ? binlen + 1 : binlen);
+ }
+ else
+ {
+ DBUG_ASSERT(raw.length() == binlen);
+ if (item->maybe_null)
+ *to++= 1;
+ memcpy((char *) to, raw.ptr(), binlen);
+ }
+}
+
+
+void
Type_handler::make_sort_key_longlong(uchar *to,
bool maybe_null,
bool null_value,
@@ -1876,6 +1898,15 @@ Type_handler_temporal_result::sortlength(THD *thd,
void
+Type_handler_timestamp_common::sortlength(THD *thd,
+ const Type_std_attributes *item,
+ SORT_FIELD_ATTR *sortorder) const
+{
+ sortorder->length= my_timestamp_binary_length(item->decimals);
+}
+
+
+void
Type_handler_int_result::sortlength(THD *thd,
const Type_std_attributes *item,
SORT_FIELD_ATTR *sortorder) const
diff --git a/sql/item.cc b/sql/item.cc
index 68aed25..d4b7586 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -1755,6 +1755,12 @@ String *Item_sp_variable::val_str(String *sp)
}
+bool Item_sp_variable::val_raw_native(String *raw)
+{
+ return val_raw_native_from_item(this_item(), raw);
+}
+
+
my_decimal *Item_sp_variable::val_decimal(my_decimal *decimal_value)
{
DBUG_ASSERT(fixed);
@@ -3432,6 +3438,18 @@ bool Item_field::get_date_result(MYSQL_TIME *ltime, ulonglong fuzzydate)
}
+bool Item_field::val_raw_native(String *raw)
+{
+ return val_raw_native_from_field(field, raw);
+}
+
+
+bool Item_field::val_raw_native_result(String *raw)
+{
+ return val_raw_native_from_field(result_field, raw);
+}
+
+
void Item_field::save_result(Field *to)
{
save_field_in_field(result_field, &null_value, to, TRUE);
@@ -5257,6 +5275,12 @@ String* Item_ref_null_helper::val_str(String* s)
}
+bool Item_ref_null_helper::val_raw_native(String *raw)
+{
+ return (owner->was_null|= val_raw_native_from_item(*ref, raw));
+}
+
+
bool Item_ref_null_helper::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
return (owner->was_null|= null_value= (*ref)->get_date_result(ltime, fuzzydate));
@@ -8485,6 +8509,14 @@ String *Item_ref::str_result(String* str)
}
+bool Item_ref::val_raw_native_result(String *raw)
+{
+ return result_field ?
+ val_raw_native_from_field(result_field, raw) :
+ val_raw_native(raw);
+}
+
+
my_decimal *Item_ref::val_decimal_result(my_decimal *decimal_value)
{
if (result_field)
@@ -8579,6 +8611,12 @@ bool Item_ref::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
}
+bool Item_ref::val_raw_native(String *raw)
+{
+ return val_raw_native_from_item(*ref, raw);
+}
+
+
my_decimal *Item_ref::val_decimal(my_decimal *decimal_value)
{
my_decimal *val= (*ref)->val_decimal_result(decimal_value);
@@ -8716,6 +8754,12 @@ bool Item_direct_ref::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
}
+bool Item_direct_ref::val_raw_native(String *raw)
+{
+ return val_raw_native_from_item(*ref, raw);
+}
+
+
Item_cache_wrapper::~Item_cache_wrapper()
{
DBUG_ASSERT(expr_cache == 0);
@@ -9005,6 +9049,28 @@ String *Item_cache_wrapper::val_str(String* str)
/**
+ Get the raw value of the possibly cached item
+*/
+
+bool Item_cache_wrapper::val_raw_native(String* raw)
+{
+ Item *cached_value;
+ DBUG_ENTER("Item_cache_wrapper::val_raw_native");
+ if (!expr_cache)
+ DBUG_RETURN(val_raw_native_from_item(orig_item, raw));
+
+ if ((cached_value= check_cache()))
+ DBUG_RETURN(val_raw_native_from_item(cached_value, raw));
+
+ cache();
+ if ((null_value= expr_value->null_value))
+ DBUG_RETURN(true);
+ DBUG_RETURN(expr_value->val_raw_native(raw));
+}
+
+
+
+/**
Get the decimal value of the possibly cached item
*/
@@ -10283,6 +10349,115 @@ Item *Item_cache_time::make_literal(THD *thd)
return new (thd->mem_root) Item_time_literal(thd, <ime, decimals);
}
+
+bool Item_cache_timestamp::val_raw_native(String *raw)
+{
+ if (!has_value())
+ {
+ null_value= true;
+ return true;
+ }
+ return null_value= raw->copy(m_raw.ptr(), m_raw.length(), &my_charset_bin);
+}
+
+
+String *Item_cache_timestamp::val_str(String *str)
+{
+ DBUG_ASSERT(is_fixed() == 1);
+ if (!has_value())
+ {
+ null_value= true;
+ return NULL;
+ }
+ return val_string_from_date(str);
+}
+
+
+my_decimal *Item_cache_timestamp::val_decimal(my_decimal *decimal_value)
+{
+ DBUG_ASSERT(is_fixed() == 1);
+ if (!has_value())
+ {
+ null_value= true;
+ return NULL;
+ }
+ return val_decimal_from_date(decimal_value);
+}
+
+
+longlong Item_cache_timestamp::val_int()
+{
+ DBUG_ASSERT(is_fixed() == 1);
+ if (!has_value())
+ {
+ null_value= true;
+ return 0;
+ }
+ return val_int_from_date();
+}
+
+
+longlong Item_cache_timestamp::val_datetime_packed()
+{
+ DBUG_ASSERT(0);
+ return 0;
+}
+
+
+longlong Item_cache_timestamp::val_time_packed()
+{
+ DBUG_ASSERT(0);
+ return 0;
+}
+
+
+double Item_cache_timestamp::val_real()
+{
+ DBUG_ASSERT(is_fixed() == 1);
+ if (!has_value())
+ {
+ null_value= true;
+ return 0;
+ }
+ return val_real_from_date();
+}
+
+
+bool Item_cache_timestamp::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+{
+ if (!has_value())
+ {
+ set_zero_time(ltime, MYSQL_TIMESTAMP_DATETIME);
+ return true;
+ }
+ Timestamp_or_zero_datetime tm(&m_raw);
+ return (null_value= tm.to_TIME(current_thd, ltime, fuzzydate));
+}
+
+
+bool Item_cache_timestamp::cache_value()
+{
+ if (!example)
+ return false;
+ value_cached= true;
+ null_value= example->val_raw_with_conversion_result(&m_raw, type_handler());
+ return true;
+}
+
+
+int Item_cache_timestamp::save_in_field(Field *field, bool no_conversions)
+{
+ if (!has_value())
+ return set_field_to_null_with_conversions(field, no_conversions);
+ if (!null_value && field->type_handler()->is_timestamp_type())
+ {
+ field->set_notnull();
+ return field->store_raw_native(m_raw.ptr(), m_raw.length());
+ }
+ return save_date_in_field(field, no_conversions);
+}
+
+
bool Item_cache_real::cache_value()
{
if (!example)
@@ -10301,6 +10476,7 @@ double Item_cache_real::val_real()
return value;
}
+
longlong Item_cache_real::val_int()
{
if (!has_value())
diff --git a/sql/item.h b/sql/item.h
index f6f4684..9531b9d 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -849,6 +849,25 @@ class Item: public Value_source,
res= NULL;
return res;
}
+ bool val_raw_native_from_item(Item *item, String *raw)
+ {
+ DBUG_ASSERT(is_fixed());
+ null_value= item->val_raw_native(raw);
+ DBUG_ASSERT(null_value == item->null_value);
+ return null_value;
+ }
+ bool val_raw_native_from_field(Field *field, String *raw)
+ {
+ if ((null_value= field->is_null()))
+ return true;
+ return (null_value= field->val_raw_native(raw));
+ }
+ bool val_raw_with_conversion_from_item(Item *item, String *raw,
+ const Type_handler *handler)
+ {
+ DBUG_ASSERT(is_fixed());
+ return null_value= item->val_raw_with_conversion(raw, handler);
+ }
my_decimal *val_decimal_from_item(Item *item, my_decimal *decimal_value)
{
DBUG_ASSERT(is_fixed());
@@ -1251,6 +1270,57 @@ class Item: public Value_source,
*/
virtual String *val_str(String *str)=0;
+
+ bool val_raw_with_conversion(String *raw, const Type_handler *handler)
+ {
+ return handler->Item_val_raw_with_conversion(this, raw);
+ }
+ bool val_raw_with_conversion_result(String *raw, const Type_handler *handler)
+ {
+ return handler->Item_val_raw_with_conversion_result(this, raw);
+ }
+
+ virtual bool val_raw_native(String *str)
+ {
+ /*
+ The default implementation for the Items that do not need raw format:
+ - Item_basic_value
+ - Item_ident_for_show
+ - Item_copy
+ - Item_exists_subselect
+ - Item_sum_field
+ - Item_sum_or_func (default implementation)
+ - Item_proc
+ - Item_type_holder (as val_xxx() are never called for it);
+ - TODO: Item_name_const: TIMESTAMP WITH LOCAL TIMEZONE'2001-01-01 00:00:00
+
+ These hybrid Item types override val_raw_native():
+ - Item_field
+ - Item_param
+ - Item_sp_variable
+ - Item_ref
+ - Item_cache_wrapper
+ - Item_direct_ref
+ - Item_direct_view_ref
+ - Item_ref_null_helper
+ - Item_sum_or_func
+ Note, these hybrid type Item_sum_or_func descendants
+ override the default implementation:
+ * Item_sum_hybrid
+ * Item_func_hybrid_field_type
+ * Item_func_min_max
+ * Item_func_sp
+ * Item_func_last_value
+ * Item_func_rollup_const
+ */
+ DBUG_ASSERT(0);
+ return null_value= true;
+ }
+ virtual bool val_raw_native_result(String *str)
+ {
+ return val_raw_native(str);
+ }
+
/*
Returns string representation of this item in ASCII format.
@@ -2711,6 +2781,7 @@ class Item_sp_variable :public Item_fixed_hybrid
String *val_str(String *sp);
my_decimal *val_decimal(my_decimal *decimal_value);
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool val_raw_native(String *raw);
bool is_null();
public:
@@ -3251,6 +3322,8 @@ class Item_field :public Item_ident,
void save_result(Field *to);
double val_result();
longlong val_int_result();
+ bool val_raw_native(String *str);
+ bool val_raw_native_result(String *str);
String *str_result(String* tmp);
my_decimal *val_decimal_result(my_decimal *);
bool val_bool_result();
@@ -3836,6 +3909,11 @@ class Item_param :public Item_basic_value,
{
return can_return_value() ? value.val_str(str, this) : NULL;
}
+ bool val_raw_native(String *raw)
+ {
+ return Item_param::type_handler()->Item_param_val_raw_native(this, raw);
+ }
+
bool get_date(MYSQL_TIME *tm, ulonglong fuzzydate);
int save_in_field(Field *field, bool no_conversions);
@@ -4650,6 +4728,33 @@ class Item_bin_string: public Item_hex_hybrid
};
+class Item_timestamp_literal: public Item_literal
+{
+ Timestamp_or_zero_datetime m_value;
+public:
+ Item_timestamp_literal(THD *thd)
+ :Item_literal(thd), m_value("",0)
+ { }
+ const Type_handler *type_handler() const { return &type_handler_timestamp2; }
+ double val_real() { return val_real_from_date(); }
+ longlong val_int() { return val_int_from_date(); }
+ my_decimal *val_decimal(my_decimal *to) { return val_decimal_from_date(to); }
+ String *val_str(String *to) { return val_string_from_date(to); }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ bool res= m_value.to_TIME(current_thd, ltime, fuzzydate);
+ DBUG_ASSERT(!res);
+ return res;
+ }
+ void set_value(const Timestamp_or_zero_datetime *value)
+ {
+ m_value= *value;
+ }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_timestamp_literal>(thd, this); }
+};
+
+
class Item_temporal_literal :public Item_literal
{
protected:
@@ -5084,11 +5189,13 @@ class Item_ref :public Item_ident,
my_decimal *val_decimal(my_decimal *);
bool val_bool();
String *val_str(String* tmp);
+ bool val_raw_native(String *str);
bool is_null();
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
double val_result();
longlong val_int_result();
String *str_result(String* tmp);
+ bool val_raw_native_result(String *str);
my_decimal *val_decimal_result(my_decimal *);
bool val_bool_result();
bool is_null_result();
@@ -5293,6 +5400,7 @@ class Item_direct_ref :public Item_ref
double val_real();
longlong val_int();
String *val_str(String* tmp);
+ bool val_raw_native(String *raw);
my_decimal *val_decimal(my_decimal *);
bool val_bool();
bool is_null();
@@ -5389,6 +5497,7 @@ class Item_cache_wrapper :public Item_result_field,
double val_real();
longlong val_int();
String *val_str(String* tmp);
+ bool val_raw_native(String *raw);
my_decimal *val_decimal(my_decimal *);
bool val_bool();
bool is_null();
@@ -5585,6 +5694,12 @@ class Item_direct_view_ref :public Item_direct_ref
else
return Item_direct_ref::val_str(tmp);
}
+ bool val_raw_native(String *raw)
+ {
+ if (check_null_ref())
+ return true;
+ return Item_direct_ref::val_raw_native(raw);
+ }
my_decimal *val_decimal(my_decimal *tmp)
{
if (check_null_ref())
@@ -5730,6 +5845,7 @@ class Item_ref_null_helper: public Item_ref
my_decimal *val_decimal(my_decimal *);
bool val_bool();
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool val_raw_native(String *raw);
virtual void print(String *str, enum_query_type query_type);
table_map used_tables() const;
Item *get_copy(THD *thd)
@@ -6592,6 +6708,27 @@ class Item_cache_date: public Item_cache_temporal
};
+class Item_cache_timestamp: public Item_cache
+{
+ StringBuffer<STRING_BUFFER_TIMESTAMP_BINARY_SIZE> m_raw;
+public:
+ Item_cache_timestamp(THD *thd)
+ :Item_cache(thd, &type_handler_timestamp2) { }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_cache_timestamp>(thd, this); }
+ bool cache_value();
+ String* val_str(String *str);
+ my_decimal *val_decimal(my_decimal *);
+ longlong val_int();
+ longlong val_datetime_packed();
+ longlong val_time_packed();
+ double val_real();
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ int save_in_field(Field *field, bool no_conversions);
+ bool val_raw_native(String *str);
+};
+
+
class Item_cache_real: public Item_cache
{
double value;
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 4fa3cdc..f638b9c 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -568,6 +568,18 @@ bool Arg_comparator::set_cmp_func_datetime()
}
+bool Arg_comparator::set_cmp_func_raw()
+{
+ THD *thd= current_thd;
+ m_compare_collation= &my_charset_numeric;
+ func= is_owner_equal_func() ? &Arg_comparator::compare_e_raw :
+ &Arg_comparator::compare_raw;
+ a= cache_converted_constant(thd, a, &a_cache, compare_type_handler());
+ b= cache_converted_constant(thd, b, &b_cache, compare_type_handler());
+ return false;
+}
+
+
bool Arg_comparator::set_cmp_func_int()
{
THD *thd= current_thd;
@@ -767,6 +779,33 @@ int Arg_comparator::compare_e_string()
}
+int Arg_comparator::compare_raw()
+{
+ if (!(*a)->val_raw_with_conversion(&value1, compare_type_handler()))
+ {
+ if (!(*b)->val_raw_with_conversion(&value2, compare_type_handler()))
+ {
+ if (set_null)
+ owner->null_value= 0;
+ return compare_type_handler()->cmp_raw(&value1, &value2);
+ }
+ }
+ if (set_null)
+ owner->null_value= 1;
+ return -1;
+}
+
+
+int Arg_comparator::compare_e_raw()
+{
+ bool res1= (*a)->val_raw_with_conversion(&value1, compare_type_handler());
+ bool res2= (*b)->val_raw_with_conversion(&value2, compare_type_handler());
+ if (res1 || res2)
+ return MY_TEST(res1 == res2);
+ return MY_TEST(compare_type_handler()->cmp_raw(&value1, &value2) == 0);
+}
+
+
int Arg_comparator::compare_real()
{
/*
@@ -2116,6 +2155,28 @@ longlong Item_func_between::val_int_cmp_time()
}
+longlong Item_func_between::val_int_cmp_raw()
+{
+ const Type_handler *h= m_comparator.type_handler();
+ StringBuffer<STRING_BUFFER_USUAL_SIZE> value, a, b;
+ if (val_raw_with_conversion_from_item(args[0], &value, h))
+ return 0;
+ bool ra= args[1]->val_raw_with_conversion(&a, h);
+ bool rb= args[2]->val_raw_with_conversion(&b, h);
+ if (!ra && !rb)
+ return (longlong)
+ ((h->cmp_raw(&value, &a) >= 0 &&
+ h->cmp_raw(&value, &b) <= 0) != negated);
+ if (ra && rb)
+ null_value= true;
+ else if (ra)
+ null_value= h->cmp_raw(&value, &b) <= 0;
+ else
+ null_value= h->cmp_raw(&value ,&a) >= 0;
+ return (longlong) (!null_value && negated);
+}
+
+
longlong Item_func_between::val_int_cmp_string()
{
String *value,*a,*b;
@@ -2295,6 +2356,15 @@ Item_func_ifnull::str_op(String *str)
}
+bool Item_func_ifnull::raw_op(String *raw)
+{
+ DBUG_ASSERT(fixed == 1);
+ if (!val_raw_with_conversion_from_item(args[0], raw, type_handler()))
+ return false;
+ return val_raw_with_conversion_from_item(args[1], raw, type_handler());
+}
+
+
bool Item_func_ifnull::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
DBUG_ASSERT(fixed == 1);
@@ -2813,6 +2883,16 @@ Item_func_nullif::time_op(MYSQL_TIME *ltime)
bool
+Item_func_nullif::raw_op(String *raw)
+{
+ DBUG_ASSERT(fixed == 1);
+ if (!compare())
+ return (null_value= true);
+ return val_raw_with_conversion_from_item(args[2], raw, type_handler());
+}
+
+
+bool
Item_func_nullif::is_null()
{
return (null_value= (!compare() ? 1 : args[2]->is_null()));
@@ -2986,6 +3066,16 @@ bool Item_func_case::time_op(MYSQL_TIME *ltime)
}
+bool Item_func_case::raw_op(String *raw)
+{
+ DBUG_ASSERT(fixed == 1);
+ Item *item= find_item();
+ if (!item)
+ return (null_value= true);
+ return val_raw_with_conversion_from_item(item, raw, type_handler());
+}
+
+
bool Item_func_case::fix_fields(THD *thd, Item **ref)
{
bool res= Item_func::fix_fields(thd, ref);
@@ -3343,6 +3433,18 @@ bool Item_func_coalesce::time_op(MYSQL_TIME *ltime)
}
+bool Item_func_coalesce::raw_op(String *raw)
+{
+ DBUG_ASSERT(fixed == 1);
+ for (uint i= 0; i < arg_count; i++)
+ {
+ if (!val_raw_with_conversion_from_item(args[i], raw, type_handler()))
+ return false;
+ }
+ return (null_value= true);
+}
+
+
my_decimal *Item_func_coalesce::decimal_op(my_decimal *decimal_value)
{
DBUG_ASSERT(fixed == 1);
@@ -3620,6 +3722,54 @@ Item *in_longlong::create_item(THD *thd)
}
+static int cmp_timestamp(void *cmp_arg,
+ Timestamp_or_zero_datetime *a,
+ Timestamp_or_zero_datetime *b)
+{
+ return a->cmp(*b);
+}
+
+
+in_timestamp::in_timestamp(THD *thd, uint elements)
+ :in_vector(thd, elements, sizeof(Value), (qsort2_cmp) cmp_timestamp, 0),
+ tmp("", 0)
+{}
+
+
+void in_timestamp::set(uint pos, Item *item)
+{
+ Timestamp_or_zero_datetime *buff= &((Timestamp_or_zero_datetime *) base)[pos];
+ StringBuffer<STRING_BUFFER_TIMESTAMP_BINARY_SIZE> raw;
+ if (item->val_raw_with_conversion(&raw, type_handler()))
+ buff->from_raw("", 0);
+ else
+ buff->from_raw(raw.ptr(), raw.length());
+}
+
+
+uchar *in_timestamp::get_value(Item *item)
+{
+ StringBuffer<STRING_BUFFER_TIMESTAMP_BINARY_SIZE> raw;
+ if (item->val_raw_with_conversion(&raw, type_handler()))
+ return 0;
+ tmp.from_raw(raw.ptr(), raw.length());
+ return (uchar*) &tmp;
+}
+
+
+Item *in_timestamp::create_item(THD *thd)
+{
+ return new (thd->mem_root) Item_timestamp_literal(thd);
+}
+
+
+void in_timestamp::value_to_item(uint pos, Item *item)
+{
+ const Timestamp_or_zero_datetime *buff= &(((Timestamp_or_zero_datetime*) base)[pos]);
+ static_cast<Item_timestamp_literal*>(item)->set_value(buff);
+}
+
+
void in_datetime::set(uint pos,Item *item)
{
struct packed_longlong *buff= &((packed_longlong*) base)[pos];
@@ -4028,6 +4178,48 @@ cmp_item *cmp_item_time::make_same()
}
+void cmp_item_timestamp::store_value(Item *item)
+{
+ item->val_raw_with_conversion(&m_value, &type_handler_timestamp2);
+ m_null_value= item->null_value;
+}
+
+
+int cmp_item_timestamp::cmp_not_null(const Value *val)
+{
+ /*
+ This method will be implemented when we add this syntax:
+ SELECT TIMESTAMP WITH LOCAL TIME ZONE '2001-01-01 10:20:30'
+ For now TIMESTAMP is compared to non-TIMESTAMP using DATETIME.
+ */
+ DBUG_ASSERT(0);
+ return 0;
+}
+
+
+int cmp_item_timestamp::cmp(Item *arg)
+{
+ String tmp;
+ arg->val_raw_with_conversion(&tmp, &type_handler_timestamp2);
+ return m_null_value || arg->null_value ? UNKNOWN :
+ type_handler_timestamp2.cmp_raw(&m_value, &tmp) != 0;
+}
+
+
+int cmp_item_timestamp::compare(cmp_item *arg)
+{
+ cmp_item_timestamp *tmp= static_cast<cmp_item_timestamp*>(arg);
+ return type_handler_timestamp2.cmp_raw(&m_value, &tmp->m_value);
+}
+
+
+cmp_item* cmp_item_timestamp::make_same()
+{
+ return new cmp_item_timestamp();
+}
+
+
+
bool Item_func_in::count_sargable_conds(void *arg)
{
((SELECT_LEX*) arg)->cond_count++;
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 08a462b..c6c50a6 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -89,6 +89,7 @@ class Arg_comparator: public Sql_alloc
bool set_cmp_func_string();
bool set_cmp_func_time();
bool set_cmp_func_datetime();
+ bool set_cmp_func_raw();
bool set_cmp_func_int();
bool set_cmp_func_real();
bool set_cmp_func_decimal();
@@ -121,6 +122,8 @@ class Arg_comparator: public Sql_alloc
int compare_e_datetime();
int compare_time();
int compare_e_time();
+ int compare_raw();
+ int compare_e_raw();
int compare_json_str_basic(Item *j, Item *s);
int compare_json_str();
int compare_str_json();
@@ -935,6 +938,7 @@ class Item_func_between :public Item_func_opt_neg
longlong val_int_cmp_string();
longlong val_int_cmp_datetime();
longlong val_int_cmp_time();
+ longlong val_int_cmp_raw();
longlong val_int_cmp_int();
longlong val_int_cmp_real();
longlong val_int_cmp_decimal();
@@ -1011,6 +1015,7 @@ class Item_func_coalesce :public Item_func_case_expression
my_decimal *decimal_op(my_decimal *);
bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate);
bool time_op(MYSQL_TIME *ltime);
+ bool raw_op(String *raw);
void fix_length_and_dec()
{
if (!aggregate_for_result(func_name(), args, arg_count, true))
@@ -1084,6 +1089,7 @@ class Item_func_ifnull :public Item_func_case_abbreviation2
my_decimal *decimal_op(my_decimal *);
bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate);
bool time_op(MYSQL_TIME *ltime);
+ bool raw_op(String *raw);
void fix_length_and_dec()
{
Item_func_case_abbreviation2::fix_length_and_dec2(args);
@@ -1140,6 +1146,10 @@ class Item_func_case_abbreviation2_switch: public Item_func_case_abbreviation2
{
return val_str_from_item(find_item(), str);
}
+ bool raw_op(String *raw)
+ {
+ return val_raw_with_conversion_from_item(find_item(), raw, type_handler());
+ }
};
@@ -1239,6 +1249,7 @@ class Item_func_nullif :public Item_func_case_expression
longlong int_op();
String *str_op(String *str);
my_decimal *decimal_op(my_decimal *);
+ bool raw_op(String *raw);
void fix_length_and_dec();
bool walk(Item_processor processor, bool walk_subquery, void *arg);
const char *func_name() const { return "nullif"; }
@@ -1402,6 +1413,19 @@ class in_longlong :public in_vector
};
+class in_timestamp :public in_vector
+{
+ Timestamp_or_zero_datetime tmp;
+public:
+ in_timestamp(THD *thd, uint elements);
+ void set(uint pos,Item *item);
+ uchar *get_value(Item *item);
+ Item* create_item(THD *thd);
+ void value_to_item(uint pos, Item *item);
+ const Type_handler *type_handler() const { return &type_handler_timestamp2; }
+};
+
+
/*
Class to represent a vector of constant DATE/DATETIME values.
*/
@@ -1656,6 +1680,20 @@ class cmp_item_time: public cmp_item_temporal
cmp_item *make_same();
};
+
+class cmp_item_timestamp: public cmp_item_scalar
+{
+ StringBuffer<STRING_BUFFER_TIMESTAMP_BINARY_SIZE> m_value;
+public:
+ cmp_item_timestamp() :cmp_item_scalar() { }
+ void store_value(Item *item);
+ int cmp_not_null(const Value *val);
+ int cmp(Item *arg);
+ int compare(cmp_item *ci);
+ cmp_item *make_same();
+};
+
+
class cmp_item_real : public cmp_item_scalar
{
double value;
@@ -2122,6 +2160,7 @@ class Item_func_case :public Item_func_case_expression
my_decimal *decimal_op(my_decimal *);
bool date_op(MYSQL_TIME *ltime, ulonglong fuzzydate);
bool time_op(MYSQL_TIME *ltime);
+ bool raw_op(String *raw);
bool fix_fields(THD *thd, Item **ref);
table_map not_null_tables() const { return 0; }
const char *func_name() const { return "case"; }
diff --git a/sql/item_func.cc b/sql/item_func.cc
index ca61656..3707b47 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -2885,6 +2885,35 @@ my_decimal *Item_func_min_max::val_decimal_native(my_decimal *dec)
}
+bool Item_func_min_max::val_raw_native(String *raw)
+{
+ DBUG_ASSERT(fixed == 1);
+ const Type_handler *handler= Item_hybrid_func::type_handler();
+ /*
+ For now we don't use val_raw_native() with traditional data types other
+ than TIMESTAMP. If we ever start to use, we'll possibly need new methods:
+ Type_handler::Item_func_min_max_val_raw_native()
+ Item::val_raw_native_from_{int|decimal|real|str|date}()
+ It may happen to be cheaper to do the operation in e.g. INT format and
+ then convert INT to raw (instead of doing the operation in raw format).
+ */
+ StringBuffer<STRING_BUFFER_USUAL_SIZE> cur;
+ for (uint i= 0; i < arg_count; i++)
+ {
+ if (val_raw_with_conversion_from_item(args[i], i == 0 ? raw : &cur, handler))
+ return true;
+ if (i > 0)
+ {
+ int cmp= handler->cmp_raw(raw, &cur);
+ if ((cmp_sign < 0 ? cmp : -cmp) < 0 &&
+ raw->copy(cur.ptr(), cur.length(), &my_charset_bin))
+ return null_value= true;
+ }
+ }
+ return null_value= false;
+}
+
+
longlong Item_func_bit_length::val_int()
{
DBUG_ASSERT(fixed == 1);
@@ -6651,6 +6680,14 @@ String *Item_func_last_value::val_str(String *str)
return tmp;
}
+
+bool Item_func_last_value::val_raw_native(String *raw)
+{
+ evaluate_sideeffects();
+ return val_raw_native_from_item(last_value, raw);
+}
+
+
longlong Item_func_last_value::val_int()
{
longlong tmp;
diff --git a/sql/item_func.h b/sql/item_func.h
index 3b6cb4c..ad6ba50 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -534,6 +534,15 @@ class Item_func_hybrid_field_type: public Item_hybrid_func
bool get_date_from_decimal_op(MYSQL_TIME *ltime, ulonglong fuzzydate);
bool get_date_from_int_op(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ /*
+ For now only the TIMESTAMP data type uses val_raw_native().
+ For conversion purposes (e.g. to string and numbers),
+ it uses DATETIME representation.
+ Later (e.g. for INET6) we'll implement the following methods:
+ val_{str|decimal|int|real}_from_raw_op()
+ get_date_from_raw_op()
+ */
+
public:
Item_func_hybrid_field_type(THD *thd):
Item_hybrid_func(thd)
@@ -584,6 +593,24 @@ class Item_func_hybrid_field_type: public Item_hybrid_func
Item_func_hybrid_field_type_get_date(this, res, fuzzy_date);
}
+ bool val_raw_native(String *raw)
+ {
+ DBUG_ASSERT(fixed);
+ /*
+ For now only the TIMESTAMP data type uses val_raw_native().
+ Later we'll probably need new methods:
+ Item_func_hybrid_field_type::val_raw_native_from_{str|int|real|date}()
+ Type_handler::Item_func_hybrid_field_type_val_raw_native(),
+ for symmetry with val_xxx() and get_date() above,
+ so the type handler can decide how to perform the hybrid operation
+ and when and how do data type conversion to raw format.
+ It may happen to be cheaper to do the operation in e.g. INT format and
+ then convert INT to raw (instead of doing the operation in raw format).
+ For now it's OK just to call raw_op() directly here.
+ */
+ return raw_op(raw);
+ }
+
/**
@brief Performs the operation that this functions implements when the
result type is INT.
@@ -634,6 +661,7 @@ class Item_func_hybrid_field_type: public Item_hybrid_func
*/
virtual bool time_op(MYSQL_TIME *res)= 0;
+ virtual bool raw_op(String *raw)= 0;
};
@@ -701,6 +729,11 @@ class Item_func_numhybrid: public Item_func_hybrid_field_type
DBUG_ASSERT(0);
return true;
}
+ bool raw_op(String *raw)
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
};
@@ -1558,6 +1591,7 @@ class Item_func_min_max :public Item_hybrid_func
return Item_func_min_max::type_handler()->
Item_func_min_max_get_date(this, res, fuzzy_date);
}
+ bool val_raw_native(String *raw);
void aggregate_attributes_real(Item **items, uint nitems)
{
/*
@@ -1620,6 +1654,8 @@ class Item_func_rollup_const :public Item_func
double val_real() { return val_real_from_item(args[0]); }
longlong val_int() { return val_int_from_item(args[0]); }
String *val_str(String *str) { return val_str_from_item(args[0], str); }
+ bool val_raw_native(String *raw)
+ { return val_raw_native_from_item(args[0], raw); }
my_decimal *val_decimal(my_decimal *dec)
{ return val_decimal_from_item(args[0], dec); }
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
@@ -2904,6 +2940,13 @@ class Item_func_sp :public Item_func,
return str;
}
+ bool val_raw_native(String *raw)
+ {
+ if (execute())
+ return true;
+ return null_value= sp_result_field->val_raw_native(raw);
+ }
+
void update_null_value()
{
execute();
@@ -3040,6 +3083,7 @@ class Item_func_last_value :public Item_func
String *val_str(String *);
my_decimal *val_decimal(my_decimal *);
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool val_raw_native(String *);
void fix_length_and_dec();
const char *func_name() const { return "last_value"; }
const Type_handler *type_handler() const { return last_value->type_handler(); }
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index ca3316a..19cc982 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -1339,6 +1339,24 @@ String *Item_singlerow_subselect::val_str(String *str)
}
+bool Item_singlerow_subselect::val_raw_native(String *raw)
+{
+ DBUG_ASSERT(fixed == 1);
+ if (forced_const)
+ return value->val_raw_native(raw);
+ if (!exec() && !value->null_value)
+ {
+ null_value= false;
+ return value->val_raw_native(raw);
+ }
+ else
+ {
+ reset();
+ return true;
+ }
+}
+
+
my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value)
{
DBUG_ASSERT(fixed == 1);
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index 5b27181..311184e 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -306,6 +306,7 @@ class Item_singlerow_subselect :public Item_subselect
double val_real();
longlong val_int ();
String *val_str (String *);
+ bool val_raw_native(String *);
my_decimal *val_decimal(my_decimal *);
bool val_bool();
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 69e228e..b779204 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -2378,6 +2378,15 @@ Item_sum_hybrid::val_str(String *str)
}
+bool Item_sum_hybrid::val_raw_native(String *raw)
+{
+ DBUG_ASSERT(fixed == 1);
+ if (null_value)
+ return true;
+ return val_raw_native_from_item(value, raw);
+}
+
+
void Item_sum_hybrid::cleanup()
{
DBUG_ENTER("Item_sum_hybrid::cleanup");
diff --git a/sql/item_sum.h b/sql/item_sum.h
index 89ebb04..1a93653 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -1069,6 +1069,7 @@ class Item_sum_hybrid :public Item_sum, public Type_handler_hybrid_field_type
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
void reset_field();
String *val_str(String *);
+ bool val_raw_native(String *);
const Type_handler *real_type_handler() const
{
return get_arg(0)->real_type_handler();
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index e01114f..ac5188a 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -1209,14 +1209,16 @@ bool Item_func_unix_timestamp::get_timestamp_value(my_time_t *seconds,
}
}
- MYSQL_TIME ltime;
- if (get_arg0_date(<ime, TIME_NO_ZERO_IN_DATE))
- return 1;
-
- uint error_code;
- *seconds= TIME_to_timestamp(current_thd, <ime, &error_code);
- *second_part= ltime.second_part;
- return (null_value= (error_code == ER_WARN_DATA_OUT_OF_RANGE));
+ StringBuffer<STRING_BUFFER_TIMESTAMP_BINARY_SIZE> raw;
+ if (val_raw_with_conversion_from_item(args[0], &raw,
+ &type_handler_timestamp2))
+ return true;
+ Timestamp_or_zero_datetime tm(&raw);
+ if (tm.is_zero_datetime())
+ return null_value= true;
+ *seconds= tm.tm().tv_sec;
+ *second_part= tm.tm().tv_usec;
+ return false;
}
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index 3607e6d..5810b94 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -130,6 +130,83 @@ bool Type_handler_data::init()
Type_handler_data *type_handler_data= NULL;
+uint Timestamp::binary_length_to_precision(uint length)
+{
+ switch (length) {
+ case 4: return 0;
+ case 5: return 2;
+ case 6: return 4;
+ case 7: return 6;
+ }
+ DBUG_ASSERT(0);
+ return 0;
+}
+
+
+void Timestamp::from_raw(const char *str, uint length)
+{
+ DBUG_ASSERT(length >= 4 && length <= 7);
+ uint dec= binary_length_to_precision(length);
+ my_timestamp_from_binary(this, (const uchar *) str, dec);
+}
+
+
+bool Timestamp::to_raw(String *raw, uint decimals) const
+{
+ uint len= my_timestamp_binary_length(decimals);
+ if (raw->reserve(len))
+ return true;
+ my_timestamp_to_binary(this, (uchar *) raw->ptr(), decimals);
+ raw->length(len);
+ return false;
+}
+
+
+bool Timestamp::to_TIME(THD *thd, MYSQL_TIME *to, ulonglong fuzzydate) const
+{
+ return thd->timestamp_to_TIME(to, tv_sec, tv_usec, fuzzydate);
+}
+
+
+Timestamp_or_zero_datetime::Timestamp_or_zero_datetime(THD *thd,
+ const MYSQL_TIME *ltime,
+ uint *error_code)
+{
+ tv_sec= TIME_to_timestamp(thd, ltime, error_code);
+ tv_usec= ltime->second_part;
+ if ((m_is_zero_datetime= (*error_code == ER_WARN_DATA_OUT_OF_RANGE)))
+ {
+ if (!non_zero_date(ltime))
+ *error_code= 0; // ltime was '0000-00-00 00:00:00'
+ }
+ else if (*error_code == ER_WARN_INVALID_TIMESTAMP)
+ *error_code= 0; // ltime fell into spring time gap, adjusted.
+}
+
+
+bool Timestamp_or_zero_datetime::to_TIME(THD *thd, MYSQL_TIME *to,
+ ulonglong fuzzydate) const
+{
+ if (m_is_zero_datetime)
+ {
+ set_zero_time(to, MYSQL_TIMESTAMP_DATETIME);
+ return false;
+ }
+ return Timestamp::to_TIME(thd, to, fuzzydate);
+}
+
+
+bool Timestamp_or_zero_datetime::to_raw(String *raw, uint decimals) const
+{
+ if (m_is_zero_datetime)
+ {
+ raw->length(0);
+ return false;
+ }
+ return Timestamp::to_raw(raw, decimals);
+}
+
+
void Time::make_from_item(Item *item, const Options opt)
{
if (item->get_date(this, opt.get_date_flags()))
@@ -537,7 +614,7 @@ const Type_handler *Type_handler_datetime_common::type_handler_for_comparison()
const Type_handler *Type_handler_timestamp_common::type_handler_for_comparison() const
{
- return &type_handler_datetime;
+ return &type_handler_timestamp;
}
@@ -722,6 +799,15 @@ Type_handler_hybrid_field_type::aggregate_for_comparison(const Type_handler *h)
*/
if (b == TIME_RESULT)
m_type_handler= h; // Temporal types bit non-temporal types
+ /*
+ Compare TIMESTAMP to a non-temporal type as DATETIME.
+ This is needed to make queries with fuzzy dates work:
+ SELECT * FROM t1
+ WHERE
+ ts BETWEEN '0000-00-00' AND '2010-00-01 00:00:00';
+ */
+ if (m_type_handler->is_timestamp_type())
+ m_type_handler= &type_handler_datetime;
}
else
{
@@ -805,7 +891,16 @@ Type_handler_hybrid_field_type::aggregate_for_min_max(const Type_handler *h)
}
else if (a == TIME_RESULT || b == TIME_RESULT)
{
- if ((a == TIME_RESULT) + (b == TIME_RESULT) == 1)
+ if (m_type_handler->is_timestamp_type() + h->is_timestamp_type() == 1)
+ {
+ /*
+ Handle LEAST(TIMESTAMP, non-TIMESTAMP) as DATETIME,
+ to make sure fuzzy dates work in this context:
+ LEAST('2001-00-00', timestamp_field)
+ */
+ m_type_handler= &type_handler_datetime2;
+ }
+ else if ((a == TIME_RESULT) + (b == TIME_RESULT) == 1)
{
/*
We're here if there's only one temporal data type:
@@ -2539,6 +2634,22 @@ int Type_handler_temporal_with_date::Item_save_in_field(Item *item,
}
+int Type_handler_timestamp_common::Item_save_in_field(Item *item,
+ Field *field,
+ bool no_conversions)
+ const
+{
+ StringBuffer<STRING_BUFFER_TIMESTAMP_BINARY_SIZE> raw;
+ if (Item_val_raw_with_conversion(item, &raw))
+ return set_field_to_null_with_conversions(field, no_conversions);
+ field->set_notnull();
+ Timestamp_or_zero_datetime tm(&raw);
+ return tm.is_zero_datetime() ?
+ field->store_timestamp(0, 0) :
+ field->store_timestamp(tm.tm().tv_sec, tm.tm().tv_usec);
+}
+
+
int Type_handler_string_result::Item_save_in_field(Item *item, Field *field,
bool no_conversions) const
{
@@ -2605,6 +2716,12 @@ Type_handler_temporal_with_date::set_comparator_func(Arg_comparator *cmp) const
return cmp->set_cmp_func_datetime();
}
+bool
+Type_handler_timestamp_common::set_comparator_func(Arg_comparator *cmp) const
+{
+ return cmp->set_cmp_func_raw();
+}
+
/*************************************************************************/
@@ -2740,7 +2857,7 @@ Type_handler_string_result::Item_get_cache(THD *thd, const Item *item) const
Item_cache *
Type_handler_timestamp_common::Item_get_cache(THD *thd, const Item *item) const
{
- return new (thd->mem_root) Item_cache_datetime(thd);
+ return new (thd->mem_root) Item_cache_timestamp(thd);
}
Item_cache *
@@ -3751,6 +3868,12 @@ longlong Type_handler_time_common::
return func->val_int_cmp_time();
}
+longlong Type_handler_timestamp_common::
+ Item_func_between_val_int(Item_func_between *func) const
+{
+ return func->val_int_cmp_raw();
+}
+
longlong Type_handler_int_result::
Item_func_between_val_int(Item_func_between *func) const
{
@@ -3814,6 +3937,12 @@ cmp_item *Type_handler_temporal_with_date::make_cmp_item(THD *thd,
return new (thd->mem_root) cmp_item_datetime;
}
+cmp_item *Type_handler_timestamp_common::make_cmp_item(THD *thd,
+ CHARSET_INFO *cs) const
+{
+ return new (thd->mem_root) cmp_item_timestamp;
+}
+
/***************************************************************************/
static int srtcmp_in(CHARSET_INFO *cs, const String *x,const String *y)
@@ -3874,6 +4003,15 @@ Type_handler_temporal_with_date::make_in_vector(THD *thd,
}
+in_vector *
+Type_handler_timestamp_common::make_in_vector(THD *thd,
+ const Item_func_in *func,
+ uint nargs) const
+{
+ return new (thd->mem_root) in_timestamp(thd, nargs);
+}
+
+
in_vector *Type_handler_row::make_in_vector(THD *thd,
const Item_func_in *func,
uint nargs) const
@@ -3975,6 +4113,17 @@ String *Type_handler_temporal_result::
}
+String *Type_handler_timestamp_common::
+ Item_func_min_max_val_str(Item_func_min_max *func, String *str) const
+{
+ MYSQL_TIME ltime;
+ if (get_date_from_val_raw_native(func, <ime) ||
+ my_TIME_to_str(<ime, str, func->decimals))
+ return NULL;
+ return str;
+}
+
+
String *Type_handler_int_result::
Item_func_min_max_val_str(Item_func_min_max *func, String *str) const
{
@@ -4013,6 +4162,16 @@ double Type_handler_temporal_result::
}
+double Type_handler_timestamp_common::
+ Item_func_min_max_val_real(Item_func_min_max *func) const
+{
+ MYSQL_TIME ltime;
+ if (get_date_from_val_raw_native(func, <ime))
+ return 0;
+ return TIME_to_double(<ime);
+}
+
+
double Type_handler_numeric::
Item_func_min_max_val_real(Item_func_min_max *func) const
{
@@ -4037,6 +4196,16 @@ longlong Type_handler_temporal_result::
}
+longlong Type_handler_timestamp_common::
+ Item_func_min_max_val_int(Item_func_min_max *func) const
+{
+ MYSQL_TIME ltime;
+ if (get_date_from_val_raw_native(func, <ime))
+ return 0;
+ return TIME_to_ulonglong(<ime);
+}
+
+
longlong Type_handler_numeric::
Item_func_min_max_val_int(Item_func_min_max *func) const
{
@@ -4071,6 +4240,17 @@ my_decimal *Type_handler_temporal_result::
}
+my_decimal *Type_handler_timestamp_common::
+ Item_func_min_max_val_decimal(Item_func_min_max *func,
+ my_decimal *dec) const
+{
+ MYSQL_TIME ltime;
+ if (get_date_from_val_raw_native(func, <ime))
+ return 0;
+ return date2my_decimal(<ime, dec);
+}
+
+
bool Type_handler_string_result::
Item_func_min_max_get_date(Item_func_min_max *func,
MYSQL_TIME *ltime, ulonglong fuzzydate) const
@@ -4107,6 +4287,13 @@ bool Type_handler_time_common::
return func->get_time_native(ltime);
}
+bool Type_handler_timestamp_common::
+ Item_func_min_max_get_date(Item_func_min_max *func,
+ MYSQL_TIME *ltime, ulonglong fuzzydate) const
+{
+ return get_date_from_val_raw_native(func, ltime);
+}
+
/***************************************************************************/
/**
@@ -5375,6 +5562,20 @@ bool Type_handler::
}
+bool Type_handler::Item_send_timestamp(Item *item,
+ Protocol *protocol,
+ st_value *buf) const
+{
+ StringBuffer<STRING_BUFFER_TIMESTAMP_BINARY_SIZE> raw;
+ MYSQL_TIME ltime;
+ if (item->val_raw_native(&raw))
+ return protocol->store_null();
+ return Timestamp_or_zero_datetime(&raw).to_TIME(current_thd, <ime, 0) ?
+ protocol->store_null() :
+ protocol->store(<ime, item->decimals);
+}
+
+
bool Type_handler::
Item_send_datetime(Item *item, Protocol *protocol, st_value *buf) const
{
@@ -6507,6 +6708,17 @@ bool Type_handler_temporal_with_date::Item_eq_value(THD *thd,
}
+bool Type_handler_timestamp_common::Item_eq_value(THD *thd,
+ const Type_cmp_attributes *attr,
+ Item *a, Item *b) const
+{
+ StringBuffer<STRING_BUFFER_TIMESTAMP_BINARY_SIZE> raw0, raw1;
+ a->val_raw_with_conversion(&raw0, this);
+ b->val_raw_with_conversion(&raw1, this);
+ return !a->null_value && !b->null_value && !cmp_raw(&raw0, &raw1);
+}
+
+
bool Type_handler_string_result::Item_eq_value(THD *thd,
const Type_cmp_attributes *attr,
Item *a, Item *b) const
@@ -6531,4 +6743,102 @@ bool Type_handler_decimal_result::Item_eq_value(THD *thd,
}
+
/***************************************************************************/
+
+bool Type_handler_timestamp_common::TIME_to_raw(THD *thd,
+ const MYSQL_TIME *ltime,
+ String *raw,
+ uint decimals) const
+{
+ uint error_code;
+ Timestamp_or_zero_datetime tm(thd, ltime, &error_code);
+ if (error_code)
+ return true;
+ tm.trunc(decimals);
+ return tm.to_raw(raw, decimals);
+}
+
+
+bool
+Type_handler_timestamp_common::Item_val_raw_with_conversion(Item *item,
+ String *raw)
+ const
+{
+ MYSQL_TIME ltime;
+ if (item->type_handler()->is_timestamp_type())
+ return item->val_raw_native(raw);
+ return
+ item->get_date(<ime, TIME_NO_ZERO_IN_DATE) ||
+ TIME_to_raw(current_thd, <ime, raw, item->datetime_precision());
+}
+
+
+bool
+Type_handler_timestamp_common::Item_val_raw_with_conversion_result(Item *item,
+ String *raw)
+ const
+{
+ MYSQL_TIME ltime;
+ if (item->type_handler()->is_timestamp_type())
+ return item->val_raw_native_result(raw);
+ return
+ item->get_date_result(<ime, TIME_NO_ZERO_IN_DATE) ||
+ TIME_to_raw(current_thd, <ime, raw, item->datetime_precision());
+}
+
+
+int Type_handler_timestamp_common::cmp_raw(const String *a,
+ const String *b) const
+{
+ /*
+ Optimize a simple case:
+ Either both timeatamp values have the same fractional precision,
+ or both values are zero datetime '0000-00-00 00:00:00.000000',
+ */
+ if (a->length() == b->length())
+ return memcmp(a->ptr(), b->ptr(), a->length());
+ return Timestamp_or_zero_datetime(a).cmp(Timestamp_or_zero_datetime(b));
+}
+
+
+bool
+Type_handler_timestamp_common::get_date_from_val_raw_native(Item *item,
+ MYSQL_TIME *ltime)
+ const
+{
+ StringBuffer<STRING_BUFFER_TIMESTAMP_BINARY_SIZE> raw;
+ if (item->val_raw_native(&raw))
+ {
+ DBUG_ASSERT(item->null_value);
+ return true;
+ }
+ return item->null_value= Timestamp_or_zero_datetime(&raw).
+ to_TIME(current_thd, ltime, 0);
+}
+
+
+bool
+Type_handler::Item_param_val_raw_native(Item_param *item, String *raw) const
+{
+ DBUG_ASSERT(0); // TODO-TYPE: MDEV-14271
+ return item->null_value= true;
+}
+
+
+bool
+Type_handler_timestamp_common::Item_param_val_raw_native(Item_param *item,
+ String *raw) const
+{
+ /*
+ The below code may not run well in corner cases.
+ This will be fixed under terms of MDEV-14271.
+ Item_param should:
+ - either remember @@time_zone at bind time
+ - or store TIMESTAMP in my_time_t format, rather than in MYSQL_TIME format.
+ */
+ MYSQL_TIME ltime;
+ return
+ item->get_date(<ime, TIME_NO_ZERO_IN_DATE) ||
+ TIME_to_raw(current_thd, <ime, raw, item->datetime_precision());
+}
diff --git a/sql/sql_type.h b/sql/sql_type.h
index f0ec0ba..07da914 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -77,6 +77,86 @@ struct SORT_FIELD_ATTR;
class Vers_history_point;
+class Timestamp: protected timeval
+{
+ uint binary_length_to_precision(uint length);
+public:
+ int cmp(const Timestamp &other) const
+ {
+ return tv_sec < other.tv_sec ? -1 :
+ tv_sec > other.tv_sec ? +1 :
+ tv_usec < other.tv_usec ? -1 :
+ tv_usec > other.tv_usec ? +1 : 0;
+ }
+ const struct timeval &tm() const { return *this; }
+ void set(const timeval &other) { timeval::operator=(other); }
+ void trunc(uint decimals) { my_timeval_trunc(this, decimals); }
+ bool to_TIME(THD *thd, MYSQL_TIME *ltime, ulonglong fuzzydate) const;
+ bool to_raw(String *to, uint decimals) const;
+ void from_raw(const char *str, uint length);
+};
+
+
+/*
+ A helper class to store MariaDB TIMESTAMP values, which can be:
+ - real TIMESTAMP (seconds and microseconds since epoch), or
+ - zero datetime '0000-00-00 00:00:00.000000'
+*/
+class Timestamp_or_zero_datetime: public Timestamp
+{
+ bool m_is_zero_datetime;
+public:
+ Timestamp_or_zero_datetime(const char *rawstr, uint length)
+ {
+ from_raw(rawstr, length);
+ }
+ Timestamp_or_zero_datetime(const String *raw)
+ {
+ from_raw(raw->ptr(), raw->length());
+ }
+ Timestamp_or_zero_datetime(bool is_zero_datetime, const struct timeval tm)
+ {
+ m_is_zero_datetime= is_zero_datetime;
+ Timestamp::set(tm);
+ }
+ Timestamp_or_zero_datetime(THD *thd, const MYSQL_TIME *ltime, uint *err_code);
+ bool is_zero_datetime() const { return m_is_zero_datetime; }
+ const struct timeval &tm() const
+ {
+ DBUG_ASSERT(!is_zero_datetime());
+ return Timestamp::tm();
+ }
+ void trunc(uint decimals)
+ {
+ if (!is_zero_datetime())
+ Timestamp::trunc(decimals);
+ }
+ int cmp(const Timestamp_or_zero_datetime &other) const
+ {
+ if (is_zero_datetime())
+ return other.is_zero_datetime() ? 0 : -1;
+ if (other.is_zero_datetime())
+ return 1;
+ return Timestamp::cmp(other);
+ }
+ bool to_TIME(THD *thd, MYSQL_TIME *to, ulonglong fuzzydate) const;
+ /*
+ Convert to raw format:
+ - Real timestamps are encoded in the same way how Field_timestamp2 stores
+ values (big endian seconds followed by big endian microseconds)
+ - Zero datetime '0000-00-00 00:00:00.000000' is encoded as empty string.
+ Two raw values are binary comparable.
+ */
+ bool to_raw(String *to, uint decimals) const;
+ void from_raw(const char *str, uint length)
+ {
+ if (!(m_is_zero_datetime= (length == 0)))
+ Timestamp::from_raw(str, length);
+ }
+};
+
+
+
/**
Class Time is designed to store valid TIME values.
@@ -1052,6 +1132,7 @@ class Type_handler
bool Item_send_double(Item *item, Protocol *protocol, st_value *buf) const;
bool Item_send_time(Item *item, Protocol *protocol, st_value *buf) const;
bool Item_send_date(Item *item, Protocol *protocol, st_value *buf) const;
+ bool Item_send_timestamp(Item *item, Protocol *protocol, st_value *buf) const;
bool Item_send_datetime(Item *item, Protocol *protocol, st_value *buf) const;
bool Column_definition_prepare_stage2_legacy(Column_definition *c,
enum_field_types type)
@@ -1326,6 +1407,7 @@ class Type_handler
Item_param *param,
const Type_all_attributes *attr,
const st_value *value) const= 0;
+ virtual bool Item_param_val_raw_native(Item_param *item, String *raw) const;
virtual bool Item_send(Item *item, Protocol *p, st_value *buf) const= 0;
virtual int Item_save_in_field(Item *item, Field *field,
bool no_conversions) const= 0;
@@ -1402,6 +1484,11 @@ class Type_handler
DBUG_ASSERT(0);
return NULL;
}
+ virtual int cmp_raw(const String *a, const String *b) const
+ {
+ DBUG_ASSERT(0);
+ return 0;
+ }
virtual bool set_comparator_func(Arg_comparator *cmp) const= 0;
virtual bool Item_const_eq(const Item_const *a, const Item_const *b,
bool binary_cmp) const
@@ -1426,6 +1513,16 @@ class Type_handler
virtual
bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const= 0;
+ virtual bool Item_val_raw_with_conversion(Item *item, String *raw) const
+ {
+ return true;
+ }
+ virtual bool Item_val_raw_with_conversion_result(Item *item,
+ String *raw) const
+ {
+ return true;
+ }
+
virtual bool Item_val_bool(Item *item) const= 0;
virtual bool Item_get_date(Item *item, MYSQL_TIME *ltime,
ulonglong fuzzydate) const= 0;
@@ -3165,6 +3262,9 @@ class Type_handler_datetime2: public Type_handler_datetime_common
class Type_handler_timestamp_common: public Type_handler_temporal_with_date
{
static const Name m_name_timestamp;
+protected:
+ bool TIME_to_raw(THD *, const MYSQL_TIME *ltime, String *raw, uint dec) const;
+ bool get_date_from_val_raw_native(Item *item, MYSQL_TIME *ltime) const;
public:
virtual ~Type_handler_timestamp_common() {}
const Name name() const { return m_name_timestamp; }
@@ -3178,6 +3278,20 @@ class Type_handler_timestamp_common: public Type_handler_temporal_with_date
{
return true;
}
+ bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
+ Item *a, Item *b) const;
+ bool Item_val_raw_with_conversion(Item *item, String *raw) const;
+ bool Item_val_raw_with_conversion_result(Item *item, String *raw) const;
+ bool Item_param_val_raw_native(Item_param *item, String *raw) const;
+ int cmp_raw(const String *a, const String *b) const;
+ longlong Item_func_between_val_int(Item_func_between *func) const;
+ cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const;
+ in_vector *make_in_vector(THD *thd, const Item_func_in *f, uint nargs) const;
+ void make_sort_key(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const;
+ void sortlength(THD *thd,
+ const Type_std_attributes *item,
+ SORT_FIELD_ATTR *attr) const;
bool Column_definition_fix_attributes(Column_definition *c) const;
uint Item_decimal_scale(const Item *item) const
{
@@ -3190,10 +3304,12 @@ class Type_handler_timestamp_common: public Type_handler_temporal_with_date
}
bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
{
- return Item_send_datetime(item, protocol, buf);
+ return Item_send_timestamp(item, protocol, buf);
}
+ int Item_save_in_field(Item *item, Field *field, bool no_conversions) const;
String *print_item_value(THD *thd, Item *item, String *str) const;
Item_cache *Item_get_cache(THD *thd, const Item *item) const;
+ bool set_comparator_func(Arg_comparator *cmp) const;
bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name,
Type_handler_hybrid_field_type *,
@@ -3201,6 +3317,13 @@ class Type_handler_timestamp_common: public Type_handler_temporal_with_date
Item **items, uint nitems) const;
void Item_param_set_param_func(Item_param *param,
uchar **pos, ulong len) const;
+ String *Item_func_min_max_val_str(Item_func_min_max *, String *) const;
+ double Item_func_min_max_val_real(Item_func_min_max *) const;
+ longlong Item_func_min_max_val_int(Item_func_min_max *) const;
+ my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
+ my_decimal *) const;
+ bool Item_func_min_max_get_date(Item_func_min_max*,
+ MYSQL_TIME *, ulonglong fuzzydate) const;
};