Skip to content

Commit e74328b

Browse files
krzysztof-kwittcpcloud
authored andcommitted
feat(bigquery): add JS UDF support
1 parent e8ebf23 commit e74328b

File tree

9 files changed

+331
-181
lines changed

9 files changed

+331
-181
lines changed

ibis/backends/bigquery/datatypes.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ class UDFContext(TypeTranslationContext):
2020
__slots__ = ()
2121

2222

23+
UDF_CONTEXT = UDFContext()
24+
2325
ibis_type_to_bigquery_type = Dispatcher("ibis_type_to_bigquery_type")
2426

2527

ibis/backends/bigquery/tests/system/udf/test_udf_execute.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ def df(alltypes):
2525

2626

2727
def test_udf(alltypes, df):
28-
@udf(input_type=[dt.double, dt.double], output_type=dt.double)
28+
@udf(
29+
input_type=[dt.double, dt.double],
30+
output_type=dt.double,
31+
determinism=True,
32+
)
2933
def my_add(a, b):
3034
return a + b
3135

ibis/backends/bigquery/tests/unit/test_datatypes.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44

55
import ibis.expr.datatypes as dt
66
from ibis.backends.bigquery.datatypes import (
7-
TypeTranslationContext,
8-
UDFContext,
7+
UDF_CONTEXT,
98
ibis_type_to_bigquery_type,
109
)
1110

@@ -54,8 +53,7 @@ def test_no_ambiguities():
5453
],
5554
)
5655
def test_simple(datatype, expected):
57-
context = TypeTranslationContext()
58-
assert ibis_type_to_bigquery_type(datatype, context) == expected
56+
assert ibis_type_to_bigquery_type(datatype) == expected
5957

6058

6159
@pytest.mark.parametrize("datatype", [dt.uint64, dt.Decimal(8, 3)])
@@ -81,5 +79,4 @@ def test_simple_failure_mode(datatype):
8179
],
8280
)
8381
def test_ibis_type_to_bigquery_type_udf(type, expected):
84-
context = UDFContext()
85-
assert ibis_type_to_bigquery_type(type, context) == expected
82+
assert ibis_type_to_bigquery_type(type, UDF_CONTEXT) == expected
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
CREATE TEMPORARY FUNCTION my_len_0(s STRING)
2+
RETURNS FLOAT64
3+
LANGUAGE js AS """
4+
'use strict';
5+
function my_len(s) {
6+
return s.length;
7+
}
8+
return my_len(s);
9+
""";
10+
11+
CREATE TEMPORARY FUNCTION my_len_1(s STRING)
12+
RETURNS FLOAT64
13+
LANGUAGE js AS """
14+
'use strict';
15+
function my_len(s) {
16+
return (s.length + 1);
17+
}
18+
return my_len(s);
19+
""";
20+
21+
SELECT (my_len_0('abcd') + my_len_0('abcd')) + my_len_1('abcd') AS `tmp`
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CREATE TEMPORARY FUNCTION my_len_0(s STRING)
2+
RETURNS FLOAT64
3+
NOT DETERMINISTIC
4+
LANGUAGE js AS """
5+
'use strict';
6+
function my_len(s) {
7+
return s.length;
8+
}
9+
return my_len(s);
10+
""";
11+
12+
SELECT my_len_0('abcd') AS `tmp`
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CREATE TEMPORARY FUNCTION my_len_0(s STRING)
2+
RETURNS FLOAT64
3+
LANGUAGE js AS """
4+
'use strict';
5+
function my_len(s) {
6+
return s.length;
7+
}
8+
return my_len(s);
9+
""";
10+
11+
SELECT my_len_0('abcd') AS `tmp`
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CREATE TEMPORARY FUNCTION my_len_0(s STRING)
2+
RETURNS FLOAT64
3+
DETERMINISTIC
4+
LANGUAGE js AS """
5+
'use strict';
6+
function my_len(s) {
7+
return s.length;
8+
}
9+
return my_len(s);
10+
""";
11+
12+
SELECT my_len_0('abcd') AS `tmp`

ibis/backends/bigquery/tests/unit/udf/test_usage.py

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,46 +4,49 @@
44
import ibis
55
import ibis.expr.datatypes as dt
66
from ibis.backends.bigquery import udf
7+
from ibis.backends.bigquery.udf import _udf_name_cache
78

89

9-
def test_multiple_calls_redefinition():
10-
@udf([dt.string], dt.double)
10+
def test_multiple_calls_redefinition(snapshot):
11+
_udf_name_cache.clear()
12+
13+
@udf.python([dt.string], dt.double)
1114
def my_len(s):
1215
return s.length
1316

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

17-
@udf([dt.string], dt.double)
20+
@udf.python([dt.string], dt.double)
1821
def my_len(s):
1922
return s.length + 1
2023

2124
expr = expr + my_len(s)
2225

2326
sql = ibis.bigquery.compile(expr)
24-
expected = '''\
25-
CREATE TEMPORARY FUNCTION my_len_0(s STRING)
26-
RETURNS FLOAT64
27-
LANGUAGE js AS """
28-
'use strict';
29-
function my_len(s) {
30-
return s.length;
31-
}
32-
return my_len(s);
33-
""";
27+
snapshot.assert_match(sql, "out.sql")
28+
29+
30+
@pytest.mark.parametrize(
31+
("determinism",),
32+
[
33+
param(True),
34+
param(False),
35+
param(None),
36+
],
37+
)
38+
def test_udf_determinism(snapshot, determinism):
39+
_udf_name_cache.clear()
40+
41+
@udf.python([dt.string], dt.double, determinism=determinism)
42+
def my_len(s):
43+
return s.length
3444

35-
CREATE TEMPORARY FUNCTION my_len_1(s STRING)
36-
RETURNS FLOAT64
37-
LANGUAGE js AS """
38-
'use strict';
39-
function my_len(s) {
40-
return (s.length + 1);
41-
}
42-
return my_len(s);
43-
""";
45+
s = ibis.literal("abcd")
46+
expr = my_len(s)
4447

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

4851

4952
@pytest.mark.parametrize(
@@ -93,6 +96,6 @@ def my_len(s):
9396
)
9497
def test_udf_int64(argument_type, return_type):
9598
# invalid argument type, valid return type
96-
@udf([argument_type], return_type)
99+
@udf.python([argument_type], return_type)
97100
def my_int64_add(x):
98101
return 1.0

0 commit comments

Comments
 (0)