Skip to content

Commit dfdedd4

Browse files
montywivuvova
authored andcommitted
MDEV-32188 make TIMESTAMP use whole 32-bit unsigned range
This patch extends the timestamp from 2038-01-19 03:14:07.999999 to 2106-02-07 06:28:15.999999 for 64 bit hardware and OS where 'long' is 64 bits. This is true for 64 bit Linux but not for Windows. This is done by treating the 32 bit stored int as unsigned instead of signed. This is safe as MariaDB has never accepted dates before the epoch (1970). The benefit of this approach that for normal timestamp the storage is compatible with earlier version. However for tables using system versioning we before stored a timestamp with the year 2038 as the 'max timestamp', which is used to detect current values. This patch stores the new 2106 year max value as the max timestamp. This means that old tables using system versioning needs to be updated with mariadb-upgrade when moving them to 11.4. That will be done in a separate commit.
1 parent ce6cce8 commit dfdedd4

File tree

90 files changed

+1227
-354
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+1227
-354
lines changed

extra/mariabackup/xtrabackup.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2404,7 +2404,7 @@ static bool innodb_init_param()
24042404

24052405
/* Check that values don't overflow on 32-bit systems. */
24062406
if (sizeof(ulint) == 4) {
2407-
if (xtrabackup_use_memory > (longlong) UINT_MAX32) {
2407+
if (xtrabackup_use_memory > (longlong) UINT_MAX32) {
24082408
msg("mariabackup: use-memory can't be over 4GB"
24092409
" on 32-bit systems");
24102410
}

include/my_global.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,7 @@ inline unsigned long long my_double2ulonglong(double d)
782782
#define INT_MAX64 0x7FFFFFFFFFFFFFFFLL
783783
#define INT_MIN32 (~0x7FFFFFFFL)
784784
#define INT_MAX32 0x7FFFFFFFL
785-
#define UINT_MAX32 0xFFFFFFFFL
785+
#define UINT_MAX32 0xFFFFFFFFUL
786786
#define INT_MIN24 (~0x007FFFFF)
787787
#define INT_MAX24 0x007FFFFF
788788
#define UINT_MAX24 0x00FFFFFF

include/my_time.h

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,26 @@ C_MODE_START
3030
extern MYSQL_PLUGIN_IMPORT ulonglong log_10_int[20];
3131
extern uchar days_in_month[];
3232

33+
#if SIZEOF_LONG == 4
3334
#define MY_TIME_T_MAX LONG_MAX
3435
#define MY_TIME_T_MIN LONG_MIN
35-
36-
/* Time handling defaults */
3736
#define TIMESTAMP_MAX_YEAR 2038
38-
#define TIMESTAMP_MIN_YEAR (1900 + YY_PART_YEAR - 1)
37+
#define TIMESTAMP_MAX_MONTH 1
38+
#define TIMESTAMP_MAX_DAY 19
3939
#define TIMESTAMP_MAX_VALUE INT_MAX32
4040
#define TIMESTAMP_MIN_VALUE 0
41+
#else
42+
/* Use 4 byte unsigned timestamp */
43+
#define MY_TIME_T_MAX ((longlong) UINT_MAX32)
44+
#define MY_TIME_T_MIN 0
45+
#define TIMESTAMP_MAX_YEAR 2106
46+
#define TIMESTAMP_MIN_VALUE 0
47+
#define TIMESTAMP_MAX_VALUE ((longlong) UINT_MAX32)
48+
#define TIMESTAMP_MAX_MONTH 2
49+
#define TIMESTAMP_MAX_DAY 7
50+
#endif /* SIZEOF_LONG */
4151

52+
#define TIMESTAMP_MIN_YEAR (1900 + YY_PART_YEAR - 1)
4253
/* two-digit years < this are 20..; >= this are 19.. */
4354
#define YY_PART_YEAR 70
4455

@@ -48,8 +59,8 @@ extern uchar days_in_month[];
4859
*/
4960
#if SIZEOF_TIME_T > 4 || defined(TIME_T_UNSIGNED)
5061
# define IS_TIME_T_VALID_FOR_TIMESTAMP(x) \
51-
((x) <= TIMESTAMP_MAX_VALUE && \
52-
(x) >= TIMESTAMP_MIN_VALUE)
62+
((ulonglong) (x) <= TIMESTAMP_MAX_VALUE && \
63+
((x) >= TIMESTAMP_MIN_VALUE)
5364
#else
5465
# define IS_TIME_T_VALID_FOR_TIMESTAMP(x) \
5566
((x) >= TIMESTAMP_MIN_VALUE)
@@ -181,7 +192,6 @@ void my_init_time(void);
181192
static inline my_bool validate_timestamp_range(const MYSQL_TIME *t)
182193
{
183194
if ((t->year > TIMESTAMP_MAX_YEAR || t->year < TIMESTAMP_MIN_YEAR) ||
184-
(t->year == TIMESTAMP_MAX_YEAR && (t->month > 1 || t->day > 19)) ||
185195
(t->year == TIMESTAMP_MIN_YEAR && (t->month < 12 || t->day < 31)))
186196
return FALSE;
187197

include/mysql_time.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,14 @@
1919

2020
/*
2121
Portable time_t replacement.
22-
Should be signed and hold seconds for 1902 -- 2038-01-19 range
23-
i.e at least a 32bit variable
22+
For 32 bit systems holds seconds for 1970 - 2038-01-19 range
23+
For 64 bit systems (where long is 64 bit) holds seconds for 1970 - 2106
2424
2525
Using the system built in time_t is not an option as
2626
we rely on the above requirements in the time functions
2727
*/
2828
typedef long my_time_t;
2929

30-
3130
/*
3231
Time declarations shared between the server and client API:
3332
you should not add anything to this header unless it's used

libmysqld/libmysql.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,8 +1314,8 @@ static my_bool reset_stmt_handle(MYSQL_STMT *stmt, uint flags);
13141314
/* A macro to check truncation errors */
13151315

13161316
#define IS_TRUNCATED(value, is_unsigned, min, max, umax) \
1317-
((is_unsigned) ? (((value) > (umax) || (value) < 0) ? 1 : 0) : \
1318-
(((value) > (max) || (value) < (min)) ? 1 : 0))
1317+
((is_unsigned) ? (( (value) > (umax)) ? 1 : 0) : \
1318+
(((value) > (max) || (value) < (min)) ? 1 : 0))
13191319

13201320
#define BIND_RESULT_DONE 1
13211321
/*

mysql-test/include/grep.inc

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,22 @@
33
# Usage:
44
# --let $grep_file= /path/to/your/file
55
# --let $grep_regex= your_regex_string
6+
# --let $grep_filter= filter
67
# --source include/grep.inc
78

9+
810
--perl
911
open (my $fh, "<", "$ENV{grep_file}") or die $!;
1012
while (<$fh>)
1113
{
12-
/$ENV{grep_regex}/ &&
14+
if (/$ENV{grep_regex}/)
15+
{
16+
if ($ENV{grep_filter})
17+
{
18+
eval($ENV{grep_filter});
19+
}
1320
print;
21+
}
1422
}
1523
close $fh;
1624
EOF
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
disable_query_log;
2+
disable_warnings;
3+
if (`SELECT from_unixtime((1<<31)+24*3600) is not null`) {
4+
--skip Need a 32 bit timestamps
5+
}
6+
enable_warnings;
7+
enable_query_log;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
disable_query_log;
2+
disable_warnings;
3+
if (`SELECT from_unixtime((1<<31)+24*3600) is null`) {
4+
--skip Need a 64 bit timestamps
5+
}
6+
enable_warnings;
7+
enable_query_log;

mysql-test/include/show_events.inc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ let $script=
117117
s{SONAME ".*"}{SONAME "LIB"};
118118
s{DOLLARmysqltest_vardir}{MYSQLTEST_VARDIR}g;
119119
s{$binlog_database}{database};
120+
s{2147483647.999999}{MAX_TIMESTAMP};
121+
s{4294967295.999999}{MAX_TIMESTAMP};
120122
$modify_binlog_name
121123
||
122124

mysql-test/main/alter_table_online_debug.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,7 @@ set debug_sync= 'now SIGNAL end';
582582
--connection default
583583
--reap
584584
show create table t1;
585+
--replace_result 4294967295 2147483647
585586
select *, UNIX_TIMESTAMP(row_start), UNIX_TIMESTAMP(row_end) from t1 for system_time all;
586587

587588
## at the moment DROP SYSTEM VERSIONING cannot be done online

mysql-test/main/func_time.result

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -581,37 +581,18 @@ NULL
581581
select from_unixtime(2147483647);
582582
from_unixtime(2147483647)
583583
2038-01-19 06:14:07
584-
select from_unixtime(2147483648);
585-
from_unixtime(2147483648)
586-
NULL
587-
Warnings:
588-
Warning 1292 Truncated incorrect unixtime value: '2147483648'
589584
select from_unixtime(0);
590585
from_unixtime(0)
591586
1970-01-01 03:00:00
592587
select unix_timestamp(from_unixtime(2147483647));
593588
unix_timestamp(from_unixtime(2147483647))
594589
2147483647
595-
select unix_timestamp(from_unixtime(2147483648));
596-
unix_timestamp(from_unixtime(2147483648))
597-
NULL
598-
Warnings:
599-
Warning 1292 Truncated incorrect unixtime value: '2147483648'
600-
select unix_timestamp('2039-01-20 01:00:00');
601-
unix_timestamp('2039-01-20 01:00:00')
602-
NULL
603590
select unix_timestamp('1968-01-20 01:00:00');
604591
unix_timestamp('1968-01-20 01:00:00')
605592
NULL
606-
select unix_timestamp('2038-02-10 01:00:00');
607-
unix_timestamp('2038-02-10 01:00:00')
608-
NULL
609593
select unix_timestamp('1969-11-20 01:00:00');
610594
unix_timestamp('1969-11-20 01:00:00')
611595
NULL
612-
select unix_timestamp('2038-01-20 01:00:00');
613-
unix_timestamp('2038-01-20 01:00:00')
614-
NULL
615596
select unix_timestamp('1969-12-30 01:00:00');
616597
unix_timestamp('1969-12-30 01:00:00')
617598
NULL
@@ -621,9 +602,6 @@ unix_timestamp('2038-01-17 12:00:00')
621602
select unix_timestamp('1970-01-01 03:00:01');
622603
unix_timestamp('1970-01-01 03:00:01')
623604
1
624-
select unix_timestamp('2038-01-19 07:14:07');
625-
unix_timestamp('2038-01-19 07:14:07')
626-
NULL
627605
SELECT CHARSET(DAYNAME(19700101));
628606
CHARSET(DAYNAME(19700101))
629607
latin1

mysql-test/main/func_time.test

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,6 @@ select unix_timestamp('1969-12-01 19:00:01');
276276
select from_unixtime(-1);
277277
# check for from_unixtime(2^31-1) and from_unixtime(2^31)
278278
select from_unixtime(2147483647);
279-
select from_unixtime(2147483648);
280279
select from_unixtime(0);
281280

282281
#
@@ -285,18 +284,14 @@ select from_unixtime(0);
285284
# unix_timestamp are consistent, when working with boundary dates.
286285
#
287286
select unix_timestamp(from_unixtime(2147483647));
288-
select unix_timestamp(from_unixtime(2147483648));
289287

290288
# check for invalid dates
291289

292290
# bad year
293-
select unix_timestamp('2039-01-20 01:00:00');
294291
select unix_timestamp('1968-01-20 01:00:00');
295292
# bad month
296-
select unix_timestamp('2038-02-10 01:00:00');
297293
select unix_timestamp('1969-11-20 01:00:00');
298294
# bad day
299-
select unix_timestamp('2038-01-20 01:00:00');
300295
select unix_timestamp('1969-12-30 01:00:00');
301296

302297
#
@@ -310,9 +305,6 @@ select unix_timestamp('2038-01-17 12:00:00');
310305
#
311306
select unix_timestamp('1970-01-01 03:00:01');
312307

313-
# check bad date, close to the boundary (we cut them off in the very end)
314-
select unix_timestamp('2038-01-19 07:14:07');
315-
316308
#
317309
# Bug #28759: DAYNAME() and MONTHNAME() return binary string
318310
#

mysql-test/main/func_time_32.result

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#
2+
# Test dates close to upper range
3+
#
4+
set time_zone="+03:00";
5+
select from_unixtime(2147483648);
6+
from_unixtime(2147483648)
7+
NULL
8+
Warnings:
9+
Warning 1292 Truncated incorrect unixtime value: '2147483648'
10+
select unix_timestamp(from_unixtime(2147483648));
11+
unix_timestamp(from_unixtime(2147483648))
12+
NULL
13+
Warnings:
14+
Warning 1292 Truncated incorrect unixtime value: '2147483648'
15+
# bad year
16+
select unix_timestamp('2039-01-20 01:00:00');
17+
unix_timestamp('2039-01-20 01:00:00')
18+
NULL
19+
# bad month
20+
select unix_timestamp('2038-02-10 01:00:00');
21+
unix_timestamp('2038-02-10 01:00:00')
22+
NULL
23+
# bad day
24+
select unix_timestamp('2038-01-20 01:00:00');
25+
unix_timestamp('2038-01-20 01:00:00')
26+
NULL
27+
# check bad date, close to the boundary (we cut them off in the very end)
28+
select unix_timestamp('2038-01-19 07:14:07');
29+
unix_timestamp('2038-01-19 07:14:07')
30+
NULL
31+
set time_zone=MET;
32+
select unix_timestamp('2038-01-19 04:14:07'),
33+
unix_timestamp('2038-01-19 04:14:08');
34+
unix_timestamp('2038-01-19 04:14:07') unix_timestamp('2038-01-19 04:14:08')
35+
2147483647 NULL
36+
set time_zone= @@global.time_zone;
37+
#
38+
# Functions that construct DATETIME
39+
#
40+
SET time_zone='+00:00';
41+
SET sql_mode=IF(@@version LIKE '%MariaDB%', 'TIME_ROUND_FRACTIONAL', '');
42+
CREATE TABLE t1 (id SERIAL, a DECIMAL(30,10));
43+
INSERT INTO t1 (a) VALUES (2147483647.9999999);
44+
SELECT a, FROM_UNIXTIME(a) FROM t1 ORDER BY id;
45+
a FROM_UNIXTIME(a)
46+
2147483647.9999999000 NULL
47+
DROP TABLE t1;
48+
set time_zone= @@global.time_zone;
49+
set sql_mode=default;
50+
#
51+
# Corner case:
52+
# ALTER TIMESTAMP to a shorter TIMESTAMP
53+
# All values round, maximum possible value truncates.
54+
#
55+
SET time_zone='+00:00';
56+
SET sql_mode=IF(@@version LIKE '%MariaDB%', 'TIME_ROUND_FRACTIONAL', '');
57+
CREATE TABLE t1 (ID INT, a TIMESTAMP(6), comment VARCHAR(64));
58+
INSERT INTO t1 VALUES (0, '2038-01-18 23:59:59.999999', 'Should round');
59+
INSERT INTO t1 VALUES (1, '2038-01-19 03:14:06.999999', 'Should round');
60+
INSERT INTO t1 VALUES (2, '2038-01-19 03:14:07.999999', 'Should truncate');
61+
ALTER TABLE t1 MODIFY a TIMESTAMP(5);
62+
Warnings:
63+
Warning 1264 Out of range value for column 'a' at row 3
64+
SELECT * FROM t1;
65+
ID a comment
66+
0 2038-01-19 00:00:00.00000 Should round
67+
1 2038-01-19 03:14:07.00000 Should round
68+
2 2038-01-19 03:14:07.99999 Should truncate
69+
DROP TABLE t1;
70+
set time_zone= @@global.time_zone;
71+
set sql_mode=default;
72+
73+
# Let us test range for TIMESTAMP
74+
#
75+
create table t1 (ts timestamp);
76+
set time_zone='UTC';
77+
insert into t1 values ('2038-01-19 03:14:07'),('2038-01-19 03:14:08');
78+
Warnings:
79+
Warning 1264 Out of range value for column 'ts' at row 2
80+
select * from t1;
81+
ts
82+
2038-01-19 03:14:07
83+
0000-00-00 00:00:00
84+
truncate table t1;
85+
set time_zone='MET';
86+
insert into t1 values ('2038-01-19 04:14:07'),('2038-01-19 04:14:08');
87+
Warnings:
88+
Warning 1264 Out of range value for column 'ts' at row 2
89+
select * from t1;
90+
ts
91+
2038-01-19 04:14:07
92+
0000-00-00 00:00:00
93+
truncate table t1;
94+
set time_zone='+01:30';
95+
insert into t1 values ('2038-01-19 04:44:07'),('2038-01-19 04:44:08');
96+
Warnings:
97+
Warning 1264 Out of range value for column 'ts' at row 2
98+
select * from t1;
99+
ts
100+
2038-01-19 04:44:07
101+
0000-00-00 00:00:00
102+
drop table t1;
103+
SET time_zone='+00:00';
104+
CREATE TABLE t1 (ts0 TIMESTAMP, ts1 TIMESTAMP(1));
105+
INSERT INTO t1 VALUES ('2001-01-01 10:20:30', '2001-01-01 10:20:30.9');
106+
# Corner case
107+
UPDATE t1 SET ts1=FROM_UNIXTIME(2147483647.9);
108+
UPDATE t1 SET ts0=COALESCE(ts1);
109+
SELECT * FROM t1;
110+
ts0 ts1
111+
2038-01-19 03:14:07 2038-01-19 03:14:07.9
112+
DROP TABLE t1;
113+
SET time_zone=DEFAULT;
114+
# Let us test CONVERT_TZ function
115+
select convert_tz('2038-01-19 04:14:08', 'MET', 'UTC');
116+
convert_tz('2038-01-19 04:14:08', 'MET', 'UTC')
117+
2038-01-19 04:14:08
118+
select convert_tz('2103-01-01 04:00:00', 'MET', 'UTC');
119+
convert_tz('2103-01-01 04:00:00', 'MET', 'UTC')
120+
2103-01-01 04:00:00

0 commit comments

Comments
 (0)