-
Notifications
You must be signed in to change notification settings - Fork 309
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 table_constraints
field to Table model
#1755
Changes from 1 commit
cea6e03
9e34810
5d034f4
da663b2
29d1238
51b3c58
c69d717
8cdbc04
55054bb
9d49e50
7bdfad9
5145eb9
cd1ee01
dd8a16b
1fa1cfd
43ec134
3b566b8
34df1c6
eec92da
a84edc4
bec1b4a
8e07232
60ba10c
75f3482
21cac59
69a7235
c6ac023
7f40eab
1959ef9
22256f6
506afa4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -390,6 +390,7 @@ class Table(_TableBase): | |||||
"view_use_legacy_sql": "view", | ||||||
"view_query": "view", | ||||||
"require_partition_filter": "requirePartitionFilter", | ||||||
"table_constraints": "tableConstraints", | ||||||
} | ||||||
|
||||||
def __init__(self, table_ref, schema=None) -> None: | ||||||
|
@@ -973,6 +974,16 @@ def clone_definition(self) -> Optional["CloneDefinition"]: | |||||
clone_info = CloneDefinition(clone_info) | ||||||
return clone_info | ||||||
|
||||||
@property | ||||||
def table_constraints(self) -> Optional["TableConstraints"]: | ||||||
"""Tables Primary Key and Foreign Key information.""" | ||||||
table_constraints = self._properties.get( | ||||||
self._PROPERTY_TO_API_FIELD["table_constraints"] | ||||||
) | ||||||
if table_constraints is not None: | ||||||
table_constraints = TableConstraints.from_api_repr(table_constraints) | ||||||
return table_constraints | ||||||
|
||||||
@classmethod | ||||||
def from_string(cls, full_table_id: str) -> "Table": | ||||||
"""Construct a table from fully-qualified table ID. | ||||||
|
@@ -2942,6 +2953,123 @@ def __repr__(self): | |||||
return "TimePartitioning({})".format(",".join(key_vals)) | ||||||
|
||||||
|
||||||
class PrimaryKey: | ||||||
"""Represents the primary key constraint on a table's columns. | ||||||
|
||||||
Args: | ||||||
columns: The columns that are composed of the primary key constraint. | ||||||
""" | ||||||
|
||||||
def __init__(self, columns: List[str]): | ||||||
self.columns = columns | ||||||
|
||||||
def __eq__(self, other): | ||||||
if not isinstance(other, PrimaryKey): | ||||||
raise NotImplementedError | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My understanding from the Python documentation is that:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @chalmerlowe oh, right. Thank you. I missed that. It should be |
||||||
return self.columns == other.columns | ||||||
|
||||||
|
||||||
class ColumnReference: | ||||||
"""The pair of the foreign key column and primary key column. | ||||||
|
||||||
Args: | ||||||
referencing_column: The column that composes the foreign key. | ||||||
referenced_column: The column in the primary key that are referenced by the referencingColumn. | ||||||
""" | ||||||
|
||||||
def __init__(self, referencing_column: str, referenced_column: str): | ||||||
self.referencing_column = referencing_column | ||||||
self.referenced_column = referenced_column | ||||||
|
||||||
def __eq__(self, other): | ||||||
if not isinstance(other, ColumnReference): | ||||||
raise NotImplementedError | ||||||
return ( | ||||||
self.referenced_column == other.referencing_column | ||||||
and self.referenced_column == other.referenced_column | ||||||
) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For this portion of the mod (2985-2987), coverage.py indicates that we are not exercising this code fully. We do have tests that reference ColumnReferences, but not necessarily in a way that fully exercises each line of this code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks. I've added |
||||||
|
||||||
@classmethod | ||||||
def from_api_repr(cls, api_repr: Dict[str, Any]) -> "ColumnReference": | ||||||
"""Create an instance from API representation.""" | ||||||
return cls(api_repr["referencingColumn"], api_repr["referencedColumn"]) | ||||||
|
||||||
|
||||||
class ForeignKey: | ||||||
"""Represents a foreign key constraint on a table's columns. | ||||||
|
||||||
Args: | ||||||
name: Set only if the foreign key constraint is named. | ||||||
referenced_table: The table that holds the primary key and is referenced by this foreign key. | ||||||
column_references: The columns that compose the foreign key. | ||||||
""" | ||||||
|
||||||
def __init__( | ||||||
self, | ||||||
name: str, | ||||||
referenced_table: TableReference, | ||||||
column_references: List[ColumnReference], | ||||||
): | ||||||
self.name = name | ||||||
self.referenced_table = referenced_table | ||||||
self.column_references = column_references | ||||||
|
||||||
def __eq__(self, other): | ||||||
if not isinstance(other, ForeignKey): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This (3019) is also identified by coverage.py as being untested |
||||||
raise NotImplementedError | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Change all
|
||||||
return ( | ||||||
self.name == other.name and self.referenced_table == other.referenced_table | ||||||
) | ||||||
|
||||||
@classmethod | ||||||
def from_api_repr(cls, api_repr: Dict[str, Any]) -> "ForeignKey": | ||||||
"""Create an instance from API representation.""" | ||||||
return cls( | ||||||
name=api_repr["name"], | ||||||
referenced_table=TableReference.from_api_repr(api_repr["referencedTable"]), | ||||||
column_references=[ | ||||||
ColumnReference.from_api_repr(column_reference_resource) | ||||||
for column_reference_resource in api_repr["columnReferences"] | ||||||
], | ||||||
) | ||||||
|
||||||
|
||||||
class TableConstraints: | ||||||
"""The TableConstraints defines the primary key and foreign key. | ||||||
|
||||||
Args: | ||||||
primary_key: | ||||||
Represents a primary key constraint on a table's columns. Present only if the table | ||||||
has a primary key. The primary key is not enforced. | ||||||
foreign_keys: | ||||||
Present only if the table has a foreign key. The foreign key is not enforced. | ||||||
|
||||||
""" | ||||||
|
||||||
def __init__( | ||||||
self, | ||||||
primary_key: Optional[PrimaryKey], | ||||||
foreign_keys: Optional[List[ForeignKey]], | ||||||
): | ||||||
self.primary_key = primary_key | ||||||
self.foreign_keys = foreign_keys | ||||||
|
||||||
@classmethod | ||||||
def from_api_repr(cls, resource: Dict[str, Any]) -> "TableConstraints": | ||||||
"""Create an instance from API representation.""" | ||||||
primary_key = None | ||||||
if "primaryKey" in resource: | ||||||
primary_key = PrimaryKey(resource["primaryKey"]["columns"]) | ||||||
|
||||||
foreign_keys = None | ||||||
if "foreignKeys" in resource: | ||||||
foreign_keys = [ | ||||||
ForeignKey.from_api_repr(foreign_key_resource) | ||||||
for foreign_key_resource in resource["foreignKeys"] | ||||||
] | ||||||
return cls(primary_key, foreign_keys) | ||||||
|
||||||
|
||||||
def _item_to_row(iterator, resource): | ||||||
"""Convert a JSON row to the native object. | ||||||
|
||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have several areas of code that coverage.py does not believe are being tested. Those lines of code are:
For line 2968, it does not appear that we have a test that specifically exercises the
raise
statement by trying to compare an input value that is not a PrimaryKey.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, I missed the coverage part 😅 Thank you for pointing that out. I've added missing coverage in commit 29d1238