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

feat: add determinism level for javascript UDFs #522

Merged
merged 2 commits into from Feb 17, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/reference.rst
Expand Up @@ -110,6 +110,7 @@ Routine
.. autosummary::
:toctree: generated

routine.DeterminismLevel
routine.Routine
routine.RoutineArgument
routine.RoutineReference
Expand Down
2 changes: 2 additions & 0 deletions google/cloud/bigquery/__init__.py
Expand Up @@ -70,6 +70,7 @@
from google.cloud.bigquery.query import StructQueryParameter
from google.cloud.bigquery.query import UDFResource
from google.cloud.bigquery.retry import DEFAULT_RETRY
from google.cloud.bigquery.routine import DeterminismLevel
from google.cloud.bigquery.routine import Routine
from google.cloud.bigquery.routine import RoutineArgument
from google.cloud.bigquery.routine import RoutineReference
Expand Down Expand Up @@ -134,6 +135,7 @@
"Compression",
"CreateDisposition",
"DestinationFormat",
"DeterminismLevel",
"ExternalSourceFormat",
"Encoding",
"QueryPriority",
Expand Down
17 changes: 17 additions & 0 deletions google/cloud/bigquery/enums.py
Expand Up @@ -231,3 +231,20 @@ class WriteDisposition(object):
WRITE_EMPTY = "WRITE_EMPTY"
"""If the table already exists and contains data, a 'duplicate' error is
returned in the job result."""


class DeterminismLevel:
"""Specifies determinism level for JavaScript user-defined functions (UDFs).

https://cloud.google.com/bigquery/docs/reference/rest/v2/routines#DeterminismLevel
"""

DETERMINISM_LEVEL_UNSPECIFIED = "DETERMINISM_LEVEL_UNSPECIFIED"
"""The determinism of the UDF is unspecified."""

DETERMINISTIC = "DETERMINISTIC"
"""The UDF is deterministic, meaning that 2 function calls with the same inputs
always produce the same result, even across 2 query runs."""

NOT_DETERMINISTIC = "NOT_DETERMINISTIC"
"""The UDF is not deterministic."""
29 changes: 29 additions & 0 deletions google/cloud/bigquery/routine/__init__.py
@@ -0,0 +1,29 @@
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""User-Defined Routines."""


from google.cloud.bigquery.enums import DeterminismLevel
from google.cloud.bigquery.routine.routine import Routine
from google.cloud.bigquery.routine.routine import RoutineArgument
from google.cloud.bigquery.routine.routine import RoutineReference


__all__ = (
"DeterminismLevel",
"Routine",
"RoutineArgument",
"RoutineReference",
)
Expand Up @@ -50,6 +50,7 @@ class Routine(object):
"return_type": "returnType",
"type_": "routineType",
"description": "description",
"determinism_level": "determinismLevel",
}

def __init__(self, routine_ref, **kwargs):
Expand Down Expand Up @@ -253,6 +254,17 @@ def description(self):
def description(self, value):
self._properties[self._PROPERTY_TO_API_FIELD["description"]] = value

@property
def determinism_level(self):
"""Optional[str]: (experimental) The determinism level of the JavaScript UDF
if defined.
"""
return self._properties.get(self._PROPERTY_TO_API_FIELD["determinism_level"])

@determinism_level.setter
def determinism_level(self, value):
self._properties[self._PROPERTY_TO_API_FIELD["determinism_level"]] = value

@classmethod
def from_api_repr(cls, resource):
"""Factory: construct a routine given its API representation.
Expand Down
1 change: 1 addition & 0 deletions tests/system/test_client.py
Expand Up @@ -2682,6 +2682,7 @@ def test_create_routine(self):
)
]
routine.body = "return maxValue(arr)"
routine.determinism_level = bigquery.DeterminismLevel.DETERMINISTIC
query_string = "SELECT `{}`([-100.0, 3.14, 100.0, 42.0]) as max_value;".format(
str(routine.reference)
)
Expand Down
41 changes: 40 additions & 1 deletion tests/unit/routine/test_routine.py
Expand Up @@ -18,6 +18,7 @@
import pytest

import google.cloud._helpers
from google.cloud import bigquery
from google.cloud import bigquery_v2


Expand Down Expand Up @@ -73,6 +74,7 @@ def test_ctor_w_properties(target_class):
)
type_ = "SCALAR_FUNCTION"
description = "A routine description."
determinism_level = bigquery.DeterminismLevel.NOT_DETERMINISTIC

actual_routine = target_class(
routine_id,
Expand All @@ -82,6 +84,7 @@ def test_ctor_w_properties(target_class):
return_type=return_type,
type_=type_,
description=description,
determinism_level=determinism_level,
)

ref = RoutineReference.from_string(routine_id)
Expand All @@ -92,6 +95,9 @@ def test_ctor_w_properties(target_class):
assert actual_routine.return_type == return_type
assert actual_routine.type_ == type_
assert actual_routine.description == description
assert (
actual_routine.determinism_level == bigquery.DeterminismLevel.NOT_DETERMINISTIC
)


def test_from_api_repr(target_class):
Expand Down Expand Up @@ -120,6 +126,7 @@ def test_from_api_repr(target_class):
"routineType": "SCALAR_FUNCTION",
"someNewField": "someValue",
"description": "A routine description.",
"determinismLevel": bigquery.DeterminismLevel.DETERMINISTIC,
}
actual_routine = target_class.from_api_repr(resource)

Expand Down Expand Up @@ -152,6 +159,7 @@ def test_from_api_repr(target_class):
assert actual_routine.type_ == "SCALAR_FUNCTION"
assert actual_routine._properties["someNewField"] == "someValue"
assert actual_routine.description == "A routine description."
assert actual_routine.determinism_level == "DETERMINISTIC"


def test_from_api_repr_w_minimal_resource(target_class):
Expand All @@ -177,6 +185,7 @@ def test_from_api_repr_w_minimal_resource(target_class):
assert actual_routine.return_type is None
assert actual_routine.type_ is None
assert actual_routine.description is None
assert actual_routine.determinism_level is None


def test_from_api_repr_w_unknown_fields(target_class):
Expand Down Expand Up @@ -208,6 +217,7 @@ def test_from_api_repr_w_unknown_fields(target_class):
"returnType": {"typeKind": "INT64"},
"routineType": "SCALAR_FUNCTION",
"description": "A routine description.",
"determinismLevel": bigquery.DeterminismLevel.DETERMINISM_LEVEL_UNSPECIFIED,
},
["arguments"],
{"arguments": [{"name": "x", "dataType": {"typeKind": "INT64"}}]},
Expand All @@ -220,6 +230,7 @@ def test_from_api_repr_w_unknown_fields(target_class):
"returnType": {"typeKind": "INT64"},
"routineType": "SCALAR_FUNCTION",
"description": "A routine description.",
"determinismLevel": bigquery.DeterminismLevel.DETERMINISM_LEVEL_UNSPECIFIED,
},
["body"],
{"definitionBody": "x * 3"},
Expand All @@ -232,6 +243,7 @@ def test_from_api_repr_w_unknown_fields(target_class):
"returnType": {"typeKind": "INT64"},
"routineType": "SCALAR_FUNCTION",
"description": "A routine description.",
"determinismLevel": bigquery.DeterminismLevel.DETERMINISM_LEVEL_UNSPECIFIED,
},
["language"],
{"language": "SQL"},
Expand All @@ -244,6 +256,7 @@ def test_from_api_repr_w_unknown_fields(target_class):
"returnType": {"typeKind": "INT64"},
"routineType": "SCALAR_FUNCTION",
"description": "A routine description.",
"determinismLevel": bigquery.DeterminismLevel.DETERMINISM_LEVEL_UNSPECIFIED,
},
["return_type"],
{"returnType": {"typeKind": "INT64"}},
Expand All @@ -256,6 +269,7 @@ def test_from_api_repr_w_unknown_fields(target_class):
"returnType": {"typeKind": "INT64"},
"routineType": "SCALAR_FUNCTION",
"description": "A routine description.",
"determinismLevel": bigquery.DeterminismLevel.DETERMINISM_LEVEL_UNSPECIFIED,
},
["type_"],
{"routineType": "SCALAR_FUNCTION"},
Expand All @@ -268,20 +282,45 @@ def test_from_api_repr_w_unknown_fields(target_class):
"returnType": {"typeKind": "INT64"},
"routineType": "SCALAR_FUNCTION",
"description": "A routine description.",
"determinismLevel": bigquery.DeterminismLevel.DETERMINISM_LEVEL_UNSPECIFIED,
},
["description"],
{"description": "A routine description."},
),
(
{
"arguments": [{"name": "x", "dataType": {"typeKind": "INT64"}}],
"definitionBody": "x * 3",
"language": "SQL",
"returnType": {"typeKind": "INT64"},
"routineType": "SCALAR_FUNCTION",
"description": "A routine description.",
"determinismLevel": bigquery.DeterminismLevel.DETERMINISM_LEVEL_UNSPECIFIED,
},
["determinism_level"],
{
"determinismLevel": bigquery.DeterminismLevel.DETERMINISM_LEVEL_UNSPECIFIED
},
),
(
{},
["arguments", "language", "body", "type_", "return_type", "description"],
[
"arguments",
"language",
"body",
"type_",
"return_type",
"description",
"determinism_level",
],
{
"arguments": None,
"definitionBody": None,
"language": None,
"returnType": None,
"routineType": None,
"description": None,
"determinismLevel": None,
},
),
(
Expand Down