Preserve dialect-specific ARRAY types instead of adapting to generic ARRAY#480
Merged
agronholm merged 2 commits intoJun 8, 2026
Merged
Conversation
…ARRAY
When reflecting a PostgreSQL ``text[]`` (or any dialect-specific ARRAY)
column, sqlacodegen previously walked the type's MRO in
``get_adapted_type`` and substituted ``sqlalchemy.dialects.postgresql.ARRAY``
with the generic ``sqlalchemy.ARRAY``. The generic ARRAY does not implement
operators like ``.contains()``, ``.any()`` or ``.all()`` -- calling them on
a generated model raises::
NotImplementedError: ARRAY.contains() not implemented for the base
ARRAY type; please use the dialect-specific ARRAY type
The workaround was to pass ``--options keep_dialect_types``, but that
also forces every other type back to its dialect-specific form
(``INTEGER`` instead of ``Integer``, etc.), which is more than the user
wants.
This change special-cases ARRAY in ``get_adapted_type``: when the column
type is a subclass of the generic ARRAY (i.e. a dialect-specific ARRAY),
the original class is kept and only the item type is adapted. Plain
generic ``sqlalchemy.ARRAY`` inputs are unaffected, and no other type's
adaptation behavior changes.
The existing ``test_arrays`` is updated to expect the dialect-specific
import, and a new ``test_array_preserves_dialect_for_runtime_operators``
test locks in the regression scenario described in the issue.
Fixes agronholm#441
sheinbergon
reviewed
Jun 6, 2026
sheinbergon
reviewed
Jun 6, 2026
sheinbergon
reviewed
Jun 6, 2026
Collaborator
|
@agronholm fine by me. WDYT? |
agronholm
reviewed
Jun 7, 2026
Owner
I share your sentiments about the excessively verbose AI comments. And I left a minor comment of my own. |
sheinbergon
approved these changes
Jun 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #441.
Problem
When sqlacodegen reflects a PostgreSQL
text[](or any dialect-specificARRAY) column,
get_adapted_typewalks the column type's MRO and substitutesthe original
sqlalchemy.dialects.postgresql.ARRAYwith the genericsqlalchemy.ARRAY. The generic ARRAY does not implement operators like.contains(),.any()or.all(), so calling them on a generated modelraises at runtime:
The existing workaround (
--options keep_dialect_types) opts out of allgeneric-type adaptation, which is more than the user usually wants:
IntegerbecomesINTEGER,BooleanbecomesBOOLEAN, and so on.Fix
get_adapted_typenow special-cases ARRAY: when the input is a subclass ofthe generic
ARRAY(i.e. a dialect-specific ARRAY such aspostgresql.ARRAY), the original class is preserved and only the item typeis adapted recursively. Plain
sqlalchemy.ARRAYinputs are unaffected, andno other type's adaptation behavior changes.
This matches the direction the maintainer pointed to in the issue thread
("Should the array type perhaps be special cased? I think that overall the
generalization of types is correct.").
Generated output before / after
Input column:
Column("tags", postgresql.ARRAY(postgresql.TEXT))Before:
After:
Item types are still adapted:
postgresql.DOUBLE_PRECISIONstill becomesDouble,postgresql.TEXTstill becomesText. Only the outerARRAYclass is kept.
Tests
test_arraysintests/test_generator_tables.pyisupdated to expect
from sqlalchemy.dialects.postgresql import ARRAYfor inputs constructed with
postgresql.ARRAY(...). The item-typeadaptation it was already exercising (
DOUBLE_PRECISION->Double,INTEGER->Integer) is preserved.test_array_preserves_dialect_for_runtime_operatorsis added asa focused regression test that mirrors the scenario from the issue
(
text[]column) and documents why the dialect-specific ARRAY isrequired.
pytest -q-> 157 passed).ruff check,ruff format --check, andmypy src/sqlacodegen/generators.pyallpass.
Notes / scope
ARRAY(SAEnum(...))using the genericsqlalchemy.ARRAYdirectly continue to work unchanged, because theearly-return only triggers when
type(coltype) is not ARRAY(i.e.the input is a dialect-specific subclass).
keep_dialect_typesoption is unaffected; this change improvesthe default path, which is what the workaround was previously needed
to escape.