Commit 34eb9838 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-13995 MAX(timestamp) returns a wrong result near DST change

parent 5b3db871
......@@ -180,3 +180,44 @@ a unix_timestamp(a)
2010-10-31 02:25:26 1288481126
drop table t1, t2;
set time_zone=DEFAULT;
#
# MDEV-13995 MAX(timestamp) returns a wrong result near DST change
#
SET global mysql56_temporal_format=false;
SET time_zone='+00:00';
CREATE TABLE t1 (a TIMESTAMP(0));
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, COALESCE(a), UNIX_TIMESTAMP(a) FROM t1;
a COALESCE(a) UNIX_TIMESTAMP(a)
2010-10-31 02:25:26 2010-10-31 02:25:26 1288477526
2010-10-31 02:25:25 2010-10-31 02:25:25 1288481125
SELECT MIN(a), UNIX_TIMESTAMP(MIN(a)) AS a FROM t1;
MIN(a) a
2010-10-31 02:25:26 1288477526
SELECT MAX(a), UNIX_TIMESTAMP(MAX(a)) AS a FROM t1;
MAX(a) a
2010-10-31 02:25:25 1288481125
SELECT t1.a, UNIX_TIMESTAMP(t1.a), t2.a, UNIX_TIMESTAMP(t2.a) FROM t1 t1, t1 t2 WHERE t1.a=t2.a;
a UNIX_TIMESTAMP(t1.a) a UNIX_TIMESTAMP(t2.a)
2010-10-31 02:25:26 1288477526 2010-10-31 02:25:26 1288477526
2010-10-31 02:25:25 1288481125 2010-10-31 02:25:25 1288481125
ALTER TABLE t1 MODIFY a TIMESTAMP(1);
SELECT a, COALESCE(a), UNIX_TIMESTAMP(a) FROM t1;
a COALESCE(a) UNIX_TIMESTAMP(a)
2010-10-31 02:25:26.0 2010-10-31 02:25:26.0 1288477526.0
2010-10-31 02:25:25.0 2010-10-31 02:25:25.0 1288481125.0
SELECT MIN(a), UNIX_TIMESTAMP(MIN(a)) AS a FROM t1;
MIN(a) a
2010-10-31 02:25:26.0 1288477526.0
SELECT MAX(a), UNIX_TIMESTAMP(MAX(a)) AS a FROM t1;
MAX(a) a
2010-10-31 02:25:25.0 1288481125.0
SELECT t1.a, UNIX_TIMESTAMP(t1.a), t2.a, UNIX_TIMESTAMP(t2.a) FROM t1 t1, t1 t2 WHERE t1.a=t2.a;
a UNIX_TIMESTAMP(t1.a) a UNIX_TIMESTAMP(t2.a)
2010-10-31 02:25:26.0 1288477526.0 2010-10-31 02:25:26.0 1288477526.0
2010-10-31 02:25:25.0 1288481125.0 2010-10-31 02:25:25.0 1288481125.0
DROP TABLE t1;
SET time_zone=DEFAULT;
SET global mysql56_temporal_format=true;
......@@ -119,3 +119,32 @@ insert t2 select a from t1;
select a, unix_timestamp(a) from t2;
drop table t1, t2;
set time_zone=DEFAULT;
--echo #
--echo # MDEV-13995 MAX(timestamp) returns a wrong result near DST change
--echo #
# This tests:
# Field_timestamp::val_native()
# Field_timestamp_hires::val_native()
# Type_handler_timestamp_common::type_handler_for_native_format()
SET global mysql56_temporal_format=false;
SET time_zone='+00:00';
CREATE TABLE t1 (a TIMESTAMP(0));
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, COALESCE(a), UNIX_TIMESTAMP(a) FROM t1;
SELECT MIN(a), UNIX_TIMESTAMP(MIN(a)) AS a FROM t1;
SELECT MAX(a), UNIX_TIMESTAMP(MAX(a)) AS a FROM t1;
SELECT t1.a, UNIX_TIMESTAMP(t1.a), t2.a, UNIX_TIMESTAMP(t2.a) FROM t1 t1, t1 t2 WHERE t1.a=t2.a;
ALTER TABLE t1 MODIFY a TIMESTAMP(1);
SELECT a, COALESCE(a), UNIX_TIMESTAMP(a) FROM t1;
SELECT MIN(a), UNIX_TIMESTAMP(MIN(a)) AS a FROM t1;
SELECT MAX(a), UNIX_TIMESTAMP(MAX(a)) AS a FROM t1;
SELECT t1.a, UNIX_TIMESTAMP(t1.a), t2.a, UNIX_TIMESTAMP(t2.a) FROM t1 t1, t1 t2 WHERE t1.a=t2.a;
DROP TABLE t1;
SET time_zone=DEFAULT;
SET global mysql56_temporal_format=true;
......@@ -353,5 +353,194 @@ Warning 1292 Truncated incorrect datetime value: '00:00:00'
SET old_mode=DEFAULT;
SET timestamp=DEFAULT;
#
# 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
#
......@@ -324,6 +324,173 @@ SELECT CONVERT_TZ(TIME('2010-01-01 00:00:00'),'+00:00','+7:5');
SET old_mode=DEFAULT;
SET timestamp=DEFAULT;
--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 #
......@@ -1068,5 +1068,55 @@ DROP PROCEDURE p1;
SET timestamp=DEFAULT;
SET time_zone=DEFAULT;
#
# MDEV-13995 MAX(timestamp) returns a wrong result near DST change
#
# Testing Item_func_rollup_const::val_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;
#
# Testing Type_handler_timestamp_common::Item_save_in_field()
# "txt" is expected to have three fractional digits
SET time_zone='+00:00';
SET timestamp=UNIX_TIMESTAMP('2001-01-01 10:20:30.123456');
CREATE TABLE t1 (ts1 TIMESTAMP(1) NOT NULL, ts2 TIMESTAMP(3) NOT NULL, txt TEXT);
INSERT INTO t1 VALUES ('0000-00-00 00:00:00', '0000-00-00 00:00:00',COALESCE(ts1,ts2));
INSERT INTO t1 VALUES (NOW(),NOW(),COALESCE(ts1,ts2));
INSERT INTO t1 VALUES (NOW(1),NOW(3),COALESCE(ts1,ts2));
SELECT * FROM t1;
ts1 ts2 txt
0000-00-00 00:00:00.0 0000-00-00 00:00:00.000 0000-00-00 00:00:00.000
2001-01-01 10:20:30.0 2001-01-01 10:20:30.000 2001-01-01 10:20:30.000
2001-01-01 10:20:30.1 2001-01-01 10:20:30.123 2001-01-01 10:20:30.100
DROP TABLE t1;
SET timestamp=DEFAULT;
SET time_zone=DEFAULT;
#
# Testing Field_timestamp::store_native
#
SET sql_mode='';
CREATE TABLE t1 (a TIMESTAMP, b TIMESTAMP);
INSERT INTO t1 VALUES ('0000-00-00 00:00:00','0000-00-00 00:00:00');
SET sql_mode='STRICT_ALL_TABLES,NO_ZERO_DATE';
UPDATE t1 SET a=b;
ERROR 22007: Incorrect datetime value: '0000-00-00 00:00:00' for column 'a' at row 1
UPDATE t1 SET a=COALESCE(b);
ERROR 22007: Incorrect datetime value: '0000-00-00 00:00:00' for column 'a' at row 1
DROP TABLE t1;
SET sql_mode=DEFAULT;
#
# End of 10.4 tests
#
......@@ -660,6 +660,56 @@ DROP PROCEDURE p1;
SET timestamp=DEFAULT;
SET time_zone=DEFAULT;
--echo #
--echo # MDEV-13995 MAX(timestamp) returns a wrong result near DST change
--echo #
--echo # Testing Item_func_rollup_const::val_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 # Testing Type_handler_timestamp_common::Item_save_in_field()
--echo # "txt" is expected to have three fractional digits
SET time_zone='+00:00';
SET timestamp=UNIX_TIMESTAMP('2001-01-01 10:20:30.123456');
CREATE TABLE t1 (ts1 TIMESTAMP(1) NOT NULL, ts2 TIMESTAMP(3) NOT NULL, txt TEXT);
INSERT INTO t1 VALUES ('0000-00-00 00:00:00', '0000-00-00 00:00:00',COALESCE(ts1,ts2));
INSERT INTO t1 VALUES (NOW(),NOW(),COALESCE(ts1,ts2));
INSERT INTO t1 VALUES (NOW(1),NOW(3),COALESCE(ts1,ts2));
SELECT * FROM t1;
DROP TABLE t1;
SET timestamp=DEFAULT;
SET time_zone=DEFAULT;
--echo #
--echo # Testing Field_timestamp::store_native
--echo #
SET sql_mode='';
CREATE TABLE t1 (a TIMESTAMP, b TIMESTAMP);
INSERT INTO t1 VALUES ('0000-00-00 00:00:00','0000-00-00 00:00:00');
SET sql_mode='STRICT_ALL_TABLES,NO_ZERO_DATE';
--error ER_TRUNCATED_WRONG_VALUE
UPDATE t1 SET a=b;
--error ER_TRUNCATED_WRONG_VALUE
UPDATE t1 SET a=COALESCE(b);
DROP TABLE t1;
SET sql_mode=DEFAULT;
--echo #
--echo # End of 10.4 tests
--echo #
......@@ -162,3 +162,30 @@ SELECT * FROM t1,t2 WHERE COALESCE(t1.a)=t2.a;
a a
20010101235959.9999999 2001-01-02 00:00:00
DROP TABLE t1,t2;
#
# MDEV-13995 MAX(timestamp) returns a wrong result near DST change
#
# Test Field_timestamp::store_native()
#
SET sql_mode=@default_sql_mode;
SET time_zone='+00:00';
CREATE TABLE t1 (ts0 TIMESTAMP, ts1 TIMESTAMP(1));
INSERT INTO t1 VALUES ('2001-01-01 10:20:30', '2001-01-01 10:20:30.9');
SELECT * FROM t1;
ts0 ts1
2001-01-01 10:20:30 2001-01-01 10:20:30.9
# This should round
UPDATE t1 SET ts0=COALESCE(ts1);
SELECT * FROM t1;
ts0 ts1
2001-01-01 10:20:31 2001-01-01 10:20:30.9
# Corner case
UPDATE t1 SET ts1=FROM_UNIXTIME(2147483647.9);
UPDATE t1 SET ts0=COALESCE(ts1);
Warnings:
Warning 1264 Out of range value for column 'ts0' at row 1
SELECT * FROM t1;
ts0 ts1
2038-01-19 03:14:07 2038-01-19 03:14:07.9
DROP TABLE t1;
SET time_zone=DEFAULT;
......@@ -136,3 +136,25 @@ INSERT INTO t2 VALUES ('2001-01-02 00:00:00');
SELECT * FROM t1,t2 WHERE t1.a=t2.a;
SELECT * FROM t1,t2 WHERE COALESCE(t1.a)=t2.a;
DROP TABLE t1,t2;
--echo #
--echo # MDEV-13995 MAX(timestamp) returns a wrong result near DST change
--echo #
--echo # Test Field_timestamp::store_native()
--echo #
SET sql_mode=@default_sql_mode;
SET time_zone='+00:00';
CREATE TABLE t1 (ts0 TIMESTAMP, ts1 TIMESTAMP(1));
INSERT INTO t1 VALUES ('2001-01-01 10:20:30', '2001-01-01 10:20:30.9');
SELECT * FROM t1;
--echo # This should round
UPDATE t1 SET ts0=COALESCE(ts1);
SELECT * FROM t1;
--echo # Corner case
UPDATE t1 SET ts1=FROM_UNIXTIME(2147483647.9);
UPDATE t1 SET ts0=COALESCE(ts1);
SELECT * FROM t1;
DROP TABLE t1;
SET time_zone=DEFAULT;
......@@ -19,6 +19,15 @@
/** MySQL56 routines and macros **/
/*
Buffer size for a native TIMESTAMP representation, for use with NativBuffer.
4 bytes for seconds
3 bytes for microseconds
1 byte for the trailing '\0' (class Native 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))
......
......@@ -5023,6 +5023,15 @@ my_time_t Field_timestamp::get_timestamp(const uchar *pos,
}
bool Field_timestamp::val_native(Native *to)
{
ASSERT_COLUMN_MARKED_FOR_READ;
my_time_t sec= (my_time_t) sint4korr(ptr);
return Timestamp_or_zero_datetime(Timestamp(sec, 0), sec == 0).
to_native(to, 0);
}
int Field_timestamp::store_TIME_with_warning(THD *thd, const Datetime *dt,
const ErrConv *str, int was_cut)
{
......@@ -5143,6 +5152,14 @@ int Field_timestamp::store_timestamp_dec(const timeval &ts, uint dec)
}
if (ts.tv_sec == 0 && ts.tv_usec == 0 &&
get_thd()->variables.sql_mode & (ulonglong) TIME_NO_ZERO_DATE)
return zero_time_stored_return_code_with_warning();
return 0;
}
int Field_timestamp::zero_time_stored_return_code_with_warning()
{
if (get_thd()->variables.sql_mode & (ulonglong) TIME_NO_ZERO_DATE)
{
ErrConvString s(
STRING_WITH_LEN("0000-00-00 00:00:00.000000") - (decimals() ? 6 - decimals() : 7),
......@@ -5151,6 +5168,23 @@ int Field_timestamp::store_timestamp_dec(const timeval &ts, uint dec)
return 1;
}
return 0;
}
int Field_timestamp::store_native(const Native &value)
{
if (!value.length()) // Zero datetime
{
reset();
return zero_time_stored_return_code_with_warning();
}
/*
The exact second precision is not important here.
Field_timestamp*::store_timestamp_dec() do not use the "dec" parameter.
Passing TIME_SECOND_PART_DIGITS is OK.
*/
return store_timestamp_dec(Timestamp(value).tv(), TIME_SECOND_PART_DIGITS);
}
......@@ -5410,6 +5444,18 @@ my_time_t Field_timestamp_hires::get_timestamp(const uchar *pos,
return mi_uint4korr(pos);
}
bool Field_timestamp_hires::val_native(Native *to)
{
ASSERT_COLUMN_MARKED_FOR_READ;
struct timeval tm;
tm.tv_sec= mi_uint4korr(ptr);
tm.tv_usec= (ulong) sec_part_unshift(read_bigendian(ptr+4, sec_part_bytes(dec)), dec);
return Timestamp_or_zero_datetime(Timestamp(tm), tm.tv_sec == 0).
to_native(to, dec);
}
double Field_timestamp_with_dec::val_real(void)
{
MYSQL_TIME ltime;
......@@ -5516,6 +5562,19 @@ my_time_t Field_timestampf::get_timestamp(const uchar *pos,
}
bool Field_timestampf::val_native(Native *to)
{
ASSERT_COLUMN_MARKED_FOR_READ;
// 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_native(to);
}
/*************************************************************/
uint Field_temporal::is_equal(Create_field *new_field)
{
......
......@@ -790,6 +790,15 @@ class Field: public Value_source
return store_timestamp_dec(Timeval(timestamp, sec_part),
TIME_SECOND_PART_DIGITS);
}
/**
Store a value represented in native format
*/
virtual int store_native(const Native &value)
{
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,
......@@ -836,6 +845,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_native(Native *to)
{
DBUG_ASSERT(!is_null());
return to->copy((const char *) ptr, pack_length());
}
String *val_int_as_str(String *val_buffer, bool unsigned_flag);
/*
Return the field value as a LEX_CSTRING, without padding to full length
......@@ -2735,6 +2749,7 @@ class Field_timestamp :public Field_temporal {
{
store_TIMEVAL(ts.tv());
}
int zero_time_stored_return_code_with_warning();
public:
Field_timestamp(uchar *ptr_arg, uint32 len_arg,
uchar *null_ptr_arg, uchar null_bit_arg,
......@@ -2785,6 +2800,8 @@ class Field_timestamp :public Field_temporal {
store_TIMESTAMP(Timestamp(ts, sec_part).round(decimals(), mode, &warn));
}
bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate);
int store_native(const Native &value);
bool val_native(Native *to);
uchar *pack(uchar *to, const uchar *from,
uint max_length __attribute__((unused)))
{
......@@ -2864,6 +2881,7 @@ class Field_timestamp_hires :public Field_timestamp_with_dec {
{
DBUG_ASSERT(dec);
}
bool val_native(Native *to);
my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const;
int cmp(const uchar *,const uchar *);
uint32 pack_length() const { return 4 + sec_part_bytes(dec); }
......@@ -2914,6 +2932,7 @@ class Field_timestampf :public Field_timestamp_with_dec {
{
return get_timestamp(ptr, sec_part);
}
bool val_native(Native *to);
uint size_of() const { return sizeof(*this); }
};
......
......@@ -1067,6 +1067,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
{
uint binlen= my_timestamp_binary_length(item->decimals);
Timestamp_or_zero_datetime_native_null native(current_thd, item);
if (native.is_null() || native.is_zero_datetime())
{
// NULL or '0000-00-00 00:00:00'
bzero(to, item->maybe_null ? binlen + 1 : binlen);
}
else
{
DBUG_ASSERT(native.length() == binlen);
if (item->maybe_null)
*to++= 1;
memcpy((char *) to, native.ptr(), binlen);
}
}
void
Type_handler::make_sort_key_longlong(uchar *to,
bool maybe_null,
......@@ -1873,6 +1895,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,
......
......@@ -1526,6 +1526,12 @@ String *Item_sp_variable::val_str(String *sp)
}
bool Item_sp_variable::val_native(THD *thd, Native *to)
{
return val_native_from_item(thd, this_item(), to);
}
my_decimal *Item_sp_variable::val_decimal(my_decimal *decimal_value)
{
DBUG_ASSERT(fixed);
......@@ -3178,6 +3184,18 @@ bool Item_field::get_date_result(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzyd
}
bool Item_field::val_native(THD *thd, Native *to)
{
return val_native_from_field(field, to);
}
bool Item_field::val_native_result(THD *thd, Native *to)
{
return val_native_from_field(result_field, to);
}
void Item_field::save_result(Field *to)
{
save_field_in_field(result_field, &null_value, to, TRUE);
......@@ -4843,6 +4861,12 @@ String* Item_ref_null_helper::val_str(String* s)
}
bool Item_ref_null_helper::val_native(THD *thd, Native *to)
{
return (owner->was_null|= val_native_from_item(thd, *ref, to));
}
bool Item_ref_null_helper::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
return (owner->was_null|= null_value= (*ref)->get_date_result(thd, ltime, fuzzydate));
......@@ -8103,6 +8127,14 @@ String *Item_ref::str_result(String* str)
}
bool Item_ref::val_native_result(THD *thd, Native *to)
{
return result_field ?
val_native_from_field(result_field, to) :
val_native(thd, to);
}
my_decimal *Item_ref::val_decimal_result(my_decimal *decimal_value)
{
if (result_field)
......@@ -8197,6 +8229,12 @@ bool Item_ref::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
}
bool Item_ref::val_native(THD *thd, Native *to)
{
return val_native_from_item(thd, *ref, to);
}
my_decimal *Item_ref::val_decimal(my_decimal *decimal_value)
{
my_decimal *val= (*ref)->val_decimal_result(decimal_value);
......@@ -8334,6 +8372,12 @@ bool Item_direct_ref::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydat
}
bool Item_direct_ref::val_native(THD *thd, Native *to)
{
return val_native_from_item(thd, *ref, to);
}
Item_cache_wrapper::~Item_cache_wrapper()
{
DBUG_ASSERT(expr_cache == 0);
......@@ -8622,6 +8666,28 @@ String *Item_cache_wrapper::val_str(String* str)
}
/**
Get the native value of the possibly cached item
*/
bool Item_cache_wrapper::val_native(THD *thd, Native* to)
{
Item *cached_value;
DBUG_ENTER("Item_cache_wrapper::val_native");
if (!expr_cache)
DBUG_RETURN(val_native_from_item(thd, orig_item, to));
if ((cached_value= check_cache()))
DBUG_RETURN(val_native_from_item(thd, cached_value, to));
cache();
if ((null_value= expr_value->null_value))
DBUG_RETURN(true);
DBUG_RETURN(expr_value->val_native(thd, to));
}
/**
Get the decimal value of the possibly cached item
*/
......@@ -9794,6 +9860,62 @@ Item *Item_cache_time::make_literal(THD *thd)
return new (thd->mem_root) Item_time_literal(thd, &ltime, decimals);
}
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);
return m_native.save_in_field(field, decimals);
}
bool Item_cache_timestamp::val_native(THD *thd, Native *to)
{
if (!has_value())
{
null_value= true;
return true;
}
return null_value= to->copy(m_native);
}
Datetime Item_cache_timestamp::to_datetime(THD *thd)
{
DBUG_ASSERT(is_fixed() == 1);
if (!has_value())
{
null_value= true;
return Datetime();
}
return Datetime(thd, Timestamp_or_zero_datetime(m_native).tv());
}
bool Item_cache_timestamp::get_date(THD *thd, MYSQL_TIME *ltime,
date_mode_t fuzzydate)
{
if (!has_value())
{
set_zero_time(ltime, MYSQL_TIMESTAMP_DATETIME);
return true;
}
Timestamp_or_zero_datetime tm(m_native);
return (null_value= tm.to_TIME(thd, ltime, fuzzydate));
}
bool Item_cache_timestamp::cache_value()
{
if (!example)
return false;
value_cached= true;
null_value= example->val_native_with_conversion_result(current_thd, &m_native,
type_handler());
return true;
}
bool Item_cache_real::cache_value()
{
if (!example)
......
......@@ -855,6 +855,25 @@ class Item: public Value_source,
res= NULL;
return res;
}
bool val_native_from_item(THD *thd, Item *item, Native *to)
{
DBUG_ASSERT(is_fixed());
null_value= item->val_native(thd, to);
DBUG_ASSERT(null_value == item->null_value);
return null_value;
}
bool val_native_from_field(Field *field, Native *to)
{
if ((null_value= field->is_null()))
return true;
return (null_value= field->val_native(to));
}
bool val_native_with_conversion_from_item(THD *thd, Item *item, Native *to,
const Type_handler *handler)
{
DBUG_ASSERT(is_fixed());
return null_value= item->val_native_with_conversion(thd, to, handler);
}
my_decimal *val_decimal_from_item(Item *item, my_decimal *decimal_value)
{
DBUG_ASSERT(is_fixed());
......@@ -1276,6 +1295,60 @@ class Item: public Value_source,
*/
virtual String *val_str(String *str)=0;
bool val_native_with_conversion(THD *thd, Native *to, const Type_handler *th)
{
return th->Item_val_native_with_conversion(thd, this, to);
}
bool val_native_with_conversion_result(THD *thd, Native *to,
const Type_handler *th)
{
return th->Item_val_native_with_conversion_result(thd, this, to);
}
virtual bool val_native(THD *thd, Native *to)
{
/*
The default implementation for the Items that do not need native 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 will need val_native() in the future,
when we add this syntax:
TIMESTAMP WITH LOCAL TIMEZONE'2001-01-01 00:00:00'
These hybrid Item types override val_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_native_result(THD *thd, Native *to)
{
return val_native(thd, to);
}
/*
Returns string representation of this item in ASCII format.
......@@ -2689,6 +2762,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(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
bool val_native(THD *thd, Native *to);
bool is_null();
public:
......@@ -3229,6 +3303,8 @@ class Item_field :public Item_ident,
void save_result(Field *to);
double val_result();
longlong val_int_result();
bool val_native(THD *thd, Native *to);
bool val_native_result(THD *thd, Native *to);
String *str_result(String* tmp);
my_decimal *val_decimal_result(my_decimal *);
bool val_bool_result();
......@@ -3815,6 +3891,11 @@ class Item_param :public Item_basic_value,
return can_return_value() ? value.val_str(str, this) : NULL;
}
bool get_date(THD *thd, MYSQL_TIME *tm, date_mode_t fuzzydate);
bool val_native(THD *thd, Native *to)
{
return Item_param::type_handler()->Item_param_val_native(thd, this, to);
}
int save_in_field(Field *field, bool no_conversions);
void set_default();
......@@ -4614,6 +4695,54 @@ 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)
{ }
const Type_handler *type_handler() const { return &type_handler_timestamp2; }
int save_in_field(Field *field, bool no_conversions)
{
Timestamp_or_zero_datetime_native native(m_value, decimals);
return native.save_in_field(field, decimals);
}
longlong val_int()
{
return m_value.to_datetime(current_thd).to_longlong();
}
double val_real()
{
return m_value.to_datetime(current_thd).to_double();
}
String *val_str(String *to)
{
return m_value.to_datetime(current_thd).to_string(to, decimals);
}
my_decimal *val_decimal(my_decimal *to)
{
return m_value.to_datetime(current_thd).to_decimal(to);
}
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
bool res= m_value.to_TIME(thd, ltime, fuzzydate);
DBUG_ASSERT(!res);
return res;
}
bool val_native(THD *thd, Native *to)
{
return m_value.to_native(to, decimals);
}
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:
......@@ -5062,11 +5191,13 @@ class Item_ref :public Item_ident,
my_decimal *val_decimal(my_decimal *);
bool val_bool();
String *val_str(String* tmp);
bool val_native(THD *thd, Native *to);
bool is_null();
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
double val_result();
longlong val_int_result();
String *str_result(String* tmp);
bool val_native_result(THD *thd, Native *to);
my_decimal *val_decimal_result(my_decimal *);
bool val_bool_result();
bool is_null_result();
......@@ -5271,6 +5402,7 @@ class Item_direct_ref :public Item_ref
double val_real();
longlong val_int();
String *val_str(String* tmp);
bool val_native(THD *thd, Native *to);
my_decimal *val_decimal(my_decimal *);
bool val_bool();
bool is_null();
......@@ -5367,6 +5499,7 @@ class Item_cache_wrapper :public Item_result_field,
double val_real();
longlong val_int();
String *val_str(String* tmp);
bool val_native(THD *thd, Native *to);
my_decimal *val_decimal(my_decimal *);
bool val_bool();
bool is_null();
......@@ -5563,6 +5696,12 @@ class Item_direct_view_ref :public Item_direct_ref
else
return Item_direct_ref::val_str(tmp);
}
bool val_native(THD *thd, Native *to)
{
if (check_null_ref())
return true;
return Item_direct_ref::val_native(thd, to);
}
my_decimal *val_decimal(my_decimal *tmp)
{
if (check_null_ref())
......@@ -5708,6 +5847,7 @@ class Item_ref_null_helper: public Item_ref
my_decimal *val_decimal(my_decimal *);
bool val_bool();
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
bool val_native(THD *thd, Native *to);
virtual void print(String *str, enum_query_type query_type);
table_map used_tables() const;
Item *get_copy(THD *thd)
......@@ -6531,6 +6671,48 @@ class Item_cache_date: public Item_cache_temporal
};
class Item_cache_timestamp: public Item_cache
{
Timestamp_or_zero_datetime_native m_native;
Datetime to_datetime(THD *thd);
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 *to)
{
return to_datetime(current_thd).to_string(to, decimals);
}
my_decimal *val_decimal(my_decimal *to)
{
return to_datetime(current_thd).to_decimal(to);
}
longlong val_int()
{
return to_datetime(current_thd).to_longlong();
}
double val_real()
{
return to_datetime(current_thd).to_double();
}
longlong val_datetime_packed(THD *thd)
{
DBUG_ASSERT(0);
return 0;
}
longlong val_time_packed(THD *thd)
{
DBUG_ASSERT(0);
return 0;
}
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
int save_in_field(Field *field, bool no_conversions);
bool val_native(THD *thd, Native *to);
};
class Item_cache_real: public Item_cache
{
double value;
......
......@@ -567,6 +567,18 @@ bool Arg_comparator::set_cmp_func_datetime()
}
bool Arg_comparator::set_cmp_func_native()
{
THD *thd= current_thd;
m_compare_collation= &my_charset_numeric;
func= is_owner_equal_func() ? &Arg_comparator::compare_e_native :
&Arg_comparator::compare_native;
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;
......@@ -770,6 +782,39 @@ int Arg_comparator::compare_e_string()
}
int Arg_comparator::compare_native()
{
THD *thd= current_thd;
if (!(*a)->val_native_with_conversion(thd, &m_native1,
compare_type_handler()))
{
if (!(*b)->val_native_with_conversion(thd, &m_native2,
compare_type_handler()))
{
if (set_null)
owner->null_value= 0;
return compare_type_handler()->cmp_native(m_native1, m_native2);
}
}
if (set_null)
owner->null_value= 1;
return -1;
}
int Arg_comparator::compare_e_native()
{
THD *thd= current_thd;
bool res1= (*a)->val_native_with_conversion(thd, &m_native1,
compare_type_handler());
bool res2= (*b)->val_native_with_conversion(thd, &m_native2,
compare_type_handler());
if (res1 || res2)
return MY_TEST(res1 == res2);
return MY_TEST(compare_type_handler()->cmp_native(m_native1, m_native2) == 0);
}
int Arg_comparator::compare_real()
{
/*
......@@ -2121,6 +2166,29 @@ longlong Item_func_between::val_int_cmp_time()
}
longlong Item_func_between::val_int_cmp_native()
{
THD *thd= current_thd;
const Type_handler *h= m_comparator.type_handler();
NativeBuffer<STRING_BUFFER_USUAL_SIZE> value, a, b;
if (val_native_with_conversion_from_item(thd, args[0], &value, h))
return 0;
bool ra= args[1]->val_native_with_conversion(thd, &a, h);
bool rb= args[2]->val_native_with_conversion(thd, &b, h);
if (!ra && !rb)
return (longlong)
((h->cmp_native(value, a) >= 0 &&
h->cmp_native(value, b) <= 0) != negated);
if (ra && rb)
null_value= true;
else if (ra)
null_value= h->cmp_native(value, b) <= 0;
else
null_value= h->cmp_native(value, a) >= 0;
return (longlong) (!null_value && negated);
}
longlong Item_func_between::val_int_cmp_string()
{
String *value,*a,*b;
......@@ -2306,6 +2374,15 @@ Item_func_ifnull::str_op(String *str)
}
bool Item_func_ifnull::native_op(THD *thd, Native *to)
{
DBUG_ASSERT(fixed == 1);
if (!val_native_with_conversion_from_item(thd, args[0], to, type_handler()))
return false;
return val_native_with_conversion_from_item(thd, args[1], to, type_handler());
}
bool Item_func_ifnull::date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
DBUG_ASSERT(fixed == 1);
......@@ -2828,6 +2905,16 @@ Item_func_nullif::time_op(THD *thd, MYSQL_TIME *ltime)
}
bool
Item_func_nullif::native_op(THD *thd, Native *to)
{
DBUG_ASSERT(fixed == 1);
if (!compare())
return (null_value= true);
return val_native_with_conversion_from_item(thd, args[2], to, type_handler());
}
bool
Item_func_nullif::is_null()
{
......@@ -3002,6 +3089,16 @@ bool Item_func_case::time_op(THD *thd, MYSQL_TIME *ltime)
}
bool Item_func_case::native_op(THD *thd, Native *to)
{
DBUG_ASSERT(fixed == 1);
Item *item= find_item();
if (!item)
return (null_value= true);
return val_native_with_conversion_from_item(thd, item, to, type_handler());
}
bool Item_func_case::fix_fields(THD *thd, Item **ref)
{
bool res= Item_func::fix_fields(thd, ref);
......@@ -3360,6 +3457,18 @@ bool Item_func_coalesce::time_op(THD *thd, MYSQL_TIME *ltime)
}
bool Item_func_coalesce::native_op(THD *thd, Native *to)
{
DBUG_ASSERT(fixed == 1);
for (uint i= 0; i < arg_count; i++)
{
if (!val_native_with_conversion_from_item(thd, args[i], to, type_handler()))
return false;
}
return (null_value= true);
}
my_decimal *Item_func_coalesce::decimal_op(my_decimal *decimal_value)
{
DBUG_ASSERT(fixed == 1);
......@@ -3637,6 +3746,53 @@ 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)
{}
void in_timestamp::set(uint pos, Item *item)
{
Timestamp_or_zero_datetime *buff= &((Timestamp_or_zero_datetime *) base)[pos];
Timestamp_or_zero_datetime_native_null native(current_thd, item, true);
if (native.is_null())
*buff= Timestamp_or_zero_datetime();
else
*buff= Timestamp_or_zero_datetime(native);
}
uchar *in_timestamp::get_value(Item *item)
{
Timestamp_or_zero_datetime_native_null native(current_thd, item, true);
if (native.is_null())
return 0;
tmp= Timestamp_or_zero_datetime(native);
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];
......@@ -4044,6 +4200,49 @@ cmp_item *cmp_item_time::make_same()
}
void cmp_item_timestamp::store_value(Item *item)
{
item->val_native_with_conversion(current_thd, &m_native,
&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)
{
THD *thd= current_thd;
Timestamp_or_zero_datetime_native_null tmp(thd, arg, true);
return m_null_value || tmp.is_null() ? UNKNOWN :
type_handler_timestamp2.cmp_native(m_native, 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_native(m_native, tmp->m_native);
}
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++;
......
......@@ -68,6 +68,7 @@ class Arg_comparator: public Sql_alloc
if (val1 == val2) return 0;
return 1;
}
NativeBuffer<STRING_BUFFER_USUAL_SIZE> m_native1, m_native2;
public:
/* Allow owner function to use string buffers. */
String value1, value2;
......@@ -89,6 +90,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_native();
bool set_cmp_func_int();
bool set_cmp_func_real();
bool set_cmp_func_decimal();
......@@ -121,6 +123,8 @@ class Arg_comparator: public Sql_alloc
int compare_e_datetime();
int compare_time();
int compare_e_time();
int compare_native();
int compare_e_native();
int compare_json_str_basic(Item *j, Item *s);
int compare_json_str();
int compare_str_json();
......@@ -935,6 +939,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_native();
longlong val_int_cmp_int();
longlong val_int_cmp_real();
longlong val_int_cmp_decimal();
......@@ -1013,6 +1018,7 @@ class Item_func_coalesce :public Item_func_case_expression
my_decimal *decimal_op(my_decimal *);
bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
bool time_op(THD *thd, MYSQL_TIME *ltime);
bool native_op(THD *thd, Native *to);
bool fix_length_and_dec()
{
if (aggregate_for_result(func_name(), args, arg_count, true))
......@@ -1092,6 +1098,7 @@ class Item_func_ifnull :public Item_func_case_abbreviation2
my_decimal *decimal_op(my_decimal *);
bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
bool time_op(THD *thd, MYSQL_TIME *ltime);
bool native_op(THD *thd, Native *to);
bool fix_length_and_dec()
{
if (Item_func_case_abbreviation2::fix_length_and_dec2(args))
......@@ -1150,6 +1157,11 @@ class Item_func_case_abbreviation2_switch: public Item_func_case_abbreviation2
{
return val_str_from_item(find_item(), str);
}
bool native_op(THD *thd, Native *to)
{
return val_native_with_conversion_from_item(thd, find_item(), to,
type_handler());
}
};
......@@ -1249,6 +1261,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 native_op(THD *thd, Native *to);
bool fix_length_and_dec();
bool walk(Item_processor processor, bool walk_subquery, void *arg);
const char *func_name() const { return "nullif"; }
......@@ -1412,6 +1425,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.
*/
......@@ -1666,6 +1692,20 @@ class cmp_item_time: public cmp_item_temporal
cmp_item *make_same();
};
class cmp_item_timestamp: public cmp_item_scalar
{
Timestamp_or_zero_datetime_native m_native;
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;
......@@ -2132,6 +2172,7 @@ class Item_func_case :public Item_func_case_expression
my_decimal *decimal_op(my_decimal *);
bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
bool time_op(THD *thd, MYSQL_TIME *ltime);
bool native_op(THD *thd, Native *to);
bool fix_fields(THD *thd, Item **ref);
table_map not_null_tables() const { return 0; }
const char *func_name() const { return "case"; }
......
......@@ -2730,6 +2730,28 @@ my_decimal *Item_func_min_max::val_decimal_native(my_decimal *dec)
}
bool Item_func_min_max::val_native(THD *thd, Native *native)
{
DBUG_ASSERT(fixed == 1);
const Type_handler *handler= Item_hybrid_func::type_handler();
NativeBuffer<STRING_BUFFER_USUAL_SIZE> cur;
for (uint i= 0; i < arg_count; i++)
{
if (val_native_with_conversion_from_item(thd, args[i],
i == 0 ? native : &cur,
handler))
return true;
if (i > 0)
{
int cmp= handler->cmp_native(*native, cur);
if ((cmp_sign < 0 ? cmp : -cmp) < 0 && native->copy(cur))
return null_value= true;
}
}
return null_value= false;
}
longlong Item_func_bit_length::val_int()
{
DBUG_ASSERT(fixed == 1);
......@@ -6453,6 +6475,14 @@ String *Item_func_last_value::val_str(String *str)
return tmp;
}
bool Item_func_last_value::val_native(THD *thd, Native *to)
{
evaluate_sideeffects();
return val_native_from_item(thd, last_value, to);
}
longlong Item_func_last_value::val_int()
{
longlong tmp;
......
......@@ -768,6 +768,12 @@ class Item_func_hybrid_field_type: public Item_hybrid_func
Item_func_hybrid_field_type_get_date_with_warn(thd, this, to, mode);
}
bool val_native(THD *thd, Native *to)
{
DBUG_ASSERT(fixed);
return native_op(thd, to);
}
/**
@brief Performs the operation that this functions implements when the
result type is INT.
......@@ -838,6 +844,7 @@ class Item_func_hybrid_field_type: public Item_hybrid_func
*/
virtual bool time_op(THD *thd, MYSQL_TIME *res)= 0;
virtual bool native_op(THD *thd, Native *native)= 0;
};
......@@ -905,6 +912,11 @@ class Item_func_numhybrid: public Item_func_hybrid_field_type
DBUG_ASSERT(0);
return true;
}
bool native_op(THD *thd, Native *to)
{
DBUG_ASSERT(0);
return true;
}
};
......@@ -1771,6 +1783,7 @@ class Item_func_min_max :public Item_hybrid_func
return Item_func_min_max::type_handler()->
Item_func_min_max_get_date(thd, this, res, fuzzydate);
}
bool val_native(THD *thd, Native *to);
void aggregate_attributes_real(Item **items, uint nitems)
{
/*
......@@ -1834,6 +1847,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_native(THD *thd, Native *to)
{ return val_native_from_item(thd, args[0], to); }
my_decimal *val_decimal(my_decimal *dec)
{ return val_decimal_from_item(args[0], dec); }
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
......@@ -3153,6 +3168,13 @@ class Item_func_sp :public Item_func,
return str;
}
bool val_native(THD *thd, Native *to)
{
if (execute())
return true;
return null_value= sp_result_field->val_native(to);
}
void update_null_value()
{
execute();
......@@ -3282,6 +3304,7 @@ class Item_func_last_value :public Item_func
String *val_str(String *);
my_decimal *val_decimal(my_decimal *);
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
bool val_native(THD *thd, Native *);
bool fix_length_and_dec();
const char *func_name() const { return "last_value"; }
const Type_handler *type_handler() const { return last_value->type_handler(); }
......
......@@ -1352,6 +1352,24 @@ String *Item_singlerow_subselect::val_str(String *str)
}
bool Item_singlerow_subselect::val_native(THD *thd, Native *to)
{
DBUG_ASSERT(fixed == 1);
if (forced_const)
return value->val_native(thd, to);
if (!exec() && !value->null_value)
{
null_value= false;
return value->val_native(thd, to);
}
else
{
reset();
return true;
}
}
my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value)
{
DBUG_ASSERT(fixed == 1);
......
......@@ -306,6 +306,7 @@ class Item_singlerow_subselect :public Item_subselect
double val_real();
longlong val_int ();
String *val_str (String *);
bool val_native(THD *thd, Native *);
my_decimal *val_decimal(my_decimal *);
bool val_bool();
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
......
......@@ -2381,6 +2381,15 @@ Item_sum_hybrid::val_str(String *str)
}
bool Item_sum_hybrid::val_native(THD *thd, Native *to)
{
DBUG_ASSERT(fixed == 1);
if (null_value)
return true;
return val_native_from_item(thd, value, to);
}
void Item_sum_hybrid::cleanup()
{
DBUG_ENTER("Item_sum_hybrid::cleanup");
......
......@@ -1072,6 +1072,7 @@ class Item_sum_hybrid :public Item_sum, public Type_handler_hybrid_field_type
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
void reset_field();
String *val_str(String *);
bool val_native(THD *thd, Native *);
const Type_handler *real_type_handler() const
{
return get_arg(0)->real_type_handler();
......
......@@ -1217,15 +1217,13 @@ bool Item_func_unix_timestamp::get_timestamp_value(my_time_t *seconds,
}
}
THD *thd= current_thd;
Datetime dt(thd, args[0], Datetime::Options(TIME_NO_ZERO_IN_DATE, thd));
if ((null_value= !dt.is_valid_datetime()))
Timestamp_or_zero_datetime_native_null native(current_thd, args[0], true);
if ((null_value= native.is_null() || native.is_zero_datetime()))
return true;
uint error_code;
*seconds= TIME_to_timestamp(thd, dt.get_mysql_time(), &error_code);
*second_part= dt.get_mysql_time()->second_part;
return (null_value= (error_code == ER_WARN_DATA_OUT_OF_RANGE));
Timestamp_or_zero_datetime tm(native);
*seconds= tm.tv().tv_sec;
*second_part= tm.tv().tv_usec;
return false;
}
......
This diff is collapsed.
This diff is collapsed.
......@@ -847,6 +847,8 @@ class Load_data_outvar
class Timeval: public timeval
{
protected:
Timeval() { }
public:
Timeval(my_time_t sec, ulong usec)
{
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment