diff --git a/contrib/babelfishpg_common/src/datetime2.c b/contrib/babelfishpg_common/src/datetime2.c index f39967c7eae..79a85d6fb70 100644 --- a/contrib/babelfishpg_common/src/datetime2.c +++ b/contrib/babelfishpg_common/src/datetime2.c @@ -9,6 +9,7 @@ #include "postgres.h" #include "fmgr.h" +#include "common/int.h" #include "utils/builtins.h" #include "utils/date.h" #include "utils/datetime.h" @@ -491,7 +492,22 @@ date_datetime2(PG_FUNCTION_ARGS) else if (DATE_IS_NOEND(dateVal)) TIMESTAMP_NOEND(result); else - result = dateVal * USECS_PER_DAY; + { + /* + * Use overflow-safe multiplication to prevent int64 overflow. + * dateVal * USECS_PER_DAY can overflow for dates outside the + * valid datetime2 range (0001-01-01 to 9999-12-31). + */ + int64 product; + + if (pg_mul_s64_overflow((int64) dateVal, USECS_PER_DAY, &product) || + !IS_VALID_DATETIME2(product)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("data out of range for datetime2"))); + + result = (Timestamp) product; + } PG_RETURN_TIMESTAMP(result); } diff --git a/test/JDBC/expected/cast_date_to_datetime2.out b/test/JDBC/expected/cast_date_to_datetime2.out new file mode 100644 index 00000000000..89995fea263 --- /dev/null +++ b/test/JDBC/expected/cast_date_to_datetime2.out @@ -0,0 +1,49 @@ + +-- Test DATE to DATETIME2 cast with out-of-range dates (BABEL-4527) +-- Out-of-range: year 999999 (exceeds datetime2 max of 9999-12-31) +SELECT CAST(CAST('999999-12-31' AS DATE) AS datetime2); +GO +~~START~~ +datetime2 +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: data out of range for datetime2)~~ + + +-- Out-of-range: year 10000 (just past valid range) +SELECT CAST(CAST('10000-01-01' AS DATE) AS datetime2); +GO +~~START~~ +datetime2 +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: data out of range for datetime2)~~ + + +-- Valid boundary: max datetime2 date +SELECT CAST(CAST('9999-12-31' AS DATE) AS datetime2); +GO +~~START~~ +datetime2 +9999-12-31 00:00:00.0000000 +~~END~~ + + +-- Out-of-range: date before datetime2 min (PostgreSQL min date, 4714-11-24 BC) +SELECT CAST(CAST('4714-11-24 BC' AS DATE) AS datetime2); +GO +~~START~~ +datetime2 +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: data out of range for datetime2)~~ + + +-- Valid boundary: min datetime2 date +SELECT CAST(CAST('0001-01-01' AS DATE) AS datetime2); +GO +~~START~~ +datetime2 +0001-01-01 00:00:00.0000000 +~~END~~ + diff --git a/test/JDBC/input/cast_date_to_datetime2.sql b/test/JDBC/input/cast_date_to_datetime2.sql new file mode 100644 index 00000000000..fb22210f37c --- /dev/null +++ b/test/JDBC/input/cast_date_to_datetime2.sql @@ -0,0 +1,21 @@ +-- Test DATE to DATETIME2 cast with out-of-range dates (BABEL-4527) + +-- Out-of-range: year 999999 (exceeds datetime2 max of 9999-12-31) +SELECT CAST(CAST('999999-12-31' AS DATE) AS datetime2); +GO + +-- Out-of-range: year 10000 (just past valid range) +SELECT CAST(CAST('10000-01-01' AS DATE) AS datetime2); +GO + +-- Valid boundary: max datetime2 date +SELECT CAST(CAST('9999-12-31' AS DATE) AS datetime2); +GO + +-- Out-of-range: date before datetime2 min (PostgreSQL min date, 4714-11-24 BC) +SELECT CAST(CAST('4714-11-24 BC' AS DATE) AS datetime2); +GO + +-- Valid boundary: min datetime2 date +SELECT CAST(CAST('0001-01-01' AS DATE) AS datetime2); +GO