Skip to content

fix(introspect): decode composite field ordinal as i16 - #5

Merged
LeadcodeDev merged 1 commit into
mainfrom
fix/composite-type-attnum-int2
Aug 1, 2026
Merged

fix(introspect): decode composite field ordinal as i16#5
LeadcodeDev merged 1 commit into
mainfrom
fix/composite-type-attnum-int2

Conversation

@LeadcodeDev

@LeadcodeDev LeadcodeDev commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Fixes #4.

The bug

fetch_composite_types reads the composite field ordinal from pg_attribute.attnum, which is a smallint in the Postgres catalog, but declared the decode target as i32. sqlx applies no implicit widening, so introspection died on the 6th column:

Error: Database(ColumnDecode { index: "5", source:
  "mismatched types; Rust type `i32` (as SQL type `INT4`)
   is not compatible with SQL type `INT2`" })

Confirmed against Postgres 16:

 attname  | sql_type
----------+----------
 attnum   | smallint

The other ordinal reads in this module go through information_schema.columns.ordinal_position, whose cardinal_number domain genuinely is an int4 — those were always correct. Only the pg_catalog query was wrong.

Why it went unnoticed

The query filters on t.typtype = 'c' while excluding the implicit row types that back tables. A schema with no standalone CREATE TYPE ... AS (..) returns zero rows, so nothing is ever decoded and the mismatch stays invisible. It only fires for users who declare real composite types.

The fix

Decode as i16, widen with i32::from at the assignment. Mirroring the catalog's actual type keeps the divergence from information_schema visible at the call site, which the added comment explains.

Tests

CI's test-postgres job already runs cargo test --all-features --test e2e_postgres -- --include-ignored, but tests/e2e_postgres.rs never existed — under continue-on-error: true the job has been silently passing over nothing. That gap is what let this ship. This PR adds the file:

  • test_composite_type_is_introspected — the regression; fails on main with the exact error above, passes with the fix
  • test_table_row_type_is_not_a_composite — guards the implicit-row-type exclusion
  • test_simple_table_generates_struct, test_enum_is_introspected — basic coverage so the target is worth running

Each test works in its own schema so they stay parallel-safe.

Verification

cargo test --all --all-features          # 657 + 42 pass, 4 pg tests ignored w/o PG_URL
PG_URL=... cargo test --test e2e_postgres -- --include-ignored   # 4 passed
cargo fmt --all -- --check               # clean
cargo clippy --all-targets --all-features -- -D warnings   # clean

End-to-end against a live Postgres 16 with CREATE TYPE address AS (street TEXT, city TEXT, zip TEXT), the CLI path from the issue now succeeds:

INFO sqlx_gen] Found 1 tables, 0 views, 1 enums, 1 composite types, 0 domains

#[derive(Debug, Clone, PartialEq, Eq, ..., sqlx::Type, SqlxGen)]
#[sqlx_gen(kind = "composite")]
#[sqlx(type_name = "address")]
pub struct Address {
    pub street: Option<String>,
    pub city: Option<String>,
    pub zip: Option<String>,
}

Follow-ups (not in this PR)

  • No version bump here — this repo bumps in separate chore: commits.
  • continue-on-error: true on the test-postgres and test-mysql jobs still hides real failures.
  • tests/e2e_mysql.rs is referenced by CI too and looks equally absent (unverified).

`pg_attribute.attnum` is a smallint, but `fetch_composite_types` decoded
it as `i32`. sqlx applies no implicit widening, so introspecting any
schema that declares a standalone composite type died with:

    ColumnDecode { index: "5", source: "mismatched types; Rust type
    `i32` (as SQL type `INT4`) is not compatible with SQL type `INT2`" }

The other ordinal reads in this module go through
`information_schema.columns.ordinal_position`, whose `cardinal_number`
domain really is an int4 — only the pg_catalog query was wrong.

Schemas with no standalone composite type return zero rows and never
decode anything, which is why this stayed hidden.

Also adds the `e2e_postgres` test target that the `test-postgres` CI job
already invokes but which never existed, so the job was silently passing
over nothing. It covers the composite regression plus basic table and
enum introspection.

Fixes #4
@LeadcodeDev
LeadcodeDev force-pushed the fix/composite-type-attnum-int2 branch from 5e8c5f1 to 3dd06ac Compare August 1, 2026 14:19
@LeadcodeDev LeadcodeDev self-assigned this Aug 1, 2026
@LeadcodeDev LeadcodeDev added the bug Something isn't working label Aug 1, 2026
@LeadcodeDev
LeadcodeDev merged commit 9bafb5a into main Aug 1, 2026
5 checks passed
@LeadcodeDev
LeadcodeDev deleted the fix/composite-type-attnum-int2 branch August 1, 2026 14:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

1 participant