Shape

array() wraps a scalar. picklist() narrows a string column. enum() creates a Postgres type.

Where this fits

A column is one value until you wrap it. An array repeats a scalar, a picklist narrows text, and an enum is a Postgres type. Names are what the database stores.

An array repeats one scalar. A picklist keeps the column as text and checks the value. An enum is its own Postgres type.

Array

.array() wraps a scalar. The SQL type gains one [] per rank. There is no second .array() call to add a dimension.

An empty array is a value. An element cannot be null. .nullable() makes the whole column null, not an element. A picklist or an enum is wrapped after it is declared: .picklist(values).array() or t.enum(name, labels).array().

dimsResult
omittedRank 1. text[].
2 to 6That many []. 2 is integer[][].
outside 1 to 6Refused.

one

Omit dims for a list.

array.ts
import { connect } from "okmodel/pg/postgresjs";
import { schema, table, t } from "okmodel/pg";

const notes = table("notes", {
  id: t.identity(),
  tags: t.text().array(), 
});
const app = schema({ tables: [notes] });

export async function add(url: string) {
  await using db = connect(url, { schema: app });
  return db.notes.insert({ tags: ["ship"] });
}

rank

dims is the rank. 2 is a matrix.

matrix.ts
import { connect } from "okmodel/pg/postgresjs";
import { schema, table, t } from "okmodel/pg";

const notes = table("notes", {
  id: t.identity(),
  grid: t.integer().array({ dims: 2 }), 
});
const app = schema({ tables: [notes] });

export async function add(url: string) {
  await using db = connect(url, { schema: app });
  return db.notes.insert({ grid: [[1, 2]] });
}

OKM1060

A rank outside 1 to 6 is refused. Calling .array() on a column that is already an array is refused.

OKM1210

A value that is not an array is rejected, and so is a null element. A rank that does not match dims is rejected when the value is read back.

Picklist

.picklist(values) narrows a string column to that union. A default has to be one of the values. .picklist(values).default("open") and .default("open").picklist(values) both check it.

checkResult
omittedThe catalog CHECK is column IN (...), in the order you listed.
falseThe list stays in the type. The catalog has no CHECK. Encode still rejects a value outside the list.

check

The default writes the CHECK.

picklist.ts
import { connect } from "okmodel/pg/postgresjs";
import { schema, table, t } from "okmodel/pg";

const notes = table("notes", {
  id: t.identity(),
  status: t.text().picklist(["open", "done"]).default("open"), 
});
const app = schema({ tables: [notes] });

export async function close(url: string, id: string) {
  await using db = connect(url, { schema: app });
  return db.notes.update({ where: { id }, set: { status: "done" } });
}

type

{ check: false } keeps the union and skips the CHECK.

picklist-type.ts
import { table, t } from "okmodel/pg";

export const notes = table("notes", {
  id: t.identity(),
  status: t.text().picklist(["open", "done"], { check: false }), 
});

OKM1060

An empty list, an empty value, or a repeated value is refused. A default outside the list is refused. The message names the value.

OKM1210

A value outside the list is rejected when it is encoded. The message names the accepted values.

Enum

t.enum(name, labels) creates a Postgres enum. The column type is that name, not text. Labels are stored in that order. The TypeScript type is the union of the labels. Two columns may share the name when the label lists match.

A picklist stays a string column and adds a CHECK. An enum is its own type. Adding a label, with the labels you already have still in that order, plans ALTER TYPE … ADD VALUE, and that step runs on its own. Removing a label, or changing their order, replaces the type and rewrites each column that uses it.

enum.ts
import { connect } from "okmodel/pg/postgresjs";
import { schema, table, t } from "okmodel/pg";

const notes = table("notes", {
  id: t.identity(),
  status: t.enum("note_status", ["open", "done"]), 
});
const app = schema({ tables: [notes] });

export async function open(url: string) {
  await using db = connect(url, { schema: app });
  return db.notes.insert({ status: "open" });
}

OKM1060

An empty name, an empty list, an empty label, or a repeated label is refused.

OKM1210

A value outside the labels is rejected when it is encoded. The message names the accepted labels.

OKM1020

The same enum name with a different label list is refused. The message names both tables.