Skip to content

add reflection for pg DOMAIN types similarly to ENUM reflection #10693

@ovangle

Description

@ovangle

Describe the use case

Motivated by an issue which I initially filed as sqlalchemy/alembic#1357, I did a little more digging into what was actually causing the issue today. It turns out that the column type information for DOMAIN columns is being replaced with a column of the domain's underlying type during reflection (see lib/sqlalchemy/dialects/postgresql/base.py:3760-3773). Note that the coltype isn't being set in that block, instead it replaces the attype and loops until it finds a suitable type to use instead of the domain.

Since it seems that the erasure is intentional (probably to work around deeper issues with wrapped types), I'm hesitant to pursue my investigation without seeking further guidance from someone more familiar with sqlalchemy internals.

Optional link from https://docs.sqlalchemy.org which documents the behavior that is expected

No response

SQLAlchemy Version in Use

2.1.0b1

DBAPI (i.e. the database driver)

psycopg2

Database Vendor and Major Version

PostgreSQL 15.4

Python Version

3.11

Operating system

Ubuntu

To Reproduce

def test_domain_type_reflection(self, metadata, connection):
        positive_int = DOMAIN("positive_int", Integer(), check='value > 0', not_null=True)
        Table(
            "table",
            metadata,
            Column("value", positive_int)
        )

        metadata.create_all(connection)
        m2 = MetaData()
        t2 = Table("table", m2, autoload_with=connection)

        eq_(t2.c.value.type.name, 'positive_int')
        eq_(t2.c.value.check, 'value > 0')

Error

./test/dialect/postgresql/test_types.py::NamedTypeTest::test_domain_type_reflection Failed: [undefined]AttributeError: 'INTEGER' object has no attribute 'name'
Traceback (most recent call last):
  File "/home/ovangle/dev/sqlalchemy/.venv/lib/python3.11/site-packages/_pytest/runner.py", line 341, in from_call
    result: Optional[TResult] = func()
                                ^^^^^^
  File "/home/ovangle/dev/sqlalchemy/.venv/lib/python3.11/site-packages/_pytest/runner.py", line 262, in <lambda>
    lambda: ihook(item=item, **kwds), when=when, reraise=reraise
            ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ovangle/dev/sqlalchemy/.venv/lib/python3.11/site-packages/pluggy/_hooks.py", line 493, in __call__
    return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ovangle/dev/sqlalchemy/.venv/lib/python3.11/site-packages/pluggy/_manager.py", line 115, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ovangle/dev/sqlalchemy/.venv/lib/python3.11/site-packages/pluggy/_callers.py", line 152, in _multicall
    return outcome.get_result()
           ^^^^^^^^^^^^^^^^^^^^
  File "/home/ovangle/dev/sqlalchemy/.venv/lib/python3.11/site-packages/pluggy/_result.py", line 114, in get_result
    raise exc.with_traceback(exc.__traceback__)
  File "/home/ovangle/dev/sqlalchemy/.venv/lib/python3.11/site-packages/pluggy/_callers.py", line 77, in _multicall
    res = hook_impl.function(*args)
          ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ovangle/dev/sqlalchemy/.venv/lib/python3.11/site-packages/_pytest/runner.py", line 177, in pytest_runtest_call
    raise e
  File "/home/ovangle/dev/sqlalchemy/.venv/lib/python3.11/site-packages/_pytest/runner.py", line 169, in pytest_runtest_call
    item.runtest()
  File "/home/ovangle/dev/sqlalchemy/.venv/lib/python3.11/site-packages/_pytest/python.py", line 1792, in runtest
    self.ihook.pytest_pyfunc_call(pyfuncitem=self)
  File "/home/ovangle/dev/sqlalchemy/.venv/lib/python3.11/site-packages/pluggy/_hooks.py", line 493, in __call__
    return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ovangle/dev/sqlalchemy/.venv/lib/python3.11/site-packages/pluggy/_manager.py", line 115, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ovangle/dev/sqlalchemy/.venv/lib/python3.11/site-packages/pluggy/_callers.py", line 113, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/home/ovangle/dev/sqlalchemy/.venv/lib/python3.11/site-packages/pluggy/_callers.py", line 77, in _multicall
    res = hook_impl.function(*args)
          ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ovangle/dev/sqlalchemy/.venv/lib/python3.11/site-packages/_pytest/python.py", line 194, in pytest_pyfunc_call
    result = testfunction(**testargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ovangle/dev/sqlalchemy/test/dialect/postgresql/test_types.py", line 1109, in test_domain_type_reflection
    eq_(t2.c.value.type.name, 'positive_int')
        ^^^^^^^^^^^^^^^^^^^^
AttributeError: 'INTEGER' object has no attribute 'name'

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    PRs (with tests!) welcomea fix or feature which is appropriate to be implemented by volunteerspostgresqlreflectionreflection of tables, columns, constraints, defaults, sequences, views, everything elseuse casenot really a feature or a bug; can be support for new DB features or user use cases not anticipated

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions