From a1b50f3cffa80177f0d0600df74cff4227aa9ea7 Mon Sep 17 00:00:00 2001 From: Peter Lamut Date: Thu, 24 Jun 2021 14:34:25 +0200 Subject: [PATCH 1/6] Add auxiliary classes for TVF routines --- google/cloud/bigquery/__init__.py | 4 ++ google/cloud/bigquery/routine/__init__.py | 4 ++ google/cloud/bigquery/routine/routine.py | 39 +++++++++++ .../routine/test_standard_sql_table_type.py | 67 +++++++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 tests/unit/routine/test_standard_sql_table_type.py diff --git a/google/cloud/bigquery/__init__.py b/google/cloud/bigquery/__init__.py index 94f87304a..0332ad384 100644 --- a/google/cloud/bigquery/__init__.py +++ b/google/cloud/bigquery/__init__.py @@ -81,6 +81,8 @@ from google.cloud.bigquery.routine import Routine from google.cloud.bigquery.routine import RoutineArgument from google.cloud.bigquery.routine import RoutineReference +from google.cloud.bigquery.routine import RoutineType +from google.cloud.bigquery.routine import StandardSQLTableType from google.cloud.bigquery.schema import SchemaField from google.cloud.bigquery.table import PartitionRange from google.cloud.bigquery.table import RangePartitioning @@ -130,6 +132,7 @@ "Routine", "RoutineArgument", "RoutineReference", + "StandardSQLTableType", # Shared helpers "SchemaField", "UDFResource", @@ -152,6 +155,7 @@ "Encoding", "KeyResultStatementKind", "QueryPriority", + "RoutineType", "SchemaUpdateOption", "SourceFormat", "SqlTypeNames", diff --git a/google/cloud/bigquery/routine/__init__.py b/google/cloud/bigquery/routine/__init__.py index d1c79b05e..9c3ce3e34 100644 --- a/google/cloud/bigquery/routine/__init__.py +++ b/google/cloud/bigquery/routine/__init__.py @@ -19,6 +19,8 @@ from google.cloud.bigquery.routine.routine import Routine from google.cloud.bigquery.routine.routine import RoutineArgument from google.cloud.bigquery.routine.routine import RoutineReference +from google.cloud.bigquery.routine.routine import RoutineType +from google.cloud.bigquery.routine.routine import StandardSQLTableType __all__ = ( @@ -26,4 +28,6 @@ "Routine", "RoutineArgument", "RoutineReference", + "RoutineType", + "StandardSQLTableType", ) diff --git a/google/cloud/bigquery/routine/routine.py b/google/cloud/bigquery/routine/routine.py index bbc0a7693..1f89ae35f 100644 --- a/google/cloud/bigquery/routine/routine.py +++ b/google/cloud/bigquery/routine/routine.py @@ -16,11 +16,50 @@ """Define resources for the BigQuery Routines API.""" +from typing import Any, Iterable, List + from google.protobuf import json_format import google.cloud._helpers from google.cloud.bigquery import _helpers import google.cloud.bigquery_v2.types +from google.cloud.bigquery_v2.types import StandardSqlField + + +# Unlike related proto-plus classes from `bigquery_v2.types.standard_sql`, this class +# is not auto-generated (tech debt), thus we need to define it manually. +class StandardSQLTableType: + """Representation of a return type from a Table Valued Functions (TFV).""" + + def __init__(self, columns: Iterable[StandardSqlField]): + self.columns = columns + + @property + def columns(self) -> List[StandardSqlField]: + return list(self._columns) # shallow copy + + @columns.setter + def columns(self, value: Iterable[StandardSqlField]): + self._columns = list(value) + + def __eq__(self, other: Any): + if not isinstance(other, StandardSQLTableType): + return NotImplemented + return self.columns == other.columns + + __hash__ = None # Python does this implicitly for us, but let's be explicit. + + +class RoutineType: + """The fine-grained type of the routine. + + https://cloud.google.com/bigquery/docs/reference/rest/v2/routines#routinetype + """ + + ROUTINE_TYPE_UNSPECIFIED = "ROUTINE_TYPE_UNSPECIFIED" + SCALAR_FUNCTION = "SCALAR_FUNCTION" + PROCEDURE = "PROCEDURE" + TABLE_VALUED_FUNCTION = "TABLE_VALUED_FUNCTION" class Routine(object): diff --git a/tests/unit/routine/test_standard_sql_table_type.py b/tests/unit/routine/test_standard_sql_table_type.py new file mode 100644 index 000000000..b0b57d038 --- /dev/null +++ b/tests/unit/routine/test_standard_sql_table_type.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +# +# 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 +# +# https://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. + +import pytest + +from google.cloud.bigquery_v2.types import StandardSqlDataType +from google.cloud.bigquery_v2.types import StandardSqlField + + +@pytest.fixture +def target_class(): + from google.cloud.bigquery.routine import StandardSQLTableType + + return StandardSQLTableType + + +@pytest.fixture +def table_type_columns(): + columns = [ + StandardSqlField( + name="int_col", + type=StandardSqlDataType(type_kind=StandardSqlDataType.TypeKind.INT64), + ), + StandardSqlField( + name="str_col", + type=StandardSqlDataType(type_kind=StandardSqlDataType.TypeKind.STRING), + ), + ] + return columns + + +def test_ctor(target_class, table_type_columns): + result = target_class(columns=table_type_columns) + + assert result.columns == table_type_columns + del table_type_columns[0] + assert len(result.columns) == 2, "Instance should store a shallow copy of columns." + + +def test_eq_hit(target_class, table_type_columns): + table_type = target_class(columns=table_type_columns) + table_type_2 = target_class(columns=table_type_columns) + assert table_type == table_type_2 + + +def test_eq_miss_different_columns(target_class, table_type_columns): + table_type = target_class(columns=table_type_columns[:1]) + table_type_2 = target_class(columns=table_type_columns[1:]) + assert table_type != table_type_2 + + +def test_eq_miss_different_type(target_class, table_type_columns): + table_type = target_class(columns=table_type_columns) + assert table_type != object() From 9be50c4b604eb55bf36cbedf4956b9ee4756f957 Mon Sep 17 00:00:00 2001 From: Peter Lamut Date: Mon, 28 Jun 2021 15:27:54 +0200 Subject: [PATCH 2/6] Add return_table_type property to Routine --- google/cloud/bigquery/routine/routine.py | 30 ++++++ tests/unit/routine/test_routine.py | 128 +++++++++++++++++++++++ 2 files changed, 158 insertions(+) diff --git a/google/cloud/bigquery/routine/routine.py b/google/cloud/bigquery/routine/routine.py index 1f89ae35f..7be1b1f9b 100644 --- a/google/cloud/bigquery/routine/routine.py +++ b/google/cloud/bigquery/routine/routine.py @@ -87,6 +87,7 @@ class Routine(object): "modified": "lastModifiedTime", "reference": "routineReference", "return_type": "returnType", + "return_table_type": "returnTableType", "type_": "routineType", "description": "description", "determinism_level": "determinismLevel", @@ -243,6 +244,35 @@ def return_type(self, value): resource = None self._properties[self._PROPERTY_TO_API_FIELD["return_type"]] = resource + @property + def return_table_type(self) -> StandardSQLTableType: + resource = self._properties.get( + self._PROPERTY_TO_API_FIELD["return_table_type"] + ) + if not resource: + return resource + + table_columns = ( + StandardSqlField.wrap( + json_format.ParseDict( + column_json, StandardSqlField()._pb, ignore_unknown_fields=True + ) + ) + for column_json in resource["columns"] + ) + return StandardSQLTableType(columns=table_columns) + + @return_table_type.setter + def return_table_type(self, value): + if not value: + resource = None + else: + resource = { + "columns": [json_format.MessageToDict(col._pb) for col in value.columns] + } + + self._properties[self._PROPERTY_TO_API_FIELD["return_table_type"]] = resource + @property def imported_libraries(self): """List[str]: The path of the imported JavaScript libraries. diff --git a/tests/unit/routine/test_routine.py b/tests/unit/routine/test_routine.py index 0a59e7c5f..9e230bada 100644 --- a/tests/unit/routine/test_routine.py +++ b/tests/unit/routine/test_routine.py @@ -156,12 +156,86 @@ def test_from_api_repr(target_class): assert actual_routine.return_type == bigquery_v2.types.StandardSqlDataType( type_kind=bigquery_v2.types.StandardSqlDataType.TypeKind.INT64 ) + assert actual_routine.return_table_type is None 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_tvf_function(target_class): + from google.cloud.bigquery.routine import RoutineArgument + from google.cloud.bigquery.routine import RoutineReference + from google.cloud.bigquery.routine import RoutineType + from google.cloud.bigquery.routine import StandardSQLTableType + + StandardSqlDataType = bigquery_v2.types.StandardSqlDataType + StandardSqlField = bigquery_v2.types.StandardSqlField + + creation_time = datetime.datetime( + 2010, 5, 19, 16, 0, 0, tzinfo=google.cloud._helpers.UTC + ) + modified_time = datetime.datetime( + 2011, 10, 1, 16, 0, 0, tzinfo=google.cloud._helpers.UTC + ) + resource = { + "routineReference": { + "projectId": "my-project", + "datasetId": "my_dataset", + "routineId": "my_routine", + }, + "etag": "abcdefg", + "creationTime": str(google.cloud._helpers._millis(creation_time)), + "lastModifiedTime": str(google.cloud._helpers._millis(modified_time)), + "definitionBody": "SELECT x FROM UNNEST([1,2,3]) x WHERE x > a", + "arguments": [{"name": "a", "dataType": {"typeKind": "INT64"}}], + "language": "SQL", + "returnTableType": { + "columns": [{"name": "int_col", "type": {"typeKind": "INT64"}}] + }, + "routineType": "TABLE_VALUED_FUNCTION", + "someNewField": "someValue", + "description": "A routine description.", + "determinismLevel": bigquery.DeterminismLevel.DETERMINISTIC, + } + actual_routine = target_class.from_api_repr(resource) + + assert actual_routine.project == "my-project" + assert actual_routine.dataset_id == "my_dataset" + assert actual_routine.routine_id == "my_routine" + assert ( + actual_routine.path + == "/projects/my-project/datasets/my_dataset/routines/my_routine" + ) + assert actual_routine.reference == RoutineReference.from_string( + "my-project.my_dataset.my_routine" + ) + assert actual_routine.etag == "abcdefg" + assert actual_routine.created == creation_time + assert actual_routine.modified == modified_time + assert actual_routine.arguments == [ + RoutineArgument( + name="a", + data_type=StandardSqlDataType(type_kind=StandardSqlDataType.TypeKind.INT64), + ) + ] + assert actual_routine.body == "SELECT x FROM UNNEST([1,2,3]) x WHERE x > a" + assert actual_routine.language == "SQL" + assert actual_routine.return_type is None + assert actual_routine.return_table_type == StandardSQLTableType( + columns=[ + StandardSqlField( + name="int_col", + type=StandardSqlDataType(type_kind=StandardSqlDataType.TypeKind.INT64), + ) + ] + ) + assert actual_routine.type_ == RoutineType.TABLE_VALUED_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): from google.cloud.bigquery.routine import RoutineReference @@ -261,6 +335,24 @@ def test_from_api_repr_w_unknown_fields(target_class): ["return_type"], {"returnType": {"typeKind": "INT64"}}, ), + ( + { + "definitionBody": "SELECT x FROM UNNEST([1,2,3]) x WHERE x > 1", + "language": "SQL", + "returnTableType": { + "columns": [{"name": "int_col", "type": {"typeKind": "INT64"}}] + }, + "routineType": "TABLE_VALUED_FUNCTION", + "description": "A routine description.", + "determinismLevel": bigquery.DeterminismLevel.DETERMINISM_LEVEL_UNSPECIFIED, + }, + ["return_table_type"], + { + "returnTableType": { + "columns": [{"name": "int_col", "type": {"typeKind": "INT64"}}] + } + }, + ), ( { "arguments": [{"name": "x", "dataType": {"typeKind": "INT64"}}], @@ -361,6 +453,42 @@ def test_set_return_type_w_none(object_under_test): assert object_under_test._properties["returnType"] is None +def test_set_return_table_type_w_none(object_under_test): + object_under_test.return_table_type = None + assert object_under_test.return_table_type is None + assert object_under_test._properties["returnTableType"] is None + + +def test_set_return_table_type_w_not_none(object_under_test): + from google.cloud.bigquery.routine import StandardSQLTableType + + StandardSqlDataType = bigquery_v2.types.StandardSqlDataType + StandardSqlField = bigquery_v2.types.StandardSqlField + + table_type = StandardSQLTableType( + columns=[ + StandardSqlField( + name="int_col", + type=StandardSqlDataType(type_kind=StandardSqlDataType.TypeKind.INT64), + ), + StandardSqlField( + name="str_col", + type=StandardSqlDataType(type_kind=StandardSqlDataType.TypeKind.STRING), + ), + ] + ) + + object_under_test.return_table_type = table_type + + assert object_under_test.return_table_type == table_type + assert object_under_test._properties["returnTableType"] == { + "columns": [ + {"name": "int_col", "type": {"typeKind": "INT64"}}, + {"name": "str_col", "type": {"typeKind": "STRING"}}, + ] + } + + def test_set_description_w_none(object_under_test): object_under_test.description = None assert object_under_test.description is None From 590af76b1bd7cff7d8172a6139dfb2fc35d9fa43 Mon Sep 17 00:00:00 2001 From: Peter Lamut Date: Mon, 28 Jun 2021 16:25:47 +0200 Subject: [PATCH 3/6] Add system test for TVF routines --- tests/system/test_client.py | 79 +++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/tests/system/test_client.py b/tests/system/test_client.py index ce3021399..45b4f37b4 100644 --- a/tests/system/test_client.py +++ b/tests/system/test_client.py @@ -2118,6 +2118,85 @@ def test_create_routine(self): assert len(rows) == 1 assert rows[0].max_value == 100.0 + def test_create_tvf_routine(self): + from google.cloud.bigquery import Routine, RoutineArgument, RoutineType + from google.cloud.bigquery import StandardSQLTableType + + StandardSqlDataType = bigquery_v2.types.StandardSqlDataType + StandardSqlField = bigquery_v2.types.StandardSqlField + + INT64 = StandardSqlDataType.TypeKind.INT64 + STRING = StandardSqlDataType.TypeKind.STRING + + client = Config.CLIENT + + dataset = self.temp_dataset(_make_dataset_id("create_tvf_routine")) + routine_ref = dataset.routine("test_tvf_routine") + + routine_body = """ + SELECT int_col, str_col + FROM ( + UNNEST([1, 2, 3]) int_col + JOIN + (SELECT str_col FROM UNNEST(["one", "two", "three"]) str_col) + ON TRUE + ) + WHERE int_col > threshold + """ + + return_table_type = StandardSQLTableType( + columns=[ + StandardSqlField( + name="int_col", type=StandardSqlDataType(type_kind=INT64), + ), + StandardSqlField( + name="str_col", type=StandardSqlDataType(type_kind=STRING), + ), + ] + ) + + routine_args = [ + RoutineArgument( + name="threshold", data_type=StandardSqlDataType(type_kind=INT64), + ) + ] + + routine_def = Routine( + routine_ref, + type_=RoutineType.TABLE_VALUED_FUNCTION, + arguments=routine_args, + return_table_type=return_table_type, + body=routine_body, + ) + + # Create TVF routine. + client.delete_routine(routine_ref, not_found_ok=True) + routine = client.create_routine(routine_def) + + assert routine.body == routine_body + assert routine.return_table_type == return_table_type + assert routine.arguments == routine_args + + # Execute the routine to see if it's working as expected. + query_job = client.query( + f""" + SELECT int_col, str_col + FROM `{routine.reference}`(1) + ORDER BY int_col, str_col ASC + """ + ) + + result_rows = [tuple(row) for row in query_job.result()] + expected = [ + (2, "one"), + (2, "three"), + (2, "two"), + (3, "one"), + (3, "three"), + (3, "two"), + ] + assert result_rows == expected + def test_create_table_rows_fetch_nested_schema(self): table_name = "test_table" dataset = self.temp_dataset(_make_dataset_id("create_table_nested_schema")) From 9aed27986d74630e59406c0d005ddc7125f246b1 Mon Sep 17 00:00:00 2001 From: Peter Lamut Date: Mon, 19 Jul 2021 14:10:40 +0200 Subject: [PATCH 4/6] Use the generated StandardSqlTableType class --- google/cloud/bigquery/__init__.py | 2 - google/cloud/bigquery/routine/__init__.py | 2 - google/cloud/bigquery/routine/routine.py | 42 ++---------- tests/system/test_client.py | 4 +- tests/unit/routine/test_routine.py | 9 ++- .../routine/test_standard_sql_table_type.py | 67 ------------------- 6 files changed, 12 insertions(+), 114 deletions(-) delete mode 100644 tests/unit/routine/test_standard_sql_table_type.py diff --git a/google/cloud/bigquery/__init__.py b/google/cloud/bigquery/__init__.py index 5d0b72c75..222aadcc9 100644 --- a/google/cloud/bigquery/__init__.py +++ b/google/cloud/bigquery/__init__.py @@ -86,7 +86,6 @@ from google.cloud.bigquery.routine import RoutineArgument from google.cloud.bigquery.routine import RoutineReference from google.cloud.bigquery.routine import RoutineType -from google.cloud.bigquery.routine import StandardSQLTableType from google.cloud.bigquery.schema import SchemaField from google.cloud.bigquery.table import PartitionRange from google.cloud.bigquery.table import RangePartitioning @@ -138,7 +137,6 @@ "Routine", "RoutineArgument", "RoutineReference", - "StandardSQLTableType", # Shared helpers "SchemaField", "UDFResource", diff --git a/google/cloud/bigquery/routine/__init__.py b/google/cloud/bigquery/routine/__init__.py index 9c3ce3e34..7353073c8 100644 --- a/google/cloud/bigquery/routine/__init__.py +++ b/google/cloud/bigquery/routine/__init__.py @@ -20,7 +20,6 @@ from google.cloud.bigquery.routine.routine import RoutineArgument from google.cloud.bigquery.routine.routine import RoutineReference from google.cloud.bigquery.routine.routine import RoutineType -from google.cloud.bigquery.routine.routine import StandardSQLTableType __all__ = ( @@ -29,5 +28,4 @@ "RoutineArgument", "RoutineReference", "RoutineType", - "StandardSQLTableType", ) diff --git a/google/cloud/bigquery/routine/routine.py b/google/cloud/bigquery/routine/routine.py index 7be1b1f9b..19e7b6d48 100644 --- a/google/cloud/bigquery/routine/routine.py +++ b/google/cloud/bigquery/routine/routine.py @@ -16,38 +16,12 @@ """Define resources for the BigQuery Routines API.""" -from typing import Any, Iterable, List - from google.protobuf import json_format import google.cloud._helpers from google.cloud.bigquery import _helpers import google.cloud.bigquery_v2.types -from google.cloud.bigquery_v2.types import StandardSqlField - - -# Unlike related proto-plus classes from `bigquery_v2.types.standard_sql`, this class -# is not auto-generated (tech debt), thus we need to define it manually. -class StandardSQLTableType: - """Representation of a return type from a Table Valued Functions (TFV).""" - - def __init__(self, columns: Iterable[StandardSqlField]): - self.columns = columns - - @property - def columns(self) -> List[StandardSqlField]: - return list(self._columns) # shallow copy - - @columns.setter - def columns(self, value: Iterable[StandardSqlField]): - self._columns = list(value) - - def __eq__(self, other: Any): - if not isinstance(other, StandardSQLTableType): - return NotImplemented - return self.columns == other.columns - - __hash__ = None # Python does this implicitly for us, but let's be explicit. +from google.cloud.bigquery_v2.types import StandardSqlTableType class RoutineType: @@ -245,22 +219,18 @@ def return_type(self, value): self._properties[self._PROPERTY_TO_API_FIELD["return_type"]] = resource @property - def return_table_type(self) -> StandardSQLTableType: + def return_table_type(self) -> StandardSqlTableType: resource = self._properties.get( self._PROPERTY_TO_API_FIELD["return_table_type"] ) if not resource: return resource - table_columns = ( - StandardSqlField.wrap( - json_format.ParseDict( - column_json, StandardSqlField()._pb, ignore_unknown_fields=True - ) - ) - for column_json in resource["columns"] + output = google.cloud.bigquery_v2.types.StandardSqlTableType() + raw_protobuf = json_format.ParseDict( + resource, output._pb, ignore_unknown_fields=True ) - return StandardSQLTableType(columns=table_columns) + return type(output).wrap(raw_protobuf) @return_table_type.setter def return_table_type(self, value): diff --git a/tests/system/test_client.py b/tests/system/test_client.py index c3c3de640..ceb62b8cd 100644 --- a/tests/system/test_client.py +++ b/tests/system/test_client.py @@ -2230,10 +2230,10 @@ def test_create_routine(self): def test_create_tvf_routine(self): from google.cloud.bigquery import Routine, RoutineArgument, RoutineType - from google.cloud.bigquery import StandardSQLTableType StandardSqlDataType = bigquery_v2.types.StandardSqlDataType StandardSqlField = bigquery_v2.types.StandardSqlField + StandardSqlTableType = bigquery_v2.types.StandardSqlTableType INT64 = StandardSqlDataType.TypeKind.INT64 STRING = StandardSqlDataType.TypeKind.STRING @@ -2254,7 +2254,7 @@ def test_create_tvf_routine(self): WHERE int_col > threshold """ - return_table_type = StandardSQLTableType( + return_table_type = StandardSqlTableType( columns=[ StandardSqlField( name="int_col", type=StandardSqlDataType(type_kind=INT64), diff --git a/tests/unit/routine/test_routine.py b/tests/unit/routine/test_routine.py index 9e230bada..fdaf13324 100644 --- a/tests/unit/routine/test_routine.py +++ b/tests/unit/routine/test_routine.py @@ -167,10 +167,10 @@ def test_from_api_repr_tvf_function(target_class): from google.cloud.bigquery.routine import RoutineArgument from google.cloud.bigquery.routine import RoutineReference from google.cloud.bigquery.routine import RoutineType - from google.cloud.bigquery.routine import StandardSQLTableType StandardSqlDataType = bigquery_v2.types.StandardSqlDataType StandardSqlField = bigquery_v2.types.StandardSqlField + StandardSqlTableType = bigquery_v2.types.StandardSqlTableType creation_time = datetime.datetime( 2010, 5, 19, 16, 0, 0, tzinfo=google.cloud._helpers.UTC @@ -222,7 +222,7 @@ def test_from_api_repr_tvf_function(target_class): assert actual_routine.body == "SELECT x FROM UNNEST([1,2,3]) x WHERE x > a" assert actual_routine.language == "SQL" assert actual_routine.return_type is None - assert actual_routine.return_table_type == StandardSQLTableType( + assert actual_routine.return_table_type == StandardSqlTableType( columns=[ StandardSqlField( name="int_col", @@ -460,12 +460,11 @@ def test_set_return_table_type_w_none(object_under_test): def test_set_return_table_type_w_not_none(object_under_test): - from google.cloud.bigquery.routine import StandardSQLTableType - StandardSqlDataType = bigquery_v2.types.StandardSqlDataType StandardSqlField = bigquery_v2.types.StandardSqlField + StandardSqlTableType = bigquery_v2.types.StandardSqlTableType - table_type = StandardSQLTableType( + table_type = StandardSqlTableType( columns=[ StandardSqlField( name="int_col", diff --git a/tests/unit/routine/test_standard_sql_table_type.py b/tests/unit/routine/test_standard_sql_table_type.py deleted file mode 100644 index b0b57d038..000000000 --- a/tests/unit/routine/test_standard_sql_table_type.py +++ /dev/null @@ -1,67 +0,0 @@ -# -*- coding: utf-8 -*- -# -# 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 -# -# https://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. - -import pytest - -from google.cloud.bigquery_v2.types import StandardSqlDataType -from google.cloud.bigquery_v2.types import StandardSqlField - - -@pytest.fixture -def target_class(): - from google.cloud.bigquery.routine import StandardSQLTableType - - return StandardSQLTableType - - -@pytest.fixture -def table_type_columns(): - columns = [ - StandardSqlField( - name="int_col", - type=StandardSqlDataType(type_kind=StandardSqlDataType.TypeKind.INT64), - ), - StandardSqlField( - name="str_col", - type=StandardSqlDataType(type_kind=StandardSqlDataType.TypeKind.STRING), - ), - ] - return columns - - -def test_ctor(target_class, table_type_columns): - result = target_class(columns=table_type_columns) - - assert result.columns == table_type_columns - del table_type_columns[0] - assert len(result.columns) == 2, "Instance should store a shallow copy of columns." - - -def test_eq_hit(target_class, table_type_columns): - table_type = target_class(columns=table_type_columns) - table_type_2 = target_class(columns=table_type_columns) - assert table_type == table_type_2 - - -def test_eq_miss_different_columns(target_class, table_type_columns): - table_type = target_class(columns=table_type_columns[:1]) - table_type_2 = target_class(columns=table_type_columns[1:]) - assert table_type != table_type_2 - - -def test_eq_miss_different_type(target_class, table_type_columns): - table_type = target_class(columns=table_type_columns) - assert table_type != object() From bc11bc3514bdfc98a9972eb22ec49f23a5d3368a Mon Sep 17 00:00:00 2001 From: Peter Lamut Date: Mon, 19 Jul 2021 14:51:29 +0200 Subject: [PATCH 5/6] Update docs with new changes --- docs/reference.rst | 1 + google/cloud/bigquery/routine/routine.py | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/docs/reference.rst b/docs/reference.rst index 8c38d0c44..8a5bff9a4 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -118,6 +118,7 @@ Routine routine.Routine routine.RoutineArgument routine.RoutineReference + routine.RoutineType Schema ====== diff --git a/google/cloud/bigquery/routine/routine.py b/google/cloud/bigquery/routine/routine.py index 19e7b6d48..a776212c3 100644 --- a/google/cloud/bigquery/routine/routine.py +++ b/google/cloud/bigquery/routine/routine.py @@ -28,6 +28,8 @@ class RoutineType: """The fine-grained type of the routine. https://cloud.google.com/bigquery/docs/reference/rest/v2/routines#routinetype + + .. versionadded:: 2.22.0 """ ROUTINE_TYPE_UNSPECIFIED = "ROUTINE_TYPE_UNSPECIFIED" @@ -220,6 +222,10 @@ def return_type(self, value): @property def return_table_type(self) -> StandardSqlTableType: + """The return type of a Table Valued Function (TVF) routine. + + .. versionadded:: 2.22.0 + """ resource = self._properties.get( self._PROPERTY_TO_API_FIELD["return_table_type"] ) From f064c30fdfb20efa3b8e297e41d1e5bb13d17059 Mon Sep 17 00:00:00 2001 From: Peter Lamut Date: Mon, 19 Jul 2021 14:57:15 +0200 Subject: [PATCH 6/6] Add missing space in misc. Sphinx directives --- google/cloud/bigquery/job/query.py | 14 +++++++------- google/cloud/bigquery/table.py | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/google/cloud/bigquery/job/query.py b/google/cloud/bigquery/job/query.py index d588e9b5a..2cb7ee28e 100644 --- a/google/cloud/bigquery/job/query.py +++ b/google/cloud/bigquery/job/query.py @@ -1386,12 +1386,12 @@ def to_arrow( This argument does nothing if ``bqstorage_client`` is supplied. - ..versionadded:: 1.24.0 + .. versionadded:: 1.24.0 max_results (Optional[int]): Maximum number of rows to include in the result. No limit by default. - ..versionadded:: 2.21.0 + .. versionadded:: 2.21.0 Returns: pyarrow.Table @@ -1403,7 +1403,7 @@ def to_arrow( ValueError: If the :mod:`pyarrow` library cannot be imported. - ..versionadded:: 1.17.0 + .. versionadded:: 1.17.0 """ query_result = wait_for_query(self, progress_bar_type, max_results=max_results) return query_result.to_arrow( @@ -1452,7 +1452,7 @@ def to_dataframe( :func:`~google.cloud.bigquery.table.RowIterator.to_dataframe` for details. - ..versionadded:: 1.11.0 + .. versionadded:: 1.11.0 create_bqstorage_client (Optional[bool]): If ``True`` (default), create a BigQuery Storage API client using the default API settings. The BigQuery Storage API @@ -1461,18 +1461,18 @@ def to_dataframe( This argument does nothing if ``bqstorage_client`` is supplied. - ..versionadded:: 1.24.0 + .. versionadded:: 1.24.0 date_as_object (Optional[bool]): If ``True`` (default), cast dates to objects. If ``False``, convert to datetime64[ns] dtype. - ..versionadded:: 1.26.0 + .. versionadded:: 1.26.0 max_results (Optional[int]): Maximum number of rows to include in the result. No limit by default. - ..versionadded:: 2.21.0 + .. versionadded:: 2.21.0 Returns: A :class:`~pandas.DataFrame` populated with row data and column diff --git a/google/cloud/bigquery/table.py b/google/cloud/bigquery/table.py index 2d9c15f50..18d969a3f 100644 --- a/google/cloud/bigquery/table.py +++ b/google/cloud/bigquery/table.py @@ -1684,7 +1684,7 @@ def to_arrow( This argument does nothing if ``bqstorage_client`` is supplied. - ..versionadded:: 1.24.0 + .. versionadded:: 1.24.0 Returns: pyarrow.Table @@ -1695,7 +1695,7 @@ def to_arrow( Raises: ValueError: If the :mod:`pyarrow` library cannot be imported. - ..versionadded:: 1.17.0 + .. versionadded:: 1.17.0 """ if pyarrow is None: raise ValueError(_NO_PYARROW_ERROR) @@ -1775,7 +1775,7 @@ def to_dataframe_iterable( created by the server. If ``max_queue_size`` is :data:`None`, the queue size is infinite. - ..versionadded:: 2.14.0 + .. versionadded:: 2.14.0 Returns: pandas.DataFrame: @@ -1861,7 +1861,7 @@ def to_dataframe( Use the :func:`tqdm.tqdm_gui` function to display a progress bar as a graphical dialog box. - ..versionadded:: 1.11.0 + .. versionadded:: 1.11.0 create_bqstorage_client (Optional[bool]): If ``True`` (default), create a BigQuery Storage API client using the default API settings. The BigQuery Storage API @@ -1870,13 +1870,13 @@ def to_dataframe( This argument does nothing if ``bqstorage_client`` is supplied. - ..versionadded:: 1.24.0 + .. versionadded:: 1.24.0 date_as_object (Optional[bool]): If ``True`` (default), cast dates to objects. If ``False``, convert to datetime64[ns] dtype. - ..versionadded:: 1.26.0 + .. versionadded:: 1.26.0 Returns: pandas.DataFrame: @@ -2010,7 +2010,7 @@ def to_dataframe_iterable( ) -> Iterator["pandas.DataFrame"]: """Create an iterable of pandas DataFrames, to process the table as a stream. - ..versionadded:: 2.21.0 + .. versionadded:: 2.21.0 Args: bqstorage_client: