Skip to content

Commit

Permalink
feat(bigquery): add JS UDF support
Browse files Browse the repository at this point in the history
  • Loading branch information
krzysztof-kwitt authored and cpcloud committed Jan 18, 2023
1 parent e8ebf23 commit e74328b
Show file tree
Hide file tree
Showing 9 changed files with 331 additions and 181 deletions.
2 changes: 2 additions & 0 deletions ibis/backends/bigquery/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ class UDFContext(TypeTranslationContext):
__slots__ = ()


UDF_CONTEXT = UDFContext()

ibis_type_to_bigquery_type = Dispatcher("ibis_type_to_bigquery_type")


Expand Down
6 changes: 5 additions & 1 deletion ibis/backends/bigquery/tests/system/udf/test_udf_execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ def df(alltypes):


def test_udf(alltypes, df):
@udf(input_type=[dt.double, dt.double], output_type=dt.double)
@udf(
input_type=[dt.double, dt.double],
output_type=dt.double,
determinism=True,
)
def my_add(a, b):
return a + b

Expand Down
9 changes: 3 additions & 6 deletions ibis/backends/bigquery/tests/unit/test_datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@

import ibis.expr.datatypes as dt
from ibis.backends.bigquery.datatypes import (
TypeTranslationContext,
UDFContext,
UDF_CONTEXT,
ibis_type_to_bigquery_type,
)

Expand Down Expand Up @@ -54,8 +53,7 @@ def test_no_ambiguities():
],
)
def test_simple(datatype, expected):
context = TypeTranslationContext()
assert ibis_type_to_bigquery_type(datatype, context) == expected
assert ibis_type_to_bigquery_type(datatype) == expected


@pytest.mark.parametrize("datatype", [dt.uint64, dt.Decimal(8, 3)])
Expand All @@ -81,5 +79,4 @@ def test_simple_failure_mode(datatype):
],
)
def test_ibis_type_to_bigquery_type_udf(type, expected):
context = UDFContext()
assert ibis_type_to_bigquery_type(type, context) == expected
assert ibis_type_to_bigquery_type(type, UDF_CONTEXT) == expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
CREATE TEMPORARY FUNCTION my_len_0(s STRING)
RETURNS FLOAT64
LANGUAGE js AS """
'use strict';
function my_len(s) {
return s.length;
}
return my_len(s);
""";

CREATE TEMPORARY FUNCTION my_len_1(s STRING)
RETURNS FLOAT64
LANGUAGE js AS """
'use strict';
function my_len(s) {
return (s.length + 1);
}
return my_len(s);
""";

SELECT (my_len_0('abcd') + my_len_0('abcd')) + my_len_1('abcd') AS `tmp`
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
CREATE TEMPORARY FUNCTION my_len_0(s STRING)
RETURNS FLOAT64
NOT DETERMINISTIC
LANGUAGE js AS """
'use strict';
function my_len(s) {
return s.length;
}
return my_len(s);
""";

SELECT my_len_0('abcd') AS `tmp`
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CREATE TEMPORARY FUNCTION my_len_0(s STRING)
RETURNS FLOAT64
LANGUAGE js AS """
'use strict';
function my_len(s) {
return s.length;
}
return my_len(s);
""";

SELECT my_len_0('abcd') AS `tmp`
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
CREATE TEMPORARY FUNCTION my_len_0(s STRING)
RETURNS FLOAT64
DETERMINISTIC
LANGUAGE js AS """
'use strict';
function my_len(s) {
return s.length;
}
return my_len(s);
""";

SELECT my_len_0('abcd') AS `tmp`
53 changes: 28 additions & 25 deletions ibis/backends/bigquery/tests/unit/udf/test_usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,49 @@
import ibis
import ibis.expr.datatypes as dt
from ibis.backends.bigquery import udf
from ibis.backends.bigquery.udf import _udf_name_cache


def test_multiple_calls_redefinition():
@udf([dt.string], dt.double)
def test_multiple_calls_redefinition(snapshot):
_udf_name_cache.clear()

@udf.python([dt.string], dt.double)
def my_len(s):
return s.length

s = ibis.literal("abcd")
expr = my_len(s) + my_len(s)

@udf([dt.string], dt.double)
@udf.python([dt.string], dt.double)
def my_len(s):
return s.length + 1

expr = expr + my_len(s)

sql = ibis.bigquery.compile(expr)
expected = '''\
CREATE TEMPORARY FUNCTION my_len_0(s STRING)
RETURNS FLOAT64
LANGUAGE js AS """
'use strict';
function my_len(s) {
return s.length;
}
return my_len(s);
""";
snapshot.assert_match(sql, "out.sql")


@pytest.mark.parametrize(
("determinism",),
[
param(True),
param(False),
param(None),
],
)
def test_udf_determinism(snapshot, determinism):
_udf_name_cache.clear()

@udf.python([dt.string], dt.double, determinism=determinism)
def my_len(s):
return s.length

CREATE TEMPORARY FUNCTION my_len_1(s STRING)
RETURNS FLOAT64
LANGUAGE js AS """
'use strict';
function my_len(s) {
return (s.length + 1);
}
return my_len(s);
""";
s = ibis.literal("abcd")
expr = my_len(s)

SELECT (my_len_0('abcd') + my_len_0('abcd')) + my_len_1('abcd') AS `tmp`'''
assert sql == expected
sql = ibis.bigquery.compile(expr)
snapshot.assert_match(sql, "out.sql")


@pytest.mark.parametrize(
Expand Down Expand Up @@ -93,6 +96,6 @@ def my_len(s):
)
def test_udf_int64(argument_type, return_type):
# invalid argument type, valid return type
@udf([argument_type], return_type)
@udf.python([argument_type], return_type)
def my_int64_add(x):
return 1.0
Loading

0 comments on commit e74328b

Please sign in to comment.