Skip to content

fix(bigquery): account for range element type, rounding mode, and foreign type in SchemaField equality - #18540

Merged
chalmerlowe merged 3 commits into
mainfrom
fix/schema-field-equality
Oct 2, 2026
Merged

chalmerlowe merged 3 commits into
mainfrom
fix/schema-field-equality

Conversation

@chalmerlowe

Copy link
Copy Markdown
Contributor

Problem

When comparing two SchemaField objects with ==, fields with different range_element_type values (such as RANGE versus RANGE) were incorrectly evaluated as equal (True).

Investigation revealed that SchemaField._key() omitted range_element_type, meaning two fields with different range element types produced identical comparison keys. Furthermore, rounding_mode and foreign_type_definition were also omitted from _key(), causing fields differing only in those attributes to compare as equal. In addition, FieldElementType lacked custom equality (__eq__) and hashing (__hash__) methods, which prevented comparing element types directly.

Solution

  1. SchemaField._key:
    • Parameterized RANGE fields as RANGE in _key(), following the existing conventions used for STRING(max_length) and NUMERIC(precision, scale).
    • Added rounding_mode and foreign_type_definition to _key().
  2. SchemaField.__repr__:
    • Updated representation unpacking to cleanly handle all key attributes.
  3. FieldElementType:
    • Implemented _key, __eq__, __ne__, __hash__, and __repr__ for FieldElementType.
  4. Unit test coverage:
    • Added mismatch and match tests for range_element_type, rounding_mode, and foreign_type_definition.
    • Added equality, hash, and representation tests for FieldElementType.
    • Added parameterized tests verifying SchemaField.from_api_repr generates expected _key() values for RANGE types.

Notes to Reviewers

  • Parameterizing RANGE as RANGE aligns with GoogleSQL syntax and mirrors how parameterized string, bytes, and numeric fields are handled in SchemaField._key().
  • FieldElementType comparisons are case-insensitive because element types are normalized to uppercase upon creation.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the BigQuery schema classes to support new fields, including RANGE element types, rounding_mode, and foreign_type_definition. Specifically, it implements equality, hashing, and representation methods for FieldElementType, and updates SchemaField._key() and SchemaField.__repr__ to include these new attributes, along with adding comprehensive unit tests. Feedback on the changes suggests simplifying the SchemaField.__repr__ implementation by slicing the key tuple instead of unpacking all ten attributes to prevent fragility, and avoiding the local import of FieldElementType inside a test function since the schema module is already imported at the module level.

Comment on lines 540 to 565
(
name,
field_type,
mode,
default_value_expression,
description,
fields,
policy_tags,
timestamp_precision_tag,
rounding_mode,
foreign_type_definition,
) = self._key()
policy_tags_inst = None if policy_tags is None else PolicyTagList(policy_tags)
adjusted_key = (*initial_tags, policy_tags_inst, timestamp_precision_tag)
adjusted_key = (
name,
field_type,
mode,
default_value_expression,
description,
fields,
policy_tags_inst,
timestamp_precision_tag,
rounding_mode,
foreign_type_definition,
)
return f"{self.__class__.__name__}{adjusted_key}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current implementation of __repr__ explicitly unpacks and reconstructs all 10 attributes of SchemaField. This is highly verbose and fragile, as any future addition or reordering of fields in _key() will break this method. Since only policy_tags needs to be modified for the representation, we can slice the key tuple and replace the policy_tags element by index, making the code much more concise and maintainable.

        key = self._key()
        policy_tags = key[6]
        policy_tags_inst = None if policy_tags is None else PolicyTagList(policy_tags)
        adjusted_key = key[:6] + (policy_tags_inst,) + key[7:]
        return f"{self.__class__.__name__}{adjusted_key}"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed by introducing an internal _SchemaFieldKey NamedTuple and using key._replace(policy_tags=...) in __repr__. This avoids both verbose explicit unpacking and brittle magic index slicing (key[:6]), ensuring future field additions or reorderings remain safe.

Comment on lines +643 to +648
from google.cloud.bigquery.schema import FieldElementType

field = self._make_one("R", "RANGE", range_element_type="DATE")
other = self._make_one(
"R", "RANGE", range_element_type=FieldElementType("DATE")
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Avoid importing FieldElementType inside the test function since the schema module is already imported at the module level. You can reference it directly as schema.FieldElementType.

        field = self._make_one("R", "RANGE", range_element_type="DATE")
        other = self._make_one(
            "R", "RANGE", range_element_type=schema.FieldElementType("DATE")
        )
References
  1. Do not import modules or classes inside functions if they are already imported at the module level.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! Removed the local import and referenced schema.FieldElementType directly.

@tswast tswast left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@chalmerlowe
chalmerlowe marked this pull request as ready for review October 1, 2026 21:39
@chalmerlowe
chalmerlowe requested review from a team as code owners October 1, 2026 21:39
@chalmerlowe
chalmerlowe requested review from sycai and removed request for a team October 1, 2026 21:39
@chalmerlowe chalmerlowe self-assigned this Oct 1, 2026
@chalmerlowe chalmerlowe added the automerge Merge the pull request once unit tests and other checks pass. label Oct 1, 2026
@chalmerlowe
chalmerlowe merged commit 053dbc9 into main Oct 2, 2026
52 checks passed
@chalmerlowe
chalmerlowe deleted the fix/schema-field-equality branch October 2, 2026 01:01
@release-please release-please Bot mentioned this pull request Oct 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automerge Merge the pull request once unit tests and other checks pass.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants