Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Type Conversions

How Rust native types round-trip through different SQL column types. Tested via entity insert + read-back (Table API).

Legend:

  • exact = lossless round-trip, value identical after insert + read
  • lossy = value stored but precision reduced (e.g. f64 rounding)
  • truncated = value stored but digits beyond column precision are cut
  • err = database rejects the insert or entity conversion fails on read

Chrono Types

MySQL

tests/mysql/1_chrono.rs

Column \ Rust typeNaiveDateNaiveTimeNaiveDateTimeDateTime<Utc>DateTime<FixedOffset>
VARCHARexactexactexactexactexact
DATEexacterrerrerr—
TIMEerrtruncating µserrerr—
TIME(6)errexacterrerr—
DATETIMEerrerrtruncating µstruncating µsoffset → +00:00
DATETIME(6)errerrexactexact—
TIMESTAMPerrerrtruncating µstruncating µsoffset → +00:00
TIMESTAMP(6)errerrexactexact—
  • Format: "2025-01-10 12:00:00" (space separator, no T); FixedOffset appends +05:30
  • TIME/DATETIME/TIMESTAMP default to 0 fractional digits — use (6) for microseconds
  • DateTime<FixedOffset>: VARCHAR preserves offset; DATETIME/TIMESTAMP normalize to UTC
  • MariaDB 11 rejects DateTime<FixedOffset> into DATETIME/TIMESTAMP (no offset literals) — use DateTime<Utc>
  • Cross-type coercions (e.g. NaiveTime → DATE) fail with variant mismatch

PostgreSQL

tests/postgres/1_chrono.rs

Column \ Rust typeNaiveDateNaiveTimeNaiveDateTimeDateTime<Utc>DateTime<FixedOffset>
VARCHARexactexactexactexactoffset → +00:00
DATEexacterrerrerr—
TIMEerrexacterrerr—
TIMESTAMPerrerrexactexactoffset → +00:00
TIMESTAMPTZerrerrexactexactoffset → +00:00
  • Format: "2025-01-10 12:00:00+00" (space separator, abbreviated tz offset)
  • Microsecond precision by default — no (6) suffix needed
  • Typed binds (chrono types, not text) required for DATE/TIME/TIMESTAMP/TIMESTAMPTZ
  • DateTime<FixedOffset>: offset always normalized to UTC (typed binds), no column preserves it
  • Cross-type coercions fail with variant mismatch

SQLite

tests/sqlite/1_chrono.rs

Column \ Rust typeNaiveDateNaiveTimeNaiveDateTimeDateTime<Utc>DateTime<FixedOffset>
TEXTexactexactexactexactexact
  • All dates stored as TEXT — format: ISO 8601 with T separator ("2025-01-10T12:00:00Z")
  • Subsecond precision and timezone offsets preserved as-is

Numeric Types

MySQL

tests/mysql/1_decimal.rs

Column \ Rust typeDecimali64f64
VARCHARexactexactexact
DECIMAL(20,6)truncated to 6 placeserrerr
DECIMAL(38,15)exacterrerr
DOUBLElossy (~15 digits)errexact
FLOATlossy (~7 digits)errlossy (~7 digits)
BIGINTfractional part lostexacterr
  • No cross-conversion between Integer, Float, and Decimal CBOR types
  • VARCHAR works for all types — from_cbor parses text as fallback

PostgreSQL

tests/postgres/1_decimal.rs

Column \ Rust typeDecimali64f64
VARCHARexactexactexact
NUMERIC(20,6)truncated to 6 placeserrerr
NUMERIC(38,15)exacterrerr
DOUBLE PRECISIONlossy (~15 digits)errexact
REALlossy (~7 digits)errlossy (~7 digits)
BIGINTfractional part lostexacterr
  • Typed binds (rust_decimal::Decimal) used for NUMERIC columns

SQLite

tests/sqlite/1_decimal.rs

Column \ Rust typeDecimali64f64
TEXTexactexactexact
NUMERIClossy (~15 digits, stored as REAL)exactexact
REALlossy (~15 digits)errexact
INTEGERfractional part lostexacterr
  • SQLite infers CBOR type from stored value, not declared column type
  • NUMERIC/REAL affinities coerce to float — i64 in REAL comes back as Float and fails
  • Store Decimal in TEXT for lossless precision