-
Notifications
You must be signed in to change notification settings - Fork 116
Reflect the field and value types of top-level STRUCT and MAP columns #995
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
Changes from all commits
4217bbe
543d681
035a3b9
e06653d
db6be39
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 |
|---|---|---|
|
|
@@ -695,6 +695,34 @@ def get_columns(self, connection: Connection, table_name: str, schema: str | Non | |
| return self._get_columns(connection, table_name, schema=schema, **kw) | ||
|
|
||
| def _get_column_type(self, type_: str, _nested: bool = False): | ||
| """Map an Athena column type string to a SQLAlchemy type. | ||
|
|
||
| Accepts both the Hive (``struct<a:int>``, ``map<int,int>``) and the | ||
| Trino (``row(a integer)``, ``map(integer, integer)``) spellings, and | ||
| parses the element, key, value, and field types of ARRAY, MAP, and | ||
| STRUCT/ROW types. | ||
|
|
||
| Args: | ||
| type_: The column type reported by Athena. | ||
| _nested: Whether ``type_`` is nested in another type. A nested MAP | ||
| or STRUCT/ROW that cannot be parsed raises, so that the | ||
| enclosing type is reported as unrecognized. | ||
|
|
||
| Returns: | ||
| The SQLAlchemy type. A type name that is not recognized, such as | ||
| ``foo`` in ``struct<a:foo>``, becomes ``NullType`` in place with a | ||
| warning. A type that cannot be parsed, such as ``map<int>`` or | ||
| ``varchar(x)``, makes its innermost enclosing ARRAY ``NullType`` | ||
| with a warning; without an enclosing ARRAY, a top-level MAP or | ||
| STRUCT/ROW becomes ``NullType`` instead. | ||
|
|
||
| Raises: | ||
| ValueError: If a type cannot be parsed and neither an enclosing | ||
| ARRAY nor a top-level MAP or STRUCT/ROW handles it, for example | ||
| a top-level ``varchar(x)`` or a nested ``map<int>``. | ||
| TypeError: In the same case, for a DECIMAL type with more | ||
| arguments than SQLAlchemy's ``DECIMAL`` accepts. | ||
| """ | ||
| type_ = type_.strip() | ||
| match = self._pattern_column_type.match(type_) | ||
| if match: | ||
|
|
@@ -710,6 +738,12 @@ def _get_column_type(self, type_: str, _nested: bool = False): | |
| except (TypeError, ValueError): | ||
| util.warn(f"Did not recognize type '{type_}'") | ||
| return types.NullType() | ||
| if not _nested and name in ("map", "row", "struct") and length: | ||
|
Member
Author
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. Self-review round one (implementation behavior, public contracts, simplicity, regression coverage) Base Covered: every file in
|
||
| try: | ||
| return self._get_column_type(type_, _nested=True) | ||
| except (TypeError, ValueError): | ||
| util.warn(f"Did not recognize type '{type_}'") | ||
| return types.NullType() | ||
| if _nested and name == "map" and length: | ||
| key, value = _split_type_arguments(length) | ||
| return AthenaMap( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -90,8 +90,8 @@ class AthenaTypeCompiler(GenericTypeCompiler): | |
| without a length render as STRING; with a length, CHAR, NCHAR, VARCHAR, | ||
| and NVARCHAR render as CHAR(n) or VARCHAR(n). Complex | ||
| types render as ``STRUCT<name:type>``, ``MAP<key, value>``, and | ||
| ``ARRAY<item>``. TIME, JSON, and a STRUCT without fields have no Athena | ||
| DDL type and raise ``CompileError``. | ||
| ``ARRAY<item>``. TIME, JSON, a STRUCT without fields, and ``NullType`` | ||
|
Member
Author
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. Design consultation (relayed Claude Fable 5.1 opinion, requested by the maintainer)
Opinion:
Open points it raised, with their status:
Author verification before implementing: the 19efdaa commit message (the
Member
Author
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. Self-review of Round-one perspective: CLEAN.
Round-two perspective: CLEAN.
Member
Author
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. Independent review of Result: CLEAN. Covered:
|
||
| have no Athena DDL type and raise ``CompileError``. | ||
|
|
||
| See Also: | ||
| AWS Athena Data Types: | ||
|
|
@@ -238,10 +238,6 @@ def visit_unicode(self, type_, **kw): | |
| def visit_unicode_text(self, type_, **kw): | ||
| return "STRING" | ||
|
|
||
| @override | ||
| def visit_null(self, type_, **kw): | ||
| return "NULL" | ||
|
|
||
| def visit_tinyint(self, type_, **kw): | ||
| """Render a tinyint type through ``visit_TINYINT``. | ||
|
|
||
|
|
||
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.
Self-review round two (claims, callers, operations, evidence)
Base
6f258501a56124f6e29c6d530ce429b702ed2ab3, head4217bbe65b6203f42522c63646bb2b5b06c02a7b. Result: FINDINGS (1, a PR body correction; no code change).column + 'x'rendersm || ...for String butm + ...forAthenaMap, whilecontains()andlike()render the same. The body now states exactly that.({'1': '2', '3': '4'}, {'a': '1', 'b': '2'})before the change, andtest_reflect_selectasserts the same row values with the new types (677 passed on AWS). The body now cites both.AthenaMapandAthenaStructhave no result processor. Links to the duplicated "Data format support" headings were avoided, because Sphinx renders the second one as#id6._get_column_typeis called only from_columnand from itself; the signature is unchanged.just test sqla/sqla-asyncwere not run locally; CI runs them on Ready.