can't adapt type for enum in case()

Offen
#12,097 13 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Maintainer antworten meist innerhalb von 1 Tag

Dieses Issue hat noch niemand übernommen.

Bewertung

Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Anfängerfreundlichkeit
35/100
Issue-Typ
Bug
Klarheit
Größtenteils klar
Aktivitätsstatus
Veraltet
Tech-Stack
postgresql, python
Bereich
databases

Rechercherichtung

Beginnen Sie mit dem Reproducer im Issue unter Verwendung von PostgreSQL und psycopg2. Untersuchen Sie anschließend, wie SQLAlchemy die Typen für die CASE-Bedingung und die Ergebniswerte ableitet, wenn sie einer Enum-Spalte zugeordnet werden. Überprüfen Sie das Verhalten mit dem gezeigten Update-Ausdruck und bestätigen Sie, dass äquivalente Enum-Werte ohne explizite Casts ausgeführt werden.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Beschreibung

documentation expected behavior law of twos question
Describe the bug

I can't use an enum type as part of a case() without casting.

Looks very similar to https://github.com/sqlalchemy/sqlalchemy/issues/6515

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

No response

SQLAlchemy Version in Use

2.0.36

DBAPI (i.e. the database driver)

psycopg2

Database Vendor and Major Version

PostgreSQL 12

Python Version

3.11

Operating system

macOS

To Reproduce
import enum

from sqlalchemy import case, create_engine, update, types as sqltypes
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, sessionmaker

Session = sessionmaker()


class Base(DeclarativeBase):
    pass


class State(enum.Enum):
    PENDING = enum.auto()
    RUNNING = enum.auto()
    CANCELLING = enum.auto()
    CANCELLED = enum.auto()
    
    
StateType = sqltypes.Enum(State)


class Job(Base):
    __tablename__ = "jobs"

    id: Mapped[int] = mapped_column(primary_key=True)
    state: Mapped[State] = mapped_column(StateType, server_default=State.PENDING.name)


def main(engine):
    with engine.connect() as conn:
        conn.execute(
            update(Job).values(
                state=case(
                    (
                        Job.state == State.PENDING,
                        State.CANCELLED,
                    ),
                    else_=State.CANCELLING,
                )
            )
        )


if __name__ == "__main__":
    engine = create_engine("postgresql:///issue12097", echo=True)
    Session.configure(bind=engine)
    Base.metadata.create_all(engine)
    try:
        main(engine)
    finally:
        Base.metadata.drop_all(engine)
Error
Traceback (most recent call last):
  File ".venv/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1967, in _exec_single_context
    self.dialect.do_execute(
  File ".venv/lib/python3.11/site-packages/sqlalchemy/engine/default.py", line 941, in do_execute
    cursor.execute(statement, parameters)
psycopg2.ProgrammingError: can't adapt type 'State'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "issue12096.py", line 50, in <module>
    main(engine)
  File "issue12096.py", line 32, in main
    conn.execute(
  File ".venv/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1418, in execute
    return meth(
           ^^^^^
  File ".venv/lib/python3.11/site-packages/sqlalchemy/sql/elements.py", line 515, in _execute_on_connection
    return connection._execute_clauseelement(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".venv/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1640, in _execute_clauseelement
    ret = self._execute_context(
          ^^^^^^^^^^^^^^^^^^^^^^
  File ".venv/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1846, in _execute_context
    return self._exec_single_context(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".venv/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1986, in _exec_single_context
    self._handle_dbapi_exception(
  File ".venv/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 2355, in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
  File ".venv/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1967, in _exec_single_context
    self.dialect.do_execute(
  File ".venv/lib/python3.11/site-packages/sqlalchemy/engine/default.py", line 941, in do_execute
    cursor.execute(statement, parameters)
sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) can't adapt type 'State'
[SQL: UPDATE jobs SET state=CASE WHEN (jobs.state = %(state_1)s) THEN %(param_1)s ELSE %(param_2)s END]
[parameters: {'state_1': 'PENDING', 'param_1': <State.CANCELLED: 4>, 'param_2': <State.CANCELLING: 3>}]
(Background on this error at: https://sqlalche.me/e/20/f405)

It works if I add casts:

            update(Job).values(
                state=case(
                    (
                        Job.state == cast(State.PENDING, StateType),
                        cast(State.CANCELLED, StateType),
                    ),
                    else_=cast(State.CANCELLING, StateType),
                )
            )
Additional context

mcve requires you to run createdb issue12097 first.

Vorherrschende Sprache
Python
Sterne
12.3k
Forks
1.8k
Ø Merge
2 T. 10 Std.
Gemergte PRs (30 T.)
9

Entwicklungsumgebung

Erste Schritte

  1. Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
  3. Forken Sie das Repository und arbeiten Sie in einem Branch.
  4. Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.

Mehr aus sqlalchemy/sqlalchemy

Alle Issues in sqlalchemy/sqlalchemy

Ähnliche Issues

Weitere Issues zu Python

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.