From 7b79ec8f67d8e038c4deef3740f299d472269809 Mon Sep 17 00:00:00 2001 From: Phillip Cloud <417981+cpcloud@users.noreply.github.com> Date: Mon, 8 Jan 2024 14:53:44 -0500 Subject: [PATCH] fix(postgres): ensure that no timezone conversion takes place on timestamptz columns when selecting them out --- ibis/backends/postgres/registry.py | 5 -- .../test_timezone_from_column/out.sql | 15 ++++++ ibis/backends/postgres/tests/test_client.py | 49 +++++++++++++++++++ 3 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 ibis/backends/postgres/tests/snapshots/test_client/test_timezone_from_column/out.sql diff --git a/ibis/backends/postgres/registry.py b/ibis/backends/postgres/registry.py index bbca6e9c20db..961fb61741c5 100644 --- a/ibis/backends/postgres/registry.py +++ b/ibis/backends/postgres/registry.py @@ -309,11 +309,6 @@ def _table_column(t, op): sa_table = get_sqla_table(ctx, table) out_expr = get_col(sa_table, op) - if op.dtype.is_timestamp(): - timezone = op.dtype.timezone - if timezone is not None: - out_expr = out_expr.op("AT TIME ZONE")(timezone).label(op.name) - # If the column does not originate from the table set in the current SELECT # context, we should format as a subquery if t.permit_subquery and ctx.is_foreign_expr(table): diff --git a/ibis/backends/postgres/tests/snapshots/test_client/test_timezone_from_column/out.sql b/ibis/backends/postgres/tests/snapshots/test_client/test_timezone_from_column/out.sql new file mode 100644 index 000000000000..b4b6f555f34c --- /dev/null +++ b/ibis/backends/postgres/tests/snapshots/test_client/test_timezone_from_column/out.sql @@ -0,0 +1,15 @@ +WITH t0 AS ( + SELECT + t2.id AS id, + t2.ts_tz AS tz, + t2.ts_no_tz AS no_tz + FROM x AS t2 +) +SELECT + t0.id, + CAST(t0.tz AS TIMESTAMPTZ) AS tz, + CAST(t0.no_tz AS TIMESTAMP) AS no_tz, + t1.id AS id_right +FROM t0 +LEFT OUTER JOIN y AS t1 + ON t0.id = t1.id \ No newline at end of file diff --git a/ibis/backends/postgres/tests/test_client.py b/ibis/backends/postgres/tests/test_client.py index b945064fb6ab..0029a5925d1b 100644 --- a/ibis/backends/postgres/tests/test_client.py +++ b/ibis/backends/postgres/tests/test_client.py @@ -17,6 +17,7 @@ import numpy as np import pandas as pd +import pandas.testing as tm import pytest from pytest import param @@ -223,3 +224,51 @@ def test_insert_with_cte(con): def test_connect_url_with_empty_host(): con = ibis.connect("postgres:///ibis_testing") assert con.con.url.host is None + + +@pytest.fixture(scope="module") +def contz(con): + with con.begin() as c: + tz = c.exec_driver_sql("SHOW TIMEZONE").scalar() + c.exec_driver_sql("SET TIMEZONE TO 'America/New_York'") + + yield con + + with con.begin() as c: + c.exec_driver_sql(f"SET TIMEZONE TO '{tz}'") + + +def test_timezone_from_column(contz, snapshot): + with contz.begin() as c: + c.exec_driver_sql( + """ + CREATE TEMPORARY TABLE x ( + id BIGINT, + ts_tz TIMESTAMP WITH TIME ZONE NOT NULL, + ts_no_tz TIMESTAMP WITHOUT TIME ZONE NOT NULL + ); + + INSERT INTO x VALUES + (1, '2018-01-01 00:00:01+00', '2018-01-01 00:00:02'); + + CREATE TEMPORARY TABLE y AS SELECT 1::BIGINT AS id; + """ + ) + + case = ( + contz.table("x") + .rename(tz="ts_tz", no_tz="ts_no_tz") + .left_join(contz.table("y"), "id") + ) + + result = case.execute() + expected = pd.DataFrame( + { + "id": [1], + "tz": [pd.Timestamp("2018-01-01 00:00:01", tz="UTC")], + "no_tz": [pd.Timestamp("2018-01-01 00:00:02")], + "id_right": [1], + } + ) + tm.assert_frame_equal(result, expected) + snapshot.assert_match(ibis.to_sql(case), "out.sql")