Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(clickhouse): fix truncating to date from a timestamp #10220

Merged
merged 1 commit into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
SELECT
toStartOfDay(parseDateTimeBestEffort('2009-05-17T12:34:56')) AS "TimestampTruncate(datetime.datetime(2009, 5, 17, 12, 34, 56), DAY)"
CAST(toStartOfDay(parseDateTimeBestEffort('2009-05-17T12:34:56')) AS Nullable(DateTime)) AS "TimestampTruncate(datetime.datetime(2009, 5, 17, 12, 34, 56), DAY)"
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
SELECT
toMonday(parseDateTimeBestEffort('2009-05-17T12:34:56')) AS "TimestampTruncate(datetime.datetime(2009, 5, 17, 12, 34, 56), WEEK)"
CAST(toMonday(parseDateTimeBestEffort('2009-05-17T12:34:56')) AS Nullable(DateTime)) AS "TimestampTruncate(datetime.datetime(2009, 5, 17, 12, 34, 56), WEEK)"
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
SELECT
toStartOfYear(parseDateTimeBestEffort('2009-05-17T12:34:56')) AS "TimestampTruncate(datetime.datetime(2009, 5, 17, 12, 34, 56), YEAR)"
CAST(toStartOfYear(parseDateTimeBestEffort('2009-05-17T12:34:56')) AS Nullable(DateTime)) AS "TimestampTruncate(datetime.datetime(2009, 5, 17, 12, 34, 56), YEAR)"
16 changes: 16 additions & 0 deletions ibis/backends/clickhouse/tests/test_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -492,3 +492,19 @@ def my_eq(a: int, b: int) -> bool: ...
expr = alltypes.int_col.collect().filter(lambda x: my_eq(x, 1))
result = expr.execute()
assert set(result) == {1}


def test_timestamp_to_start_of_week(con):
pytest.importorskip("pyarrow")

expr = ibis.timestamp("2024-02-03 00:00:00").truncate("W")
result = con.to_pyarrow(expr).as_py()
assert result == datetime(2024, 1, 29, 0, 0, 0)


def test_date_to_start_of_day(con):
pytest.importorskip("pyarrow")

expr = ibis.date("2024-02-03")
expr1 = expr.truncate("D")
assert con.to_pyarrow(expr1) == con.to_pyarrow(expr)
26 changes: 20 additions & 6 deletions ibis/backends/sql/compilers/clickhouse.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,21 +377,35 @@ def visit_TimestampFromUNIX(self, op, *, arg, unit):

def visit_TimestampTruncate(self, op, *, arg, unit):
if (short := unit.short) == "W":
func = "toMonday"
funcname = "toMonday"
else:
func = f"toStartOf{unit.singular.capitalize()}"
funcname = f"toStartOf{unit.singular.capitalize()}"

if short in ("s", "ms", "us", "ns"):
arg = self.f.toDateTime64(arg, op.arg.dtype.scale or 0)
return self.f[func](arg)
func = self.f[funcname]

if short in ("Y", "Q", "M", "W", "D"):
# these units return `Date` so we have to cast back to the
# corresponding Ibis type
return self.cast(func(arg), op.dtype)
elif short in ("s", "ms", "us", "ns"):
return func(self.f.toDateTime64(arg, op.arg.dtype.scale or 0))
else:
assert short in ("h", "m"), short
return func(arg)

visit_TimeTruncate = visit_TimestampTruncate

def visit_DateTruncate(self, op, *, arg, unit):
if unit.short == "W":
if unit.short == "D":
# no op because truncating a date to a date has no effect
return arg
elif unit.short == "W":
func = "toMonday"
else:
func = f"toStartOf{unit.singular.capitalize()}"

# no cast needed here because all of the allowed units return `Date`
# values
return self.f[func](arg)

def visit_TimestampBucket(self, op, *, arg, interval, offset):
Expand Down