Skip to content

Commit

Permalink
feat(datafusion): add coalesce, nullif, ifnull, zeroifnull
Browse files Browse the repository at this point in the history
  • Loading branch information
mesejo authored and cpcloud committed Aug 8, 2023
1 parent 4a066f0 commit 1cc67c9
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 6 deletions.
26 changes: 26 additions & 0 deletions ibis/backends/datafusion/compiler.py
Expand Up @@ -526,6 +526,32 @@ def null_if_zero(op, **kw):
return df.functions.nullif(arg, df.literal(0))


@translate.register(ops.Coalesce)
def coalesce(op, **kw):
args = (translate(arg, **kw) for arg in op.arg)
return df.functions.coalesce(*args)


@translate.register(ops.NullIf)
def nullif(op, **kw):
arg = translate(op.arg, **kw)
null_if_value = translate(op.null_if_expr, **kw)
return df.functions.nullif(arg, null_if_value)


@translate.register(ops.IfNull)
def if_null(op, **kw):
arg = translate(op.arg, **kw)
ifnull_expr = translate(op.ifnull_expr, **kw)
return df.functions.coalesce(arg, ifnull_expr)


@translate.register(ops.ZeroIfNull)
def zero_if_null(op, **kw):
arg = translate(op.arg, **kw)
return df.functions.coalesce(arg, df.literal(0))


@translate.register(ops.Log)
def log(op, **kw):
arg = translate(op.arg, **kw)
Expand Down
7 changes: 2 additions & 5 deletions ibis/backends/tests/test_generic.py
Expand Up @@ -108,7 +108,6 @@ def test_boolean_literal(con, backend):
param(ibis.literal(10).nullif(5), 10, id="nullif_not_null"),
],
)
@pytest.mark.notimpl(["datafusion"])
def test_scalar_fillna_nullif(con, expr, expected):
if expected is None:
# The exact kind of null value used differs per backend (and version).
Expand Down Expand Up @@ -168,14 +167,15 @@ def test_isna(backend, alltypes, col, filt):
"snowflake",
"polars",
"trino",
"datafusion",
],
reason="NaN != NULL for these backends",
),
id="nan_col",
),
],
)
@pytest.mark.notimpl(["datafusion", "mssql", "druid", "oracle"])
@pytest.mark.notimpl(["mssql", "druid", "oracle"])
def test_column_fillna(backend, alltypes, value):
table = alltypes.mutate(missing=ibis.literal(value).cast("float64"))
pd_table = table.execute()
Expand All @@ -193,7 +193,6 @@ def test_column_fillna(backend, alltypes, value):
param(ibis.coalesce(ibis.NA, ibis.NA, 3.14), 3.14, id="non_null_last"),
],
)
@pytest.mark.notimpl(["datafusion"])
def test_coalesce(con, expr, expected):
result = con.execute(expr.name("tmp"))

Expand Down Expand Up @@ -670,7 +669,6 @@ def test_logical_negation_column(backend, alltypes, df, op):
backend.assert_series_equal(result, expected, check_names=False)


@pytest.mark.notimpl(["datafusion"])
@pytest.mark.parametrize(
("dtype", "zero", "expected"),
[("int64", 0, 1), ("float64", 0.0, 1.0)],
Expand All @@ -680,7 +678,6 @@ def test_zeroifnull_literals(con, dtype, zero, expected):
assert con.execute(ibis.literal(expected, type=dtype).zeroifnull()) == expected


@pytest.mark.notimpl(["datafusion"])
def test_zeroifnull_column(backend, alltypes, df):
expr = alltypes.int_col.nullif(1).zeroifnull().name("tmp")
result = expr.execute().astype("int32")
Expand Down
2 changes: 1 addition & 1 deletion ibis/expr/types/generic.py
Expand Up @@ -279,7 +279,7 @@ def fillna(self, fill_value: Scalar) -> Value:
def nullif(self, null_if_expr: Value) -> Value:
"""Set values to null if they equal the values `null_if_expr`.
Commonly use to avoid divide-by-zero problems by replacing zero with
Commonly used to avoid divide-by-zero problems by replacing zero with
`NULL` in the divisor.
Parameters
Expand Down

0 comments on commit 1cc67c9

Please sign in to comment.