From 6a8e62e9a11a4ed3ab3fc3c938a89e76f364d355 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Thu, 9 Jun 2022 00:12:20 +0200 Subject: [PATCH] fix: raise DataError instead of OverflowError loading intervals too large This happened only for the binary format, not the text format. --- docs/news.rst | 2 ++ psycopg/psycopg/types/datetime.py | 6 +++++- psycopg_c/psycopg_c/types/datetime.pyx | 5 ++++- tests/types/test_datetime.py | 5 +++-- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/docs/news.rst b/docs/news.rst index 441960e41..917c86e0a 100644 --- a/docs/news.rst +++ b/docs/news.rst @@ -36,6 +36,8 @@ Psycopg 3.0.15 (unreleased) - Fix wrong escaping of unprintable chars in COPY (nonetheless correctly interpreted by PostgreSQL). - Restore the connection to usable state after an error in `~Cursor.stream()`. +- Raise `DataError` instead of `OverflowError` loading binary intervals + out-of-range. Current release diff --git a/psycopg/psycopg/types/datetime.py b/psycopg/psycopg/types/datetime.py index 3a0394b8e..763a2340a 100644 --- a/psycopg/psycopg/types/datetime.py +++ b/psycopg/psycopg/types/datetime.py @@ -671,7 +671,11 @@ class IntervalBinaryLoader(Loader): elif months < 0: years, months = divmod(-months, 12) days = days - 30 * months - 365 * years - return timedelta(days=days, microseconds=micros) + + try: + return timedelta(days=days, microseconds=micros) + except OverflowError as e: + raise DataError(f"can't parse interval: {e}") from None def _get_datestyle(conn: Optional["BaseConnection[Any]"]) -> bytes: diff --git a/psycopg_c/psycopg_c/types/datetime.pyx b/psycopg_c/psycopg_c/types/datetime.pyx index 68580204a..b56470904 100644 --- a/psycopg_c/psycopg_c/types/datetime.pyx +++ b/psycopg_c/psycopg_c/types/datetime.pyx @@ -992,7 +992,10 @@ cdef class IntervalBinaryLoader(CLoader): usdays = -usdays us = -us - return cdt.timedelta_new(days + usdays, ussecs, us) + try: + return cdt.timedelta_new(days + usdays, ussecs, us) + except OverflowError as ex: + raise e.DataError(f"can't parse interval: {ex}") cdef const char *_parse_date_values( diff --git a/tests/types/test_datetime.py b/tests/types/test_datetime.py index 4a8c56f28..66d026230 100644 --- a/tests/types/test_datetime.py +++ b/tests/types/test_datetime.py @@ -681,9 +681,10 @@ class TestInterval: cur.execute(f"select '{expr}'::interval") assert cur.fetchone()[0] == as_td(val) + @pytest.mark.parametrize("fmt_out", pq.Format) @pytest.mark.parametrize("val", ["min", "max"]) - def test_load_interval_overflow(self, conn, val): - cur = conn.cursor() + def test_load_interval_overflow(self, conn, val, fmt_out): + cur = conn.cursor(binary=fmt_out) cur.execute( "select %s + %s * '1s'::interval", (as_td(val), -1 if val == "min" else 1), -- 2.47.2