Limits & differences
Types
| Difference | Status | What to do |
|---|---|---|
NUMERIC precision is capped at 38 digits; NUMERIC(p>38) is refused, never narrowed. Bare NUMERIC becomes (38,9) with a NOTICE. | Refused42601 | Always declare NUMERIC(p,s) with p ≤ 38. |
INTEGER / INT are stored as 64-bit and introspect as BIGINT. | Supported | Declare BIGINT if you want the catalog to match. |
A function call inside VALUES (repeat(), upper(), …) is refused. Literals, typed literals, CURRENT_TIMESTAMP / now() and the default-expression functions are fine. | Refused42601 | Compute in the application, or INSERT … SELECT expr. Backlog 0.1.44. |
TIMESTAMP WITHOUT TIME ZONE is stored as timestamptz (UTC microseconds). | Accepted · NOTICE | Treat every timestamp as UTC. Convert at the edge. |
SET TimeZone changes nothing; the session is always UTC. | Accepted · NOTICE | Render in the application. A render-only session time zone is under consideration. |
No SERIAL sequences: SERIAL/BIGSERIAL/IDENTITY produce 64-bit generated ids, not a counter. No nextval / currval. | Supported | Read generated ids back with RETURNING. |
T[] and VECTOR(N) are advertised as text on the wire ({…} / […] literals), not a native array OID. | Partial | Parse the literal client-side; bound array parameters work (= ANY($1)). |
VECTOR without a dimension is refused. | Refused42601 | VECTOR(768). |
CHAR(n) is length-checked, not blank-padded. | Supported since 0.1.43 | Pad in the application if fixed width matters. |
Statements
| Refused or different | Status | What to do |
|---|---|---|
UPDATE / DELETE without WHERE | Refused42601 | WHERE TRUE to touch every row; TRUNCATE TABLE t to clear a table. |
ALTER TABLE … RENAME COLUMN / RENAME TO | Refused0A000 since 0.1.44 | Copy with CREATE TABLE … AS SELECT, then DROP. |
ALTER COLUMN other than SET DATA TYPE; narrowing retypes | Refused42703 | Recreate the table. |
DROP INDEX | Refused42601 | DROP TABLE __idx_<table>_<col> / __uidx_…. |
| Non-unique index on more than one column; index on an expression | Refused42601 | One index per column, or a UNIQUE index; materialise the expression. |
DROP TABLE a, b, TRUNCATE a, b, TRUNCATE … PARTITION (…) | Refused42601 | One table per statement. |
CREATE VIEW, CREATE MATERIALIZED VIEW, CREATE SEQUENCE | Refused0A000 since 0.1.44 | A teaching refusal names the alternative (CREATE RULE stays 42601). CREATE TABLE … AS SELECT and refresh it; ids come from SERIAL or GENERATE_SNOWFLAKE_ID(). |
LOCK TABLE, pg_advisory_lock() and friends | Refused42601 | Nothing to lock. Guard a row with SELECT … FOR UPDATE instead (pg_advisory_lock() fails as an unknown relation, 42P01). |
CREATE TRIGGER, CREATE EVENT TRIGGER, CREATE FUNCTION … RETURNS trigger | Refused0A000 since 0.1.43 | Validation → CHECK; reactions → an ingest rule + pipeline, or an Action. A plpgsql-subset trigger engine is on the roadmap (0.1.44 backlog). |
CHECK with a subquery, arithmetic or a function call | Refused0A000 | Comparisons of a column with a literal or column, AND/OR/NOT, IS [NOT] NULL, [NOT] IN (literals), [NOT] BETWEEN, typed DATE/TIMESTAMP literals. |
ON CONFLICT ON CONSTRAINT name | Refused42601 | Name the columns: ON CONFLICT (a, b). |
MERGE with no equality on a same-named column in ON and no primary key; USING other than a table, VALUES or SELECT; non-literal VALUES | Refused42601 | Add t.col = s.col; wrap the source in a SELECT. |
COPY … TO, COPY … FROM '<file>' | Refused42601 | COPY … FROM STDIN only; export with SELECT or the HTTP API. |
Schemas: a non-public qualifier is not resolved. | Refused42P01 | The bucket is the only namespace. public. is stripped; CREATE SCHEMA public and SET search_path are no-ops. |
pg_catalog queries beyond the emulated set; information_schema.table_constraints | Refused42P01 | Use information_schema.tables / .columns, DESCRIBE, SHOW TABLES. See Catalog. |
Transactions and concurrency
| Difference | Status | What to do |
|---|---|---|
SELECT … FOR UPDATE takes no lock and never blocks; it guards the rows read and fails COMMIT with 40001 on conflict. | Supported | Wrap the write in a retry-on-40001 loop. See Transactions. |
FOREIGN KEY is enforced on the insert side only (23503 on INSERT/UPSERT). Deleting a parent that has children succeeds; an autocommit UPDATE of an FK column, MERGE, and the gRPC WriteStream lane are not checked. Adding a constraint is not retroactive. | Partial | Own the delete side in the application: delete children first, or guard the parent with FOR UPDATE and check. |
ON DELETE / ON UPDATE CASCADE / SET NULL / SET DEFAULT, DEFERRABLE, INITIALLY DEFERRED on a foreign key. | Refused0A000 | Drop the option. Cascades are the application’s job. |
| The catalog does not list foreign keys back to ORMs. | Partial | Suppress FK diffs in migration autogenerate. |
SET TRANSACTION ISOLATION LEVEL changes nothing. | Accepted · no effect | The model is fixed: snapshot reads + commit guard. |
ON CONFLICT DO NOTHING inside an explicit transaction may report rowcount 1 for a row skipped at COMMIT. | Partial since 0.1.43 | Read the row after COMMIT if the count matters. Backlog 0.1.44. |
Interactive BEGIN over the HTTP/gRPC Execute API. | Refused | Send BEGIN; …; COMMIT; in one call (DML only), or use the Postgres wire. |
SELECT or DDL inside an Execute transaction block. | Refused | Issue them outside the block. The Postgres wire has no such limit. |
A semicolon-separated batch over Execute (without BEGIN/COMMIT). | Refused | Execute takes one statement per call: split the batch and issue the statements in order. The Postgres wire accepts multi-statement simple queries. |
| A strong read may be refused under a heavy write backlog rather than served partially. | Partial | Retry shortly; run compact after bulk loads. See Errors. |
Schema
| Difference | Status | What to do |
|---|---|---|
RESTORE TABLE discards every later write, and does not roll indexes back. | Supported | Rebuild indexes after a restore. |
Version history retention floor is 168 h; vacuum cannot go below it. | Supported | Plan restores within the window. |
CREATE TABLE … AS SELECT infers types (long, double, string, boolean); no primary key is declared. | Supported | Create the table with explicit columns first, then INSERT … SELECT. |
A table without a PRIMARY KEY has a hidden _rowid key, visible in DESCRIBE, hidden from Postgres metadata. | Supported | Declare a primary key for OLTP tables. |
TIMESTAMP(p) rounding is applied on SQL INSERT/UPDATE only — not on COPY FROM STDIN or the gRPC Upsert / WriteStream lanes (widths ARE enforced on every lane since 0.1.44). | Partial | Round timestamps before a bulk load. Backlog 0.1.45. |
Reserved words at, end, order, table, to, user, offset as bare identifiers. | Refused42601 | Quote them, or rename. |
Wire and session
| Difference | Status | What to do |
|---|---|---|
Server-side cursors are forward-only; SCROLL / FETCH BACKWARD are refused. | Partial | Re-declare the cursor. |
COPY … FROM STDIN WITH (FORMAT csv) does not accept embedded newlines inside quoted fields. | Partial | Use the text format, or strip newlines. |
LISTEN / NOTIFY, DEALLOCATE, DISCARD, RESET of unknown GUCs. | Accepted · no effect | Accepted, no effect. |
GUCs honoured: statement_timeout, hnsw.ef_search, ivfflat.probes, dodil.vector_exact_scan, dodil.vector_consistency. Every other SET is a no-op. | Partial | — |
A prepared read is executed once at Describe to learn its result schema. | Supported | Drivers that prepare aggressively issue more reads than statements. |
Extended-protocol Describe on a RETURNING DML answers with a zero-row probe. | Supported | — |
Ingest lanes
| Difference | Status | What to do |
|---|---|---|
| Pipeline-bound tables materialise their schema on first ingest, not at creation. | Supported | DESCRIBE after the first document lands. |
Items that were on this page and are now fixed are documented where they belong, marked with the release that changed them: exact NUMERIC division and 22012 (0.1.43), VARCHAR(n) / TIMESTAMP(p) enforcement (0.1.43), ON CONFLICT DO NOTHING first-wins (0.1.43), FOR UPDATE through a JOIN (0.1.43), online ADD COLUMN (0.1.42), read-your-writes inside a prepared-statement transaction (0.1.42), indexes on populated tables (0.1.41), concurrent duplicate INSERT → 23505 (0.1.41), cross-table transactions and read-your-writes inside a transaction (2026-07).