Postgres type mapping¶
This page is generated from the type table in crates/rowx-postgres/src/types.rs, the single source of truth for the
runtime codecs and for rowx check. Regenerate it with uv run python scripts/gen-types-doc.py.
- Result type is the Python type a column decodes to, and the leaf your row type must use.
- Accepted parameters are the Python types that can be interpolated into a parameter of that type.
None(SQL NULL) is accepted for every parameter. - Encoding is the wire format used to send a parameter.
| Type key | Result type | Timezone | Accepted parameters | Encoding |
|---|---|---|---|---|
bool |
bool |
bool |
binary | |
int2 |
int |
int |
binary | |
int4 |
int |
int |
binary | |
int8 |
int |
int |
binary | |
oid |
int |
int |
binary | |
float4 |
float |
float, int |
binary | |
float8 |
float |
float, int |
binary | |
numeric |
decimal.Decimal |
Decimal, int |
text | |
text |
str |
str |
binary | |
varchar |
str |
str |
binary | |
bpchar |
str |
str |
binary | |
name |
str |
str |
binary | |
citext |
str |
str |
binary | |
char |
str |
str |
binary | |
bytea |
bytes |
bytes, bytearray, memoryview |
binary | |
date |
datetime.date |
date |
binary | |
time |
datetime.time |
naive | time |
binary |
timetz |
datetime.time |
aware | time |
text |
timestamp |
datetime.datetime |
naive | datetime |
binary |
timestamptz |
datetime.datetime |
aware | datetime |
binary |
interval |
datetime.timedelta; or rowx_postgres.Interval by annotation |
timedelta, Interval |
binary | |
uuid |
uuid.UUID |
UUID, str |
binary | |
inet |
ipaddress.IPv4Interface \| ipaddress.IPv6Interface |
IPv4Interface, IPv6Interface, IPv4Address, IPv6Address, str |
binary | |
cidr |
ipaddress.IPv4Network \| ipaddress.IPv6Network |
IPv4Network, IPv6Network, str |
binary | |
macaddr |
str |
str |
binary | |
macaddr8 |
str |
str |
binary | |
int4range |
rowx_postgres.Range[int] |
Range |
binary | |
int8range |
rowx_postgres.Range[int] |
Range |
binary | |
numrange |
rowx_postgres.Range[decimal.Decimal] |
Range |
binary | |
daterange |
rowx_postgres.Range[datetime.date] |
Range |
binary | |
tsrange |
rowx_postgres.Range[datetime.datetime] |
naive | Range |
binary |
tstzrange |
rowx_postgres.Range[datetime.datetime] |
aware | Range |
binary |
money |
rowx_postgres.Money |
Money |
binary | |
bit |
str |
str |
binary | |
varbit |
str |
str |
binary | |
json |
any value (json.loads) |
any JSON-serialisable value | text | |
jsonb |
any value (json.loads) |
any JSON-serialisable value | text | |
enum |
str |
str |
text | |
domain |
base type | base type | base type | |
array |
list[X] |
list, tuple |
binary | |
void |
None |
none | ||
point |
rowx_postgres.Point |
Point |
binary | |
line |
rowx_postgres.Line |
Line |
binary | |
lseg |
rowx_postgres.LineSegment |
LineSegment |
binary | |
box |
rowx_postgres.Box |
Box |
binary | |
path |
rowx_postgres.Path |
Path |
binary | |
polygon |
rowx_postgres.Polygon |
Polygon |
binary | |
circle |
rowx_postgres.Circle |
Circle |
binary | |
hstore |
dict[str, str \| None] |
dict |
binary | |
ltree |
str |
str |
binary | |
lquery |
str |
str |
binary | |
cube |
rowx_postgres.Cube |
Cube |
binary |
Notes¶
boolis never accepted for an integer parameter, anddatetimeis never accepted for adateparameter.- A
float4/float8parameter takes anint, but anintabove 2^53 loses precision. stris never treated as a sequence, so it cannot be bound to an array parameter.jsonandjsonbaccept any JSON-serialisable value, always encoded withjson.dumps. Astrbecomes a JSON string; to bind raw JSON text, write{s}::text::jsonb.- Timezone rules are checked when encoding: a naive
datetimefortimestamptz(or an aware one fortimestamp) raisesEncodeError. - A domain behaves as its base type.
- An array column decodes to
list[X](nested lists for multi-dimensional arrays).list[X]andlist[X | None]both match, at every nesting level. - Arrays and ranges are always sent in the binary format, whatever the format of their element or subtype is as a
scalar (a
numericparameter is text, anumeric[]ornumrangeparameter is binary), so a value can never change the element or bound structure. voiddecodes toNoneand is only valid as a tuple element.- A range decodes to
rowx_postgres.Range[T], withTthe leaf of its subtype, in the server's canonical form ([1,5]ofint4rangeis[1, 6)). ANonebound is an infinite side, andRange(empty=True)is the empty range. A user-defined range overint4,int8,numeric,date,timestamportimestamptzdecodes like the built-in range of that subtype and keeps whatever form its own canonical function gives it. intervaldecodes todatetime.timedelta, which raisesDecodeErrorfor a month component or ±infinity. Annotating the columnrowx_postgres.Intervalreads it losslessly asmonths,daysandmicrosecondsinstead, and ±infinity keeps the server's sentinel fields (the maximum or minimum of each). A parameter takes either class.moneydecodes torowx_postgres.Money(minor_units), the int8 the server sends. rowx holds no scale, becauselc_monetarycan change mid-session;Money.to_decimal(scale)takes it explicitly. To read the formatted text, cast the column totext.bitandvarbitdecode to astrof0and1. Abit(n)parameter of any other length is rejected by the server.- The geometric types decode to frozen dataclasses in
rowx_postgres:Point(x, y),Line(a, b, c),LineSegment(start, end),Box(upper_right, lower_left),Path(points, closed),Polygon(points)andCircle(x, y, radius), withpointsa tuple ofPoint. The server canonicalises aboxso its upper right corner comes first, and a decoded box shows that order. Equality compares the fields exactly, not with Postgres' epsilon-based~=, so a NaN coordinate is never equal to anything. Apathorpolygonneeds at least one point, and the server rejects alinewhoseaandbare both zero. hstoredecodes todict[str, str | None](a NULL value isNone; a key is never NULL, and aNonekey in a parameter raisesEncodeError).ltreeandlqueryarestr.cubeisrowx_postgres.Cube(lower_left, upper_right), twotuple[float, ...]of one length; the corners keep the order the server stores (the first corner written islower_left, not necessarily the minimum), a point has equal corners, and the codec sends equal corners as a point. Astrthat the server's parser rejects fails at Bind.- Extension types (
citext,hstore,ltree,lquery,cube) are recognised by name and owning extension, found throughpg_depend, so a same-named type from another schema or no extension isunsupported-typein the checker and raisesDecodeErrorat runtime (when the statement is prepared). The extension may live in any schema. - Any type not listed here is
unsupported-typein the checker and raisesDecodeErrorat runtime (for example multiranges, composites andxml).
Differences from psycopg¶
Decoding the same server value with rowx and with psycopg gives equal results, except for these cases. A test asserts that this list matches the one it checks.
timestamptz: rowx always returns a UTC-aware value, while psycopg uses the sessionTimeZone; the instants are equal.float4: rowx widens the exact 32-bit value to afloat, while psycopg parses the shortest decimal text; the values are equal after rounding to 32 bits.char: A non-ASCII"char"is cut to one byte by the server, which is not valid UTF-8, so rowx raisesDecodeError.interval: An interval with a month or year part raisesDecodeErrorunless the annotation isrowx_postgres.Interval, while psycopg approximates a month as 30 days.date: An infinite date raisesDecodeError, while psycopg raises its ownDataError.timestamp: An infinite timestamp raisesDecodeError, while psycopg raises its ownDataError.void:voiddecodes toNone, while psycopg returns an empty string.inet: rowx always returns an interface, even for a full-width mask, while psycopg returns an address for/32and/128; the interface'sipis that address.int4range: rowx returnsrowx_postgres.Range, while psycopg returns its ownRange; the bounds are equal.int8range: rowx returnsrowx_postgres.Range, while psycopg returns its ownRange; the bounds are equal.numrange: rowx returnsrowx_postgres.Range, while psycopg returns its ownRange; the bounds are equal.daterange: rowx returnsrowx_postgres.Range, while psycopg returns its ownRange; the bounds are equal, but an infinite bound of this subtype raisesDecodeError, while psycopg raises its ownDataError.tsrange: rowx returnsrowx_postgres.Range, while psycopg returns its ownRange; the bounds are equal, but an infinite bound of this subtype raisesDecodeError, while psycopg raises its ownDataError.tstzrange: rowx returnsrowx_postgres.Range, while psycopg returns its ownRange; the bounds are equal as instants, but an infinite bound of this subtype raisesDecodeError, while psycopg raises its ownDataError.money: rowx returnsrowx_postgres.Money, the int8 minor units with no scale, while psycopg returns the text the server formats underlc_monetary.point: rowx returnsrowx_postgres.Point, while psycopg returns the text the server formats; the coordinates are equal.line: rowx returnsrowx_postgres.Line, while psycopg returns the text the server formats; the coordinates are equal.lseg: rowx returnsrowx_postgres.LineSegment, while psycopg returns the text the server formats; the coordinates are equal.box: rowx returnsrowx_postgres.Box, while psycopg returns the text the server formats; the coordinates are equal.path: rowx returnsrowx_postgres.Path, while psycopg returns the text the server formats; the coordinates are equal.polygon: rowx returnsrowx_postgres.Polygon, while psycopg returns the text the server formats; the coordinates are equal.circle: rowx returnsrowx_postgres.Circle, while psycopg returns the text the server formats; the coordinates are equal.cube: rowx returnsrowx_postgres.Cube, while psycopg returns the text the server formats; the coordinates are equal.