Postgres types
Ranges, network, geometry, search, named types, and other Postgres types.
These types are specific to Postgres.
Ranges
A range is empty, or a pair of bounds. A null bound is unbounded. lowerInclusive and upperInclusive choose [ or (.
| Builder | SQL | Bound | Bytes | Postgres |
|---|---|---|---|---|
t.int4range() | int4range | number, the integer range | variable | yes |
t.int8range() | int8range | string, a bigint | variable | yes |
t.numrange() | numrange | string, a numeric | variable | yes |
t.daterange() | daterange | Temporal.PlainDate | variable | yes |
t.tstzrange() | tstzrange | Temporal.Instant | variable | yes |
There is no tsrange builder. An instant range is t.tstzrange().
import { table, t } from "okmodel/pg";
export const plans = table("plans", {
id: t.identity(),
scores: t.int4range(),
active: t.tstzrange(),
});A bound is { empty: false, lower, upper, lowerInclusive, upperInclusive }. { empty: true } is the empty range.
Network
Values are canonical text.
| Builder | SQL | Value | Bytes | Postgres |
|---|---|---|---|---|
t.inet() | inet | An IPv4 or IPv6 host. A prefix is optional. | 7 or 19 | yes |
t.cidr() | cidr | An IPv4 or IPv6 network. A prefix is required. | 7 or 19 | yes |
t.macaddr() | macaddr | Six lowercase groups: aa:bb:cc:dd:ee:ff. | 6 | yes |
t.macaddr8() | macaddr8 | Eight lowercase groups. | 8 | yes |
7 bytes is IPv4. 19 bytes is IPv6.
An IPv4 prefix above 32, or any prefix above 128, is refused. t.cidr() with no prefix is refused.
import { table, t } from "okmodel/pg";
export const hosts = table("hosts", {
id: t.identity(),
address: t.inet(),
block: t.cidr(),
mac: t.macaddr(),
});Geometry
Built-in plane geometry. A point is { x, y }. A line is { a, b, c } for ax + by + c = 0.
| Builder | SQL | TypeScript | Bytes | Postgres |
|---|---|---|---|---|
t.point() | point | { x: number, y: number } | 16 | yes |
t.line() | line | { a: number, b: number, c: number } | 24 | yes |
box, circle, lseg, path, and polygon have no builder. PostGIS is not these types.
import { table, t } from "okmodel/pg";
export const places = table("places", {
id: t.identity(),
location: t.point(),
});Search
| Builder | SQL | TypeScript | Bytes | Postgres |
|---|---|---|---|---|
t.tsvector() | tsvector | string | variable | yes |
t.ltree() | ltree | string | variable | extension |
t.tsvector() stores the vector text. A generated vector is .generated(). t.ltree() is labels separated by dots, such as top.science. Each label is letters, digits, or underscores. The schema lists extension("ltree") from okmodel/ext. See extensions. There is no tsquery builder.
import { extension } from "okmodel/ext";
import { schema, table, t } from "okmodel/pg";
const notes = table("notes", {
id: t.identity(),
search: t.tsvector(),
path: t.ltree(),
});
export default schema({
tables: [notes],
extensions: [extension("ltree")],
});OKM1020
t.ltree() with no ltree extension on the schema is refused when the catalog is built.
Named types
| Builder | SQL | TypeScript | Bytes | Postgres |
|---|---|---|---|---|
t.enum(name, values) | the enum name | the union of the labels | 4 | yes |
t.domain(name, base, check) | the domain name | the base column's type | the base type | yes |
t.custom({ sqlType, encode, decode }) | sqlType | the type encode accepts | the SQL type | yes |
An enum is its own type. A picklist stays a string column and adds a CHECK.
t.domain("email", t.text(), "VALUE LIKE '%@%'") stores the check as written. The name and the check must be non-empty. An enum cannot be the base. Changing the base type of an existing domain is OKM1020. An enum or domain name with a capital letter, a space, or a quote is written as one quoted identifier wherever the type is used, so it never runs as SQL.
t.custom() stores a SQL type that has no builder. sqlType must be non-empty. accepts names the object kinds the codec takes, such as "Object". Omit accepts for a scalar. extension records an extension dependency. sqlForm defaults to "quote".
import { table, t } from "okmodel/pg";
export const notes = table("notes", {
id: t.identity(),
email: t.domain("email", t.text(), "VALUE LIKE '%@%'"),
label: t.custom({
sqlType: "text",
encode: (value: string) => value,
decode: (wire: string) => wire,
}),
});Other Postgres types
Postgres has these types. This dialect has no builder under that name. The Postgres cell stays yes, because the database has the type. The call is what you write instead.
| Postgres type | Bytes | Call | Postgres |
|---|---|---|---|
serial | 4 | t.integerIdentity() | yes |
bigserial | 8 | t.identity() | yes |
smallserial | 2 | t.smallint() | yes |
money | 8 | t.numeric(precision, scale) | yes |
decimal | variable | t.numeric() | yes |
tsrange | variable | t.tstzrange() | yes |
tsquery | variable | t.custom() with sqlType: "tsquery" | yes |
bit, bit varying | 1 byte per 8 bits, plus 5 or 8 | t.custom() with that SQL type | yes |
xml, hstore | variable | t.custom() with that SQL type | yes |
box, lseg | 32 | t.custom() with that SQL type | yes |
circle | 24 | t.custom() with that SQL type | yes |
path | 16 + 16 per point | t.custom() with that SQL type | yes |
polygon | 40 + 16 per point | t.custom() with that SQL type | yes |
oid | 4 | t.custom() with that SQL type | yes |
pg_lsn | 8 | t.custom() with that SQL type | yes |
| multirange | variable | t.custom() with that SQL type | yes |
Next: Columns.