Skip to content

fix(codegen): map postgres range columns to PgRange for oxide format - #25

Merged
Mattt (aevv) merged 5 commits into
oxide-codegenfrom
mattt/map-postgres-ranges-in-oxide-codegen
Aug 24, 2026
Merged

fix(codegen): map postgres range columns to PgRange for oxide format#25
Mattt (aevv) merged 5 commits into
oxide-codegenfrom
mattt/map-postgres-ranges-in-oxide-codegen

Conversation

@aevv

@aevv Mattt (aevv) commented Aug 23, 2026

Copy link
Copy Markdown

Range columns generate a struct that compiles and then fails to decode. sea-schema surfaces them as ColumnType::Custom, which write_rs_type renders as String — correct for the standard format, where get_col_type_attrs pairs it with select_as = "text", but the oxide format decodes straight into the model struct via sqlx::FromRow:

ColumnDecode { index: "range", source: "mismatched types; Rust type
  `Option<String>` (as SQL type `TEXT`) is not compatible with SQL type `NUMRANGE`" }

This is latent rather than live: bin_table_data.bin_range has been an int8range generating as Option<String> for a while, but oxide never hits it because ev_db hand-writes its own BinTableData with Option<PgRange<i64>> — plausibly because the generated one was unusable. This PR makes the generated type match that hand-written one, so the workaround can eventually go away.

All six range types now render as sqlx's PgRange, honouring --date-time-crate for the temporal ones.

Two constraints shape the output:

  • PgRange implements neither Serialize nor Deserialize, and serde has no impls for std::ops::Bound, so there is nothing to derive. Range fields carry #[serde(skip)]; skipping needs Default to deserialize, so they are always Option regardless of nullability. This matches existing practice — the hand-written BinTableData derives no serde at all — but it does mean a range is silently absent from JSON rather than failing loudly. If a consumer ever needs it serialized, the fix is to emit a representation here rather than to un-skip.
  • BigDecimal implements only PartialEq, so an entity holding a numrange cannot derive Eq. get_oxide_eq_needed handles that the way get_eq_needed already does for floats. Ranges over Eq element types keep the derive.

Verified by regenerating evervault/common-specs against a live Postgres and reading a real numrange row back into the generated struct.

The three pre-existing sea-orm-codegen --lib failures (test_get_info, test_get_rs_type_with_chrono, test_gen_postgres) are the Json mapping ones already noted in #24, unrelated to this change.

🤖 Generated with Claude Code

@aevv
Mattt (aevv) changed the base branch from oxide-codegen to mattt/emit-uuid-import-in-oxide-codegen August 23, 2026 11:07
@aevv Mattt (aevv) changed the title fix(codegen): make oxide entities compile and decode for uuid and range columns fix(codegen): map postgres range columns to PgRange for oxide format Aug 23, 2026
Base automatically changed from mattt/emit-uuid-import-in-oxide-codegen to oxide-codegen August 24, 2026 07:27
sea-schema surfaces range columns as ColumnType::Custom, which
write_rs_type renders as String. That is correct for the standard format,
where get_col_type_attrs pairs it with select_as = "text", but the oxide
format decodes rows straight into the model struct with sqlx::FromRow, so
a numrange column produced a struct that compiled and then failed at
runtime with "Rust type Option<String> is not compatible with SQL type
NUMRANGE".

Render the six range types as sqlx's PgRange instead, following the
element types sqlx implements and honouring --date-time-crate for the
temporal ones.

Two constraints shape the output. PgRange implements neither Serialize
nor Deserialize, so range fields carry #[serde(skip)]; skipping a field
requires Default to deserialize, so they are always Option regardless of
nullability. BigDecimal implements only PartialEq, so an entity holding a
numrange cannot derive Eq, which get_oxide_eq_needed now accounts for the
way get_eq_needed already does for floats.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@aevv
Mattt (aevv) force-pushed the mattt/map-postgres-ranges-in-oxide-codegen branch from 244c9c2 to aaee098 Compare August 24, 2026 07:27
Mattt (aevv) and others added 4 commits August 24, 2026 09:02
Only emit serde range attributes when the generated model derives serde, preserving database nullability otherwise. Add a focused PR workflow for building and testing codegen.
Keep direct SQLx and chrono types scoped to oxide generation so compact and expanded output continues to use SeaORM prelude aliases.
The oxide chrono arm collapsed Timestamp onto TimestampWithTimeZone, so a
`timestamp without time zone` column was rendered as DateTime<Utc>. sqlx
decodes TIMESTAMP only into NaiveDateTime and rejects the aware type at
runtime, with nothing to catch it at compile time.

The `time` arms were already grouping Timestamp with DateTime; this makes the
chrono arms agree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@aevv
Mattt (aevv) marked this pull request as ready for review August 24, 2026 09:11
@aevv
Mattt (aevv) merged commit 5729d74 into oxide-codegen Aug 24, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants