From 6f8683894448ca2498c7bcce78da91f770ff2bdc Mon Sep 17 00:00:00 2001 From: chalmer lowe Date: Mon, 27 Jan 2025 17:11:56 +0000 Subject: [PATCH 1/7] feat: adds roundingmode and entity types --- google/cloud/bigquery/enums.py | 40 +++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/google/cloud/bigquery/enums.py b/google/cloud/bigquery/enums.py index d8cbe9969..bfe892c01 100644 --- a/google/cloud/bigquery/enums.py +++ b/google/cloud/bigquery/enums.py @@ -246,6 +246,11 @@ class KeyResultStatementKind: class StandardSqlTypeNames(str, enum.Enum): + """Enum of allowed SQL type names in schema.SchemaField. + + Datatype used in GoogleSQL. + """ + def _generate_next_value_(name, start, count, last_values): return name @@ -267,6 +272,9 @@ def _generate_next_value_(name, start, count, last_values): ARRAY = enum.auto() STRUCT = enum.auto() RANGE = enum.auto() + # NOTE: FOREIGN acts as a wrapper for data types + # not natively understood by BigQuery unless translated + FOREIGN = enum.auto() class EntityTypes(str, enum.Enum): @@ -285,7 +293,10 @@ class EntityTypes(str, enum.Enum): # See also: https://cloud.google.com/bigquery/data-types#legacy_sql_data_types # and https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types class SqlTypeNames(str, enum.Enum): - """Enum of allowed SQL type names in schema.SchemaField.""" + """Enum of allowed SQL type names in schema.SchemaField. + + Datatype used in Legacy SQL. + """ STRING = "STRING" BYTES = "BYTES" @@ -306,6 +317,9 @@ class SqlTypeNames(str, enum.Enum): DATETIME = "DATETIME" INTERVAL = "INTERVAL" # NOTE: not available in legacy types RANGE = "RANGE" # NOTE: not available in legacy types + # NOTE: FOREIGN acts as a wrapper for data types + # not natively understood by BigQuery unless translated + FOREIGN = "FOREIGN" class WriteDisposition(object): @@ -344,3 +358,27 @@ class DeterminismLevel: NOT_DETERMINISTIC = "NOT_DETERMINISTIC" """The UDF is not deterministic.""" + + +class RoundingMode(enum.Enum): + """Rounding mode options that can be used when storing NUMERIC or BIGNUMERIC + values. + + * Unspecified will default to using ROUND_HALF_AWAY_FROM_ZERO. + * ROUND_HALF_AWAY_FROM_ZERO rounds half values away from zero when applying + precision and scale upon writing of NUMERIC and BIGNUMERIC values. + For Scale: + 0 1.1, 1.2, 1.3, 1.4 => 1 + 1.5, 1.6, 1.7, 1.8, 1.9 => 2 + * ROUND_HALF_EVEN rounds half values to the nearest even value when applying + precision and scale upon writing of NUMERIC and BIGNUMERIC values. + For Scale: + 0 1.1, 1.2, 1.3, 1.4 => 1 + 1.5 => 2 + 1.6, 1.7, 1.8, 1.9 => 2 + 2.5 => 2 + """ + + ROUNDING_MODE_UNSPECIFIED = 0 + ROUND_HALF_AWAY_FROM_ZERO = 1 + ROUND_HALF_EVEN = 2 From c66377eee92e862c06a84262d8f0818f5dff5c3c Mon Sep 17 00:00:00 2001 From: chalmer lowe Date: Mon, 27 Jan 2025 18:33:43 +0000 Subject: [PATCH 2/7] Adds rounding_mode to schema file and tests --- google/cloud/bigquery/schema.py | 73 ++++++++++++++++++++++++++++++++- tests/unit/test_schema.py | 52 ++++++++++++++++++++++- 2 files changed, 122 insertions(+), 3 deletions(-) diff --git a/google/cloud/bigquery/schema.py b/google/cloud/bigquery/schema.py index 42dfbfca8..68a62e25e 100644 --- a/google/cloud/bigquery/schema.py +++ b/google/cloud/bigquery/schema.py @@ -22,14 +22,15 @@ from google.cloud.bigquery import _helpers from google.cloud.bigquery import standard_sql +from google.cloud.bigquery import enums from google.cloud.bigquery.enums import StandardSqlTypeNames _STRUCT_TYPES = ("RECORD", "STRUCT") # SQL types reference: -# https://cloud.google.com/bigquery/data-types#legacy_sql_data_types -# https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types +# LEGACY SQL: https://cloud.google.com/bigquery/data-types#legacy_sql_data_types +# GoogleSQL: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types LEGACY_TO_STANDARD_TYPES = { "STRING": StandardSqlTypeNames.STRING, "BYTES": StandardSqlTypeNames.BYTES, @@ -48,6 +49,7 @@ "DATE": StandardSqlTypeNames.DATE, "TIME": StandardSqlTypeNames.TIME, "DATETIME": StandardSqlTypeNames.DATETIME, + "FOREIGN": StandardSqlTypeNames.FOREIGN, # no direct conversion from ARRAY, the latter is represented by mode="REPEATED" } """String names of the legacy SQL types to integer codes of Standard SQL standard_sql.""" @@ -166,6 +168,35 @@ class SchemaField(object): the type is RANGE, this field is required. Possible values for the field element type of a RANGE include `DATE`, `DATETIME` and `TIMESTAMP`. + + rounding_mode: Union[enums.RoundingMode, str, None] + Specifies the rounding mode to be used when storing values of + NUMERIC and BIGNUMERIC type. + + Unspecified will default to using ROUND_HALF_AWAY_FROM_ZERO. + ROUND_HALF_AWAY_FROM_ZERO rounds half values away from zero + when applying precision and scale upon writing of NUMERIC and BIGNUMERIC + values. + + For Scale: 0 + 1.1, 1.2, 1.3, 1.4 => 1 + 1.5, 1.6, 1.7, 1.8, 1.9 => 2 + + ROUND_HALF_EVEN rounds half values to the nearest even value + when applying precision and scale upon writing of NUMERIC and BIGNUMERIC + values. + + For Scale: 0 + 1.1, 1.2, 1.3, 1.4 => 1 + 1.5 => 2 + 1.6, 1.7, 1.8, 1.9 => 2 + 2.5 => 2 + + foreign_type_definition: Optional[str] + Definition of the foreign data type. + + Only valid for top-level schema fields (not nested fields). + If the type is FOREIGN, this field is required. """ def __init__( @@ -181,11 +212,14 @@ def __init__( scale: Union[int, _DefaultSentinel] = _DEFAULT_VALUE, max_length: Union[int, _DefaultSentinel] = _DEFAULT_VALUE, range_element_type: Union[FieldElementType, str, None] = None, + rounding_mode: Union[enums.RoundingMode, str, None] = None, + foreign_type_definition: Optional[str] = None, ): self._properties: Dict[str, Any] = { "name": name, "type": field_type, } + self._properties["name"] = name if mode is not None: self._properties["mode"] = mode.upper() if description is not _DEFAULT_VALUE: @@ -206,6 +240,25 @@ def __init__( self._properties["rangeElementType"] = {"type": range_element_type} if isinstance(range_element_type, FieldElementType): self._properties["rangeElementType"] = range_element_type.to_api_repr() + if rounding_mode is not None: + if isinstance(rounding_mode, enums.RoundingMode): + self._properties["roundingMode"] = rounding_mode.name + elif isinstance(rounding_mode, str): + self._properties["roundingMode"] = rounding_mode + else: + self._properties["roundingMode"] = None + if isinstance(foreign_type_definition, str): + self._properties["foreignTypeDefinition"] = foreign_type_definition + + # The order of operations is important: + # If field_type is FOREIGN, then foreign_type_definition must be set. + if field_type != "FOREIGN": + self._properties["type"] = field_type + else: + if self._properties.get("foreignTypeDefinition") is None: + raise ValueError( + "If the 'field_type' is 'FOREIGN', then 'foreign_type_definition' is required." + ) if fields: # Don't set the property if it's not set. self._properties["fields"] = [field.to_api_repr() for field in fields] @@ -304,6 +357,22 @@ def range_element_type(self): ret = self._properties.get("rangeElementType") return FieldElementType.from_api_repr(ret) + @property + def rounding_mode(self): + """Enum that specifies the rounding mode to be used when storing values of + NUMERIC and BIGNUMERIC type. + """ + return self._properties.get("roundingMode") + + @property + def foreign_type_definition(self): + """Definition of the foreign data type. + + Only valid for top-level schema fields (not nested fields). + If the type is FOREIGN, this field is required. + """ + return self._properties.get("foreignTypeDefinition") + @property def fields(self): """Optional[tuple]: Subfields contained in this field. diff --git a/tests/unit/test_schema.py b/tests/unit/test_schema.py index efbc5d26f..58333a4b1 100644 --- a/tests/unit/test_schema.py +++ b/tests/unit/test_schema.py @@ -19,6 +19,7 @@ import pytest from google.cloud import bigquery +from google.cloud.bigquery import enums from google.cloud.bigquery.standard_sql import StandardSqlStructType from google.cloud.bigquery import schema from google.cloud.bigquery.schema import PolicyTagList @@ -49,9 +50,12 @@ def test_constructor_defaults(self): self.assertEqual(field.fields, ()) self.assertIsNone(field.policy_tags) self.assertIsNone(field.default_value_expression) + self.assertEqual(field.rounding_mode, None) + self.assertEqual(field.foreign_type_definition, None) def test_constructor_explicit(self): FIELD_DEFAULT_VALUE_EXPRESSION = "This is the default value for this field" + ROUNDINGMODE = enums.RoundingMode.ROUNDING_MODE_UNSPECIFIED field = self._make_one( "test", "STRING", @@ -64,6 +68,8 @@ def test_constructor_explicit(self): ) ), default_value_expression=FIELD_DEFAULT_VALUE_EXPRESSION, + rounding_mode=ROUNDINGMODE, + foreign_type_definition="INTEGER", ) self.assertEqual(field.name, "test") self.assertEqual(field.field_type, "STRING") @@ -80,6 +86,8 @@ def test_constructor_explicit(self): ) ), ) + self.assertEqual(field.rounding_mode, ROUNDINGMODE.name) + self.assertEqual(field.foreign_type_definition, "INTEGER") def test_constructor_explicit_none(self): field = self._make_one("test", "STRING", description=None, policy_tags=None) @@ -137,8 +145,16 @@ def test_to_api_repr(self): {"names": ["foo", "bar"]}, ) + ROUNDINGMODE = enums.RoundingMode.ROUNDING_MODE_UNSPECIFIED + field = self._make_one( - "foo", "INTEGER", "NULLABLE", description="hello world", policy_tags=policy + "foo", + "INTEGER", + "NULLABLE", + description="hello world", + policy_tags=policy, + rounding_mode=ROUNDINGMODE, + foreign_type_definition=None, ) self.assertEqual( field.to_api_repr(), @@ -148,6 +164,7 @@ def test_to_api_repr(self): "type": "INTEGER", "description": "hello world", "policyTags": {"names": ["foo", "bar"]}, + "roundingMode": "ROUNDING_MODE_UNSPECIFIED", }, ) @@ -181,6 +198,7 @@ def test_from_api_repr(self): "description": "test_description", "name": "foo", "type": "record", + "roundingMode": "ROUNDING_MODE_UNSPECIFIED", } ) self.assertEqual(field.name, "foo") @@ -192,6 +210,7 @@ def test_from_api_repr(self): self.assertEqual(field.fields[0].field_type, "INTEGER") self.assertEqual(field.fields[0].mode, "NULLABLE") self.assertEqual(field.range_element_type, None) + self.assertEqual(field.rounding_mode, "ROUNDING_MODE_UNSPECIFIED") def test_from_api_repr_policy(self): field = self._get_target_class().from_api_repr( @@ -283,6 +302,11 @@ def test_fields_property(self): schema_field = self._make_one("boat", "RECORD", fields=fields) self.assertEqual(schema_field.fields, fields) + def test_roundingmode_property_str(self): + ROUNDINGMODE = "ROUNDING_MODE_UNSPECIFIED" + schema_field = self._make_one("test", "STRING", rounding_mode=ROUNDINGMODE) + self.assertEqual(schema_field.rounding_mode, ROUNDINGMODE) + def test_to_standard_sql_simple_type(self): examples = ( # a few legacy types @@ -457,6 +481,32 @@ def test_to_standard_sql_unknown_type(self): bigquery.StandardSqlTypeNames.TYPE_KIND_UNSPECIFIED, ) + def test_to_standard_sql_foreign_type_valid(self): + legacy_type = "FOREIGN" + standard_type = bigquery.StandardSqlTypeNames.FOREIGN + foreign_type_definition = "INTEGER" + + field = self._make_one( + "some_field", + field_type=legacy_type, + foreign_type_definition=foreign_type_definition, + ) + standard_field = field.to_standard_sql() + self.assertEqual(standard_field.name, "some_field") + self.assertEqual(standard_field.type.type_kind, standard_type) + + def test_to_standard_sql_foreign_type_invalid(self): + legacy_type = "FOREIGN" + foreign_type_definition = None + + with self.assertRaises(ValueError) as context: + self._make_one( + "some_field", + field_type=legacy_type, + foreign_type_definition=foreign_type_definition, + ) + self.assertTrue("If the 'field_type'" in context.exception.args[0]) + def test___eq___wrong_type(self): field = self._make_one("test", "STRING") other = object() From afe0d9bbccd84ec69844e722b89eb3c9d1428349 Mon Sep 17 00:00:00 2001 From: chalmer lowe Date: Mon, 27 Jan 2025 19:57:37 +0000 Subject: [PATCH 3/7] tweaks RoundingMode docstring and roundingmode logic --- google/cloud/bigquery/enums.py | 16 +++++++++------- google/cloud/bigquery/schema.py | 4 ++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/google/cloud/bigquery/enums.py b/google/cloud/bigquery/enums.py index bfe892c01..7529848ad 100644 --- a/google/cloud/bigquery/enums.py +++ b/google/cloud/bigquery/enums.py @@ -364,16 +364,18 @@ class RoundingMode(enum.Enum): """Rounding mode options that can be used when storing NUMERIC or BIGNUMERIC values. - * Unspecified will default to using ROUND_HALF_AWAY_FROM_ZERO. - * ROUND_HALF_AWAY_FROM_ZERO rounds half values away from zero when applying + ROUNDING_MODE_UNSPECIFIED: will default to using ROUND_HALF_AWAY_FROM_ZERO. + + ROUND_HALF_AWAY_FROM_ZERO: rounds half values away from zero when applying precision and scale upon writing of NUMERIC and BIGNUMERIC values. - For Scale: - 0 1.1, 1.2, 1.3, 1.4 => 1 + For Scale: 0 + 1.1, 1.2, 1.3, 1.4 => 1 1.5, 1.6, 1.7, 1.8, 1.9 => 2 - * ROUND_HALF_EVEN rounds half values to the nearest even value when applying + + ROUND_HALF_EVEN: rounds half values to the nearest even value when applying precision and scale upon writing of NUMERIC and BIGNUMERIC values. - For Scale: - 0 1.1, 1.2, 1.3, 1.4 => 1 + For Scale: 0 + 1.1, 1.2, 1.3, 1.4 => 1 1.5 => 2 1.6, 1.7, 1.8, 1.9 => 2 2.5 => 2 diff --git a/google/cloud/bigquery/schema.py b/google/cloud/bigquery/schema.py index 68a62e25e..a891afcad 100644 --- a/google/cloud/bigquery/schema.py +++ b/google/cloud/bigquery/schema.py @@ -245,8 +245,8 @@ def __init__( self._properties["roundingMode"] = rounding_mode.name elif isinstance(rounding_mode, str): self._properties["roundingMode"] = rounding_mode - else: - self._properties["roundingMode"] = None + else: + self._properties["roundingMode"] = None if isinstance(foreign_type_definition, str): self._properties["foreignTypeDefinition"] = foreign_type_definition From e4404608c24b21ef55dfebf8c90e5aa4e2c6e23c Mon Sep 17 00:00:00 2001 From: chalmer lowe Date: Tue, 28 Jan 2025 14:13:23 +0000 Subject: [PATCH 4/7] Updates tests to apply better coverage for rounding_mode --- google/cloud/bigquery/schema.py | 7 ++----- tests/unit/test_schema.py | 21 ++++++++++++++++++++- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/google/cloud/bigquery/schema.py b/google/cloud/bigquery/schema.py index a891afcad..aaf0bc595 100644 --- a/google/cloud/bigquery/schema.py +++ b/google/cloud/bigquery/schema.py @@ -242,11 +242,8 @@ def __init__( self._properties["rangeElementType"] = range_element_type.to_api_repr() if rounding_mode is not None: if isinstance(rounding_mode, enums.RoundingMode): - self._properties["roundingMode"] = rounding_mode.name - elif isinstance(rounding_mode, str): - self._properties["roundingMode"] = rounding_mode - else: - self._properties["roundingMode"] = None + rounding_mode = rounding_mode.name + self._properties["roundingMode"] = rounding_mode if isinstance(foreign_type_definition, str): self._properties["foreignTypeDefinition"] = foreign_type_definition diff --git a/tests/unit/test_schema.py b/tests/unit/test_schema.py index 58333a4b1..2bdbff774 100644 --- a/tests/unit/test_schema.py +++ b/tests/unit/test_schema.py @@ -303,10 +303,29 @@ def test_fields_property(self): self.assertEqual(schema_field.fields, fields) def test_roundingmode_property_str(self): - ROUNDINGMODE = "ROUNDING_MODE_UNSPECIFIED" + # via init + ROUNDINGMODE = "ROUND_HALF_AWAY_FROM_ZERO" schema_field = self._make_one("test", "STRING", rounding_mode=ROUNDINGMODE) self.assertEqual(schema_field.rounding_mode, ROUNDINGMODE) + # via _properties + del schema_field + schema_field = self._make_one("test", "STRING") + schema_field._properties["roundingMode"] = ROUNDINGMODE + self.assertEqual(schema_field.rounding_mode, ROUNDINGMODE) + + def test_foreign_type_definition_property_str(self): + FOREIGN_TYPE_DEFINITION = "INTEGER" + schema_field = self._make_one( + "test", "STRING", foreign_type_definition=FOREIGN_TYPE_DEFINITION + ) + self.assertEqual(schema_field.foreign_type_definition, FOREIGN_TYPE_DEFINITION) + + del schema_field + schema_field = self._make_one("test", "STRING") + schema_field._properties["foreignTypeDefinition"] = FOREIGN_TYPE_DEFINITION + self.assertEqual(schema_field.foreign_type_definition, FOREIGN_TYPE_DEFINITION) + def test_to_standard_sql_simple_type(self): examples = ( # a few legacy types From afcd892cc56ae4316a973ec19229b76971b752e7 Mon Sep 17 00:00:00 2001 From: chalmer lowe Date: Tue, 28 Jan 2025 14:36:41 +0000 Subject: [PATCH 5/7] Modifies docstring --- google/cloud/bigquery/enums.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/google/cloud/bigquery/enums.py b/google/cloud/bigquery/enums.py index 7529848ad..e814cd187 100644 --- a/google/cloud/bigquery/enums.py +++ b/google/cloud/bigquery/enums.py @@ -369,16 +369,16 @@ class RoundingMode(enum.Enum): ROUND_HALF_AWAY_FROM_ZERO: rounds half values away from zero when applying precision and scale upon writing of NUMERIC and BIGNUMERIC values. For Scale: 0 - 1.1, 1.2, 1.3, 1.4 => 1 - 1.5, 1.6, 1.7, 1.8, 1.9 => 2 + * 1.1, 1.2, 1.3, 1.4 => 1 + * 1.5, 1.6, 1.7, 1.8, 1.9 => 2 ROUND_HALF_EVEN: rounds half values to the nearest even value when applying precision and scale upon writing of NUMERIC and BIGNUMERIC values. For Scale: 0 - 1.1, 1.2, 1.3, 1.4 => 1 - 1.5 => 2 - 1.6, 1.7, 1.8, 1.9 => 2 - 2.5 => 2 + * 1.1, 1.2, 1.3, 1.4 => 1 + * 1.5 => 2 + * 1.6, 1.7, 1.8, 1.9 => 2 + * 2.5 => 2 """ ROUNDING_MODE_UNSPECIFIED = 0 From eacf9fe8201a3128bacc075e5c3e91aa20b5da0c Mon Sep 17 00:00:00 2001 From: chalmer lowe Date: Wed, 29 Jan 2025 11:19:35 +0000 Subject: [PATCH 6/7] Removes client-side validation, simplifies some code --- google/cloud/bigquery/enums.py | 11 +++++++---- google/cloud/bigquery/schema.py | 11 ----------- tests/unit/test_schema.py | 19 ++----------------- 3 files changed, 9 insertions(+), 32 deletions(-) diff --git a/google/cloud/bigquery/enums.py b/google/cloud/bigquery/enums.py index e814cd187..5519bc989 100644 --- a/google/cloud/bigquery/enums.py +++ b/google/cloud/bigquery/enums.py @@ -360,7 +360,7 @@ class DeterminismLevel: """The UDF is not deterministic.""" -class RoundingMode(enum.Enum): +class RoundingMode(str, enum.Enum): """Rounding mode options that can be used when storing NUMERIC or BIGNUMERIC values. @@ -381,6 +381,9 @@ class RoundingMode(enum.Enum): * 2.5 => 2 """ - ROUNDING_MODE_UNSPECIFIED = 0 - ROUND_HALF_AWAY_FROM_ZERO = 1 - ROUND_HALF_EVEN = 2 + def _generate_next_value_(name, start, count, last_values): + return name + + ROUNDING_MODE_UNSPECIFIED = enum.auto() + ROUND_HALF_AWAY_FROM_ZERO = enum.auto() + ROUND_HALF_EVEN = enum.auto() diff --git a/google/cloud/bigquery/schema.py b/google/cloud/bigquery/schema.py index aaf0bc595..81cec6db2 100644 --- a/google/cloud/bigquery/schema.py +++ b/google/cloud/bigquery/schema.py @@ -241,21 +241,10 @@ def __init__( if isinstance(range_element_type, FieldElementType): self._properties["rangeElementType"] = range_element_type.to_api_repr() if rounding_mode is not None: - if isinstance(rounding_mode, enums.RoundingMode): - rounding_mode = rounding_mode.name self._properties["roundingMode"] = rounding_mode if isinstance(foreign_type_definition, str): self._properties["foreignTypeDefinition"] = foreign_type_definition - # The order of operations is important: - # If field_type is FOREIGN, then foreign_type_definition must be set. - if field_type != "FOREIGN": - self._properties["type"] = field_type - else: - if self._properties.get("foreignTypeDefinition") is None: - raise ValueError( - "If the 'field_type' is 'FOREIGN', then 'foreign_type_definition' is required." - ) if fields: # Don't set the property if it's not set. self._properties["fields"] = [field.to_api_repr() for field in fields] diff --git a/tests/unit/test_schema.py b/tests/unit/test_schema.py index 2bdbff774..467f1e1de 100644 --- a/tests/unit/test_schema.py +++ b/tests/unit/test_schema.py @@ -55,7 +55,6 @@ def test_constructor_defaults(self): def test_constructor_explicit(self): FIELD_DEFAULT_VALUE_EXPRESSION = "This is the default value for this field" - ROUNDINGMODE = enums.RoundingMode.ROUNDING_MODE_UNSPECIFIED field = self._make_one( "test", "STRING", @@ -68,7 +67,7 @@ def test_constructor_explicit(self): ) ), default_value_expression=FIELD_DEFAULT_VALUE_EXPRESSION, - rounding_mode=ROUNDINGMODE, + rounding_mode=enums.RoundingMode.ROUNDING_MODE_UNSPECIFIED, foreign_type_definition="INTEGER", ) self.assertEqual(field.name, "test") @@ -86,7 +85,7 @@ def test_constructor_explicit(self): ) ), ) - self.assertEqual(field.rounding_mode, ROUNDINGMODE.name) + self.assertEqual(field.rounding_mode, "ROUNDING_MODE_UNSPECIFIED") self.assertEqual(field.foreign_type_definition, "INTEGER") def test_constructor_explicit_none(self): @@ -303,12 +302,10 @@ def test_fields_property(self): self.assertEqual(schema_field.fields, fields) def test_roundingmode_property_str(self): - # via init ROUNDINGMODE = "ROUND_HALF_AWAY_FROM_ZERO" schema_field = self._make_one("test", "STRING", rounding_mode=ROUNDINGMODE) self.assertEqual(schema_field.rounding_mode, ROUNDINGMODE) - # via _properties del schema_field schema_field = self._make_one("test", "STRING") schema_field._properties["roundingMode"] = ROUNDINGMODE @@ -514,18 +511,6 @@ def test_to_standard_sql_foreign_type_valid(self): self.assertEqual(standard_field.name, "some_field") self.assertEqual(standard_field.type.type_kind, standard_type) - def test_to_standard_sql_foreign_type_invalid(self): - legacy_type = "FOREIGN" - foreign_type_definition = None - - with self.assertRaises(ValueError) as context: - self._make_one( - "some_field", - field_type=legacy_type, - foreign_type_definition=foreign_type_definition, - ) - self.assertTrue("If the 'field_type'" in context.exception.args[0]) - def test___eq___wrong_type(self): field = self._make_one("test", "STRING") other = object() From 70b5fb1e849ff1caf8e2cef04c5d4afec13ed5df Mon Sep 17 00:00:00 2001 From: chalmer lowe Date: Fri, 31 Jan 2025 16:02:25 +0000 Subject: [PATCH 7/7] Updates foreign_type_definition processing --- google/cloud/bigquery/schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/cloud/bigquery/schema.py b/google/cloud/bigquery/schema.py index 81cec6db2..0f011a275 100644 --- a/google/cloud/bigquery/schema.py +++ b/google/cloud/bigquery/schema.py @@ -242,7 +242,7 @@ def __init__( self._properties["rangeElementType"] = range_element_type.to_api_repr() if rounding_mode is not None: self._properties["roundingMode"] = rounding_mode - if isinstance(foreign_type_definition, str): + if foreign_type_definition is not None: self._properties["foreignTypeDefinition"] = foreign_type_definition if fields: # Don't set the property if it's not set.