Skip to content

fix(query): auto-coerce Postgres ENUM columns to text on read (#26) - #29

Merged
turnerrainer merged 1 commit into
devfrom
fix/issue-26-pg-enum-text-coerce
Sep 9, 2026
Merged

fix(query): auto-coerce Postgres ENUM columns to text on read (#26)#29
turnerrainer merged 1 commit into
devfrom
fix/issue-26-pg-enum-text-coerce

Conversation

@turnerrainer

Copy link
Copy Markdown
Owner

Summary

Fixes #26. A bare SELECT status FROM t where status is a Postgres user-defined enum previously returned JSON null for the enum column, because sqlx's built-in String decoder isn't compatible with the enum's dynamic OID and every branch of pg_column_value fell through to the last-resort text probe (which also errors on unknown OIDs).

The row materialiser now inspects PgTypeInfo::kind() and, for PgTypeKind::Enum (and PgTypeKind::Array(Enum)), decodes via row.try_get_raw + <String as Decode<Postgres>>::decode directly. Postgres transmits enum values as UTF-8 labels in both text and binary formats, so no cast is required.

  • Author's preferred option (Option 1 in the issue).
  • No caller-visible break — the wire shape becomes what a hand-written ::text cast would have produced.

Schema changes

  • New Liquibase changeset 010-enum-tableticket_priority enum + tickets table with a scalar enum column and an enum-array column.
  • New test fixture fixtures-tickets — three rows exercising the scalar, mixed-array, and empty-array cases.

Existing operator DBs are unaffected: Liquibase is idempotent by changeset ID and the new changeset only adds a type + a table.

Test plan

  • cargo +1.88.0 fmt --all -- --check
  • cargo clippy --all-targets --locked -- -D warnings
  • SQLite-only suite (no Postgres) — 313 tests pass
  • Postgres integration suite — 67 tests pass (4 new)
    • pg_enum_column_selected_without_cast_returns_label_string
    • pg_enum_column_multi_row_labels_are_string
    • pg_enum_array_column_returns_array_of_labels
    • pg_empty_enum_array_returns_empty_array

🤖 Generated with Claude Code

sqlx's built-in `String` decoder isn't compatible with a user-defined
enum's dynamic OID, so `try_get::<Option<String>>` returned Err and
every branch of `pg_column_value` fell through to JSON `null` for
enum columns. Callers had to remember `::text` on every enum SELECT.

The row materialiser now inspects `PgTypeInfo::kind()` and decodes
`PgTypeKind::Enum` (and `PgTypeKind::Array(Enum)`) columns via their
raw wire form. Postgres transmits enum values as UTF-8 labels, so
this is safe and no cast is required.

Adds a Liquibase changeset for a `ticket_priority` enum + `tickets`
table so the Postgres integration suite covers scalar enum, multi-row
enum, enum array, and empty enum array.

Closes #26.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@turnerrainer
turnerrainer force-pushed the fix/issue-26-pg-enum-text-coerce branch from 4b76950 to 1bf03a1 Compare September 9, 2026 20:56
@turnerrainer
turnerrainer merged commit 79c447b into dev Sep 9, 2026
4 checks passed
@turnerrainer
turnerrainer deleted the fix/issue-26-pg-enum-text-coerce branch September 9, 2026 23:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SELECT of an ENUM column errors unless the query explicitly casts to ::text

1 participant