Skip to content

Commit fadef5c

Browse files
authored
fix: Add range validation in DATE to DATETIME2 cast (#4799) (#4882)
CAST(DATE AS DATETIME2) hangs for dates outside the valid datetime2 range (0001-01-01 to 9999-12-31) because the multiplication dateVal * USECS_PER_DAY overflows int64. Add a pre-multiplication bounds check in date_datetime2() to raise 'data out of range for datetime2' for out-of-range input dates. Task: BABEL-4527 Signed-off-by: Kristian Lejao <klejao@amazon.com>
1 parent 4d08312 commit fadef5c

3 files changed

Lines changed: 87 additions & 1 deletion

File tree

contrib/babelfishpg_common/src/datetime2.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "postgres.h"
1010

1111
#include "fmgr.h"
12+
#include "common/int.h"
1213
#include "utils/builtins.h"
1314
#include "utils/date.h"
1415
#include "utils/datetime.h"
@@ -491,7 +492,22 @@ date_datetime2(PG_FUNCTION_ARGS)
491492
else if (DATE_IS_NOEND(dateVal))
492493
TIMESTAMP_NOEND(result);
493494
else
494-
result = dateVal * USECS_PER_DAY;
495+
{
496+
/*
497+
* Use overflow-safe multiplication to prevent int64 overflow.
498+
* dateVal * USECS_PER_DAY can overflow for dates outside the
499+
* valid datetime2 range (0001-01-01 to 9999-12-31).
500+
*/
501+
int64 product;
502+
503+
if (pg_mul_s64_overflow((int64) dateVal, USECS_PER_DAY, &product) ||
504+
!IS_VALID_DATETIME2(product))
505+
ereport(ERROR,
506+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
507+
errmsg("data out of range for datetime2")));
508+
509+
result = (Timestamp) product;
510+
}
495511

496512
PG_RETURN_TIMESTAMP(result);
497513
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
2+
-- Test DATE to DATETIME2 cast with out-of-range dates (BABEL-4527)
3+
-- Out-of-range: year 999999 (exceeds datetime2 max of 9999-12-31)
4+
SELECT CAST(CAST('999999-12-31' AS DATE) AS datetime2);
5+
GO
6+
~~START~~
7+
datetime2
8+
~~ERROR (Code: 33557097)~~
9+
10+
~~ERROR (Message: data out of range for datetime2)~~
11+
12+
13+
-- Out-of-range: year 10000 (just past valid range)
14+
SELECT CAST(CAST('10000-01-01' AS DATE) AS datetime2);
15+
GO
16+
~~START~~
17+
datetime2
18+
~~ERROR (Code: 33557097)~~
19+
20+
~~ERROR (Message: data out of range for datetime2)~~
21+
22+
23+
-- Valid boundary: max datetime2 date
24+
SELECT CAST(CAST('9999-12-31' AS DATE) AS datetime2);
25+
GO
26+
~~START~~
27+
datetime2
28+
9999-12-31 00:00:00.0000000
29+
~~END~~
30+
31+
32+
-- Out-of-range: date before datetime2 min (PostgreSQL min date, 4714-11-24 BC)
33+
SELECT CAST(CAST('4714-11-24 BC' AS DATE) AS datetime2);
34+
GO
35+
~~START~~
36+
datetime2
37+
~~ERROR (Code: 33557097)~~
38+
39+
~~ERROR (Message: data out of range for datetime2)~~
40+
41+
42+
-- Valid boundary: min datetime2 date
43+
SELECT CAST(CAST('0001-01-01' AS DATE) AS datetime2);
44+
GO
45+
~~START~~
46+
datetime2
47+
0001-01-01 00:00:00.0000000
48+
~~END~~
49+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
-- Test DATE to DATETIME2 cast with out-of-range dates (BABEL-4527)
2+
3+
-- Out-of-range: year 999999 (exceeds datetime2 max of 9999-12-31)
4+
SELECT CAST(CAST('999999-12-31' AS DATE) AS datetime2);
5+
GO
6+
7+
-- Out-of-range: year 10000 (just past valid range)
8+
SELECT CAST(CAST('10000-01-01' AS DATE) AS datetime2);
9+
GO
10+
11+
-- Valid boundary: max datetime2 date
12+
SELECT CAST(CAST('9999-12-31' AS DATE) AS datetime2);
13+
GO
14+
15+
-- Out-of-range: date before datetime2 min (PostgreSQL min date, 4714-11-24 BC)
16+
SELECT CAST(CAST('4714-11-24 BC' AS DATE) AS datetime2);
17+
GO
18+
19+
-- Valid boundary: min datetime2 date
20+
SELECT CAST(CAST('0001-01-01' AS DATE) AS datetime2);
21+
GO

0 commit comments

Comments
 (0)