Skip to content

ComplexHelper registers enums/composites connection-globally: later plain SELECTs return Enum members instead of labels #54

Closed

Description

@aersam

Found during the MDMApp rollout (details also in a comment on #51). Affects pgdevkit 0.15.0 (and earlier versions that registered types via ComplexHelper).

pg_retrieve / pg_retrieve_many (and the other pg_* helpers) with a ComplexHelper register every enum/composite type of the table on the connection (load_all_complex_types -> _recurse_register -> register_enum(info, con) / register_composite). The registration is global for that connection and sticks to it in the pool, even though the read path wraps complex columns in to_jsonb(...) and doesn't need any registered type. Afterwards any plain SELECT of that enum type on the same connection returns psycopg's auto-numbered Enum member instead of the label:

con = await psycopg.AsyncConnection.connect(...)
(await (await con.execute("select 'idle'::imp.delta_compute_status")).fetchone())   # ('idle',)
await pg_retrieve_many(con, M, {}, limit=1, complex_helper=ComplexHelper(con))      # M maps imp.file_import
(await (await con.execute("select 'idle'::imp.delta_compute_status")).fetchone())   # (<Delta_Compute_Status.idle: 1>,)

In MDMApp this made unrelated endpoints/tests return 3 instead of "done" depending on pool order (3 tests failed in the full akeneo_editor suite, all passing in isolation), so MDMApp could not adopt pg_retrieve* and kept its own. Because #52 added complex_helper="auto" (and caches the registration per connection), the blast radius is now every pg_* call on any table with an enum/composite column: other code on the same pooled connection silently sees different values.

Suggested fix (pick one, with a regression test like the snippet above):

  • Don't register types on the read path that already goes through to_jsonb(...); register only where a Python value must be converted for a write.
  • Or register enums so they keep returning labels (a str-valued enum via register_enum(info, con, enum=<StrEnum>)), or not at all (the default text loader already returns labels).
  • Either way, document that register_* is connection-global.

Until fixed: apps using complex_helper= / "auto" on tables with enum columns, on a connection that is also used for plain reads of those enum types, may see enum members instead of strings.

Claude Session: dd3520fe-363b-5088-ab76-5e87fad68be0

Activity

  1. added
    bugSomething isn't working
    pr-availableA PR exists that addresses this issue
    on Oct 9, 2026
  2. aersam commented on Oct 9, 2026

    @aersam
    ContributorAuthor

    PR #55 is ready for review (CI green, version 0.15.1, also fixes an enum[]-inside-composite crash).

    ComplexHelper no longer registers anything on the connection: writes register enum/composite adapters on the cursor that runs the statement only (new ComplexHelper.register_on), reads and RETURNING use to_jsonb. A plain SELECT of an enum/composite on the same (pooled) connection behaves exactly as before any pg_* call. Decisions for a human: RETURNING of pg_insert/pg_update_dict/pg_upsert_dict now yields plain values (dict/label) instead of namedtuples/Enum members; direct recursive_convert users must register on their cursor; ComplexHelper.registered kept as a deprecated shim.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpr-availableA PR exists that addresses this issue

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions