pub enum DataType {
Show 71 variants
SmallInt,
Int,
BigInt,
Float,
Real,
Text,
Varchar(u32),
Char(u32),
Bool,
Vector {
dim: u32,
encoding: VecEncoding,
},
Numeric {
precision: u16,
scale: i16,
},
Date,
Timestamp,
Timestamptz,
Name,
Xid,
Xid8,
Oid,
Interval,
Json,
Jsonb,
Bytes,
TextArray,
IntArray,
BigIntArray,
OidArray,
IntervalArray,
BoolArray,
SmallIntArray,
FloatArray,
NumericArray,
DateArray,
TimestampArray,
TimestamptzArray,
UuidArray,
JsonArray,
JsonbArray,
BytesArray,
VarcharArray,
CharArray,
Multirange(RangeKind),
Point,
Lseg,
Path,
PgBox,
Polygon,
Line,
Circle,
Inet,
Cidr,
Macaddr,
Macaddr8,
PgLsn,
Bit(u32),
BitVarying(u32),
Xml,
Char1,
MoneyArray,
TsVector,
TsQuery,
Uuid,
Time,
Year,
TimeTz,
Money,
Range(RangeKind),
Hstore,
IntArray2D,
BigIntArray2D,
TextArray2D,
BoolArray2D,
}Expand description
Runtime type tags. Vector { dim, encoding } / Varchar(max) /
Char(size) are parameterised; the parameter travels with both
the column schema and the on-wire serialised representation.
Variants§
SmallInt
16-bit signed. Backed by Value::SmallInt(i16); arithmetic that
would overflow surfaces as a type error at INSERT time.
Int
BigInt
Float
Real
v7.38 (read01, T-float4) — real / float4: 32-bit IEEE float (PG
real). Backed by Value::Real(f32); behaves like Float for most
dispatch but renders / stores at f32 precision.
Text
Varchar(u32)
VARCHAR(n) — same byte representation as Text, but INSERT
rejects values longer than n Unicode characters.
Char(u32)
CHAR(n) — same representation as Text, but INSERT right-pads
with U+0020 to exactly n Unicode characters (or rejects when
the input is already longer).
Bool
Vector
pgvector-style fixed-dimension vector. encoding selects
the in-cell representation (F32 = pre-v6 raw f32 buffer;
Sq8 = v6.0.1 8-bit scalar-quantised). The DDL grammar
surfaces encoding via the optional USING <encoding>
clause: VECTOR(128) USING SQ8.
Numeric
NUMERIC(precision, scale) — exact fixed-point decimal stored as
a scaled i128. precision caps total decimal digits, scale
fixes digits after the decimal point. v1.12 supports up to
precision 38 (the i128-safe ceiling). NUMERIC and NUMERIC(p)
surface as Numeric { precision: p, scale: 0 }.
Fields
Date
DATE — calendar date with day precision, stored as i32 days
since the Unix epoch (1970-01-01).
Timestamp
TIMESTAMP (a.k.a. MySQL DATETIME) — instant with microsecond
precision, stored as i64 microseconds since the Unix epoch.
Timestamptz
v7.9.2 TIMESTAMPTZ — bit-identical to Timestamp on disk
(i64 microseconds, UTC by convention). Carried as a distinct
type tag so the PG-wire layer can advertise OID 1184 (PG’s
timestamp with time zone) and sqlx/pgx/JDBC clients
decode into their TZ-aware datetime types. The internal
semantics are unchanged: SPG never stored per-row offsets,
and neither did PG — TIMESTAMPTZ in PG is also UTC i64.
Name
v7.39 (round 291) — PG’s name: the type its catalogs use for
identifiers. Text truncated to NAMEDATALEN-1 (63) bytes, with
its own type identity — pg_typeof('abc'::name) is name, and
CREATE TABLE t (a name) is legal SQL that SPG rejected.
Xid
v7.39 (round 640) — PG’s xid: a transaction id. Value::Xid
has existed since round 512, so a '5'::xid literal already knew
what it was; this is the DECLARED half, which nothing had. Without
it pg_typeof(NULL::xid) answered bigint, pg_type could not
list oid 28 — leaving the 48 pg_attribute rows that describe
xmin / xmax pointing at a type no catalog carried — and
CREATE TABLE t (a xid) was refused as an unknown type.
On disk it is the 8-byte body its BIGINT sibling writes, and it
reads back as a Value::Xid, so a stored column and a literal are
the same thing to everything downstream.
What is NOT yet true of the identity: PG gives xid equality and
hashing and no ordering operator at all, so min / max /
count(DISTINCT …) / <= all error there and all answer here.
Measured, not assumed — and left for the operator surface rather
than claimed by this comment.
Xid8
v7.39 (round 640) — PG’s xid8: the same transaction id, 64 bits
wide and monotonic. Unlike DataType::Xid it has no value of
its own; a cell is a Value::BigInt and only the declared type
witnesses it. That is enough for pg_typeof, the catalogs and
the wire OID, and not enough to refuse a bigint where PG refuses
one. pg_current_xact_id() returns this type on PG.
Oid
v7.39 (round 667) — PG’s oid: an unsigned 32-bit object
identifier. Modelled exactly like DataType::Xid8 above: it has
no value of its own, a cell is a Value::BigInt, and only the
declared type witnesses it.
That deliberately buys less than a full value type. What it buys:
CREATE TABLE t(o OID) is accepted (it was rejected outright with
type "oid" does not exist, while the neighbouring XID worked),
pg_typeof answers oid rather than bigint, and the catalogs
report their own key columns honestly. What it does NOT buy is
refusing a bigint where PG refuses an oid — sum(oid) and
avg(oid) still answer here and error on PG, because at runtime
the cell is indistinguishable from a bigint. Round 664 tried to
close those two by name and withdrew: a guard keyed on the name
would have caught sum(bigint) with it.
The cast itself was already right before this — 4294967296::oid
and 'abc'::oid produce PG’s errors word for word, and (-1)::oid
wraps to 4294967295 as PG does. Only the resulting type was lost,
because conversions.rs mapped the target to BigInt.
Interval
INTERVAL — calendar-aware span (months + microseconds). v2.11
supports INTERVAL only as a runtime intermediate (literals,
arithmetic results); on-disk encoding is rejected so this branch
can’t appear in a ColumnSchema.
Json
v4.9: JSON — text-backed JSON document. We don’t parse
the content (no path operators or jsonb functions yet) —
the column accepts any TEXT-compatible value and round-trips
it verbatim. PG OID 114 on the wire.
Jsonb
v7.9.0: JSONB — semantically identical to Json on
the storage side (same Value::Json cells, same
row codec), but advertised as PG OID 3802 on the wire
so sqlx-style clients that bind jsonb columns
decode correctly. mailrs migration blocker #3.
Bytes
v7.10.4: BYTES / BYTEA — variable-length raw binary.
Backed by Value::Bytes(Vec<u8>). PG wire OID 17. Literal
forms accepted by parser/engine: PG hex form '\xDEADBEEF'
(case-insensitive hex pairs) and escape form
'foo\\000bar' (the latter decoded at coercion time when
the target column is BYTEA — TEXT columns leave the
backslash sequence verbatim).
TextArray
v7.10.9: TEXT[] — single-dimension TEXT array. Elements
may be NULL (PG semantics). PG wire OID 1009. Literal
forms: ARRAY['a', 'b', NULL] and the PG external form
'{a,b,NULL}'::TEXT[]. Engine implements = ANY(arr),
<> ALL(arr), and 1-based indexing arr[i]. Catalog
FILE_VERSION 18+; older snapshots reject this DataType
(forward-only by design — TEXT[] columns aren’t readable
on a pre-v7.10 binary).
IntArray
v7.11.12: INT[] — single-dimension i32 array. PG wire
OID 1007 (_int4). Same ARRAY[...] / '{1,2,3}'::INT[]
literal surface as TEXT[]. Catalog FILE_VERSION 19+.
BigIntArray
v7.11.12: BIGINT[] — single-dimension i64 array. PG
wire OID 1016 (_int8). Catalog FILE_VERSION 19+.
OidArray
v7.39 (round 694) — oid[]. It exists for the reason
DataType::Oid does: mapping it onto BigIntArray answers
pg_typeof('{1,2}'::oid[]) with bigint[], which is the defect
round 667 closed for the scalar.
IntervalArray
v7.37.5 β-P4 — INTERVAL[] — single-dimension array of
IntervalSpan { months, days, micros }. PG wire OID 1187
(_interval). Catalog tag 35 + per-cell body
[u16 count][per elem: u8 null + (if non-null) 16-byte interval body in LE PG-byte-equal field order].
FILE_VERSION 48+.
BoolArray
v7.37.5 γ — full PG array-of-scalar family. Catalog tags
36..48; wire OIDs from PG pg_type.dat. Per-element body
uses the scalar’s existing write_value_body shape.
FILE_VERSION 48+ (same window as β; no separate bump).
SmallIntArray
FloatArray
NumericArray
DateArray
TimestampArray
TimestamptzArray
UuidArray
JsonArray
JsonbArray
BytesArray
VarcharArray
CharArray
Multirange(RangeKind)
v7.37.5 δ — PG 14+ multirange types. A multirange is an
ordered collection of non-overlapping ranges of the same
element kind (e.g. int4multirange(int4range(1,5), int4range(10,15)) → {[1,5),[10,15)}). The same DataType
variant covers all six builtin multiranges; RangeKind
pins the element type so encode/decode/display can route
off one switch (parallel to Range(RangeKind)).
Wire OIDs: int4multirange=4451, int8multirange=4537,
nummultirange=4536, tsmultirange=4533, tstzmultirange=4534,
datemultirange=4535. Catalog tag 49 + 1-byte RangeKind on
the dense type-tag side. FILE_VERSION 48+ (same window as
β/γ, no separate bump).
Point
v7.37.5 ε — PG geometry scalar family. Mirrors PG’s seven
builtin geometric types one-for-one. Body shapes (LE):
Point = 16 B fixed (f64 x + f64 y) OID 600
Lseg = 32 B fixed (Point p1 + Point p2) OID 601
Path = varlena ([u8 closed][u32 n][Pointn]) OID 602
Box = 32 B fixed (Point ur + Point ll) OID 603
Polygon = varlena ([u32 n][Pointn]) OID 604
Line = 24 B fixed (f64 a + f64 b + f64 c) OID 628
Circle = 24 B fixed (Point center + f64 r) OID 718
Catalog tags 50..56. FILE_VERSION 48+ (same window as β/γ/δ;
no separate bump). Geometric operators (<-> / @> / &&
/ << / >> / ~=) are a planner-integration follow-up,
parallel to the Range operator defer in e2e_pg_range.rs.
Lseg
Path
PgBox
Polygon
Line
Circle
Inet
v7.37.5 ζ-A — PG network address family. Body shapes (LE):
Inet = 18 B fixed (u8 family + u8 bits + 16 B addr) OID 869
Cidr = 18 B fixed (same shape as Inet; CIDR rejects
host bits at parse / coerce) OID 650
Macaddr = 6 B fixed OID 829
Macaddr8 = 8 B fixed (EUI-64) OID 774
Catalog tags 57-60. FILE_VERSION 48+. family = 4 is IPv4
(uses the first 4 bytes of the 16-B addr slot, rest 0);
family = 6 is IPv6 (full 16 B).
Cidr
Macaddr
Macaddr8
PgLsn
v7.39 (read01 pg_lsn.c) — PG pg_lsn (WAL location). 8 bytes,
rendered %X/%X. Catalog tag 66. OID 3220.
Bit(u32)
v7.37.5 ζ-A — PG bit string. Body = [u32 nbits][ceil(nbits/8) bytes],
big-endian within each byte (matches PG binary).
Bit OID 1560 (fixed-length, but SPG carries the
length per cell — column declaration
BIT(n) constrains at coerce time)
BitVarying OID 1562 (variable-length, declared as VARBIT)
Catalog tags 61-62.
v7.39 (round 281) — BIT(n): a FIXED-length bit string. 0
means the type was written without a typmod, which PG treats as
bit(1). Column assignment requires the length to match
exactly; an explicit cast pads or truncates instead.
BitVarying(u32)
v7.39 (round 281) — BIT VARYING(n): n is a MAXIMUM, and 0
means unbounded (varbit with no typmod).
Xml
v7.37.5 ζ-A — PG xml. Body identical to TEXT (storage is
the verbatim XML string; no parse-time validation). Only
the wire OID (142) differs. Catalog tag 63.
Char1
v7.37.5 ζ-A — PG "char" (the internal single-byte type,
distinct from CHAR(n) / BPCHAR). Body = 1 byte raw.
OID 18. Catalog tag 64.
MoneyArray
v7.37.5 ζ-A — MONEY[]. Body = `[u16 count][per elem: u8 null
- (non-null) i64 LE cents]`. OID 791. Catalog tag 65.
TsVector
v7.12.0: PG tsvector — ordered, deduplicated set of
(lexeme, positions, weight) tuples. PG wire OID 3614.
Catalog FILE_VERSION 20+. Storage shape is row-codec
tag 22; the schema-agnostic write_value path emits tag
18. Literal: 'foo:1 bar:2,3'::tsvector (PG external
form). G-CRIT-3 entry — v7.12.0 only ships the type +
codec; matching @@ lands in v7.12.2.
TsQuery
v7.12.0: PG tsquery — parse tree of lexemes joined by
& | ! and phrase operators. PG wire OID 3615.
Catalog FILE_VERSION 20+.
Uuid
v7.17.0: PG uuid — 128-bit identifier stored as
Value::Uuid([u8; 16]). PG wire OID 2950. Canonical
text form is lowercase 8-4-4-4-12 hyphenated; input
also accepts uppercase, unhyphenated, and brace-wrapped
forms ({xxxx…}). Catalog FILE_VERSION 36+; tag 24 on
the dense type-tag side, tag 20 on the schema-agnostic
value side. The drop-in PG/MySQL surface for Django /
Rails / Hibernate “id UUID PRIMARY KEY DEFAULT
gen_random_uuid()” default-PK pattern.
Time
v7.17.0 Phase 3.P0-32: PG time (without time zone) — i64
microseconds since 00:00:00. PG wire OID 1083. Display:
canonical zero-padded HH:MM:SS when fractional is zero,
HH:MM:SS.ffffff otherwise. Catalog FILE_VERSION 37+;
tag 25 on the dense type-tag side, tag 21 on the schema-
agnostic value side. The wall-clock-of-day half of PG’s
date/time triplet (date / time / timestamp).
Year
v7.17.0 Phase 3.P0-33: MySQL YEAR — u16 in range
1901..=2155 plus the special zero-year sentinel 0. No
dedicated PG OID (advertised as INT4 / OID 23 on the wire
— psql renders integers, MySQL CLI renders 4-digit
zero-padded text). Display always 4 digits: 0000 for the
zero-year, 1985 / 2007 / etc otherwise. Catalog
FILE_VERSION 38+; tag 26 on the dense type-tag side, tag
22 on the schema-agnostic value side.
TimeTz
v7.17.0 Phase 3.P0-34: PG time with time zone (TIMETZ) —
i64 microseconds since 00:00:00 in the local wall clock
PLUS i32 offset-from-UTC in seconds. PG wire OID 1266.
Display: HH:MM:SS[.ffffff]±HH[:MM] (PG timetz_out).
Range: offset in ±50400 seconds (±14 hours). Catalog
FILE_VERSION 39+; tag 27 on the dense type-tag side, tag
23 on the schema-agnostic value side.
Money
v7.17.0 Phase 3.P0-35: PG money — i64 cents (locale-
independent storage). PG wire OID 790. Display: en_US
locale ($N,NNN.CC, negative → -$1.23). Input accepts
$N.NN, $N,NNN.NN, bare integer (treated as major
units), optional leading -. Range: full i64. Catalog
FILE_VERSION 40+; tag 28 on the dense type-tag side, tag
24 on the schema-agnostic value side.
Range(RangeKind)
v7.17.0 Phase 3.P0-38: PG range type. The same DataType
variant covers all six builtin ranges (int4range,
int8range, numrange, tsrange, tstzrange, daterange) —
RangeKind pins the element type so encode / decode /
display can route off one switch. Catalog FILE_VERSION
43+; tag 29 + a 1-byte RangeKind on the dense type-tag
side, tag 25 on the schema-agnostic value side.
Hstore
v7.17.0 Phase 3.P0-39: PG hstore extension type — flat
text => text map with NULL value support. Catalog
FILE_VERSION 44+; tag 30 on the dense type-tag side, tag
26 on the schema-agnostic value side. The contrib OID is
installation-dependent in real PG; SPG advertises it via
dynamic lookup, falling back to TEXT (OID 25) on the wire
when the installed hstore extension hasn’t claimed an
OID yet.
IntArray2D
v7.17.0 Phase 3.P0-40: PG int[][] — 2-dimensional INT
matrix. Storage: row-major Vec<Vec<Option
BigIntArray2D
v7.17.0 Phase 3.P0-40: PG bigint[][] — 2-dimensional
BIGINT matrix. Storage / OID / tags mirror IntArray2D.
Tag 32 dense, tag 28 schema-agnostic.
TextArray2D
v7.17.0 Phase 3.P0-40: PG text[][] — 2-dimensional TEXT
matrix. Storage: row-major Vec<Vec<Option
BoolArray2D
v7.39 (read01 round 75) — bool[][]. BOOL is the ONE element type whose
ARRAY rendering differs from its scalar one (t vs true), so a
text-backed 2-D cannot be PG-faithful for it: rendering the whole array
wants t, and subscripting a cell to text wants false. Every other
element type renders the same either way, which is why this is the only
typed 2-D variant SPG needs.