New: prepared statements, UPDATE and DELETE, and key lookups that seek — 0.143 ms through the gateway.Read the update

SQL and psql

UltraFabric speaks the PostgreSQL protocol through a gateway, ufgw. You log in with a token, and every statement becomes a plan that the data node verifies before it runs.

This is a first, deliberately small SQL subset. There is no public build or hosted endpoint yet. This page describes what runs in the repository's end-to-end tests today. Early access teams will get the first hosted databases.

How a connection works

  1. The client connects to ufgw and sends a token as its password.
  2. The gateway verifies the token: RS256 signature against a configured key set, issuer, audience ultrafabric, and expiry. A token signed with any other key is refused with 28P01.
  3. The gateway opens a connection to the data node, ufd, over its local socket. The node establishes the requester's identity itself; it does not take the gateway's word for it.
  4. Each statement is parsed by PostgreSQL's own parser, bound against the catalog and turned into a plan. The node's verifier refuses any plan that could read outside your tenant or your access policy.

Tokens travel in clear text inside the PostgreSQL password message, so the gateway accepts them only over TLS or from the same machine. Without a certificate it refuses to listen on anything but a loopback address. ufd itself never listens on the network.

A session

$ PGPASSWORD=$TOKEN psql -h 127.0.0.1 -p 45432 -U app -d uf

uf=> CREATE TABLE orders (id bigint PRIMARY KEY, cust int NOT NULL,
       status text, amount numeric(12,2), placed date);
uf=> INSERT INTO orders VALUES (1, 7, 'paid', 120.50, '2024-03-02');
uf=> SELECT status, count(*), sum(amount) FROM orders GROUP BY status ORDER BY status;
 status | count |     sum
--------+-------+-------------
 open   |  3310 | 16454322.57
 paid   |  3324 | 16582184.52
 void   |  3366 | 16970803.94
(3 rows)

Tenants

Every query reads only your tenant's rows; there is no tenant column to select, filter on or write. Primary keys are unique per tenant, so two tenants can both have an order 1. An UPDATE or DELETE changes only your tenant's rows.

The schema belongs to the application, not to a tenant: all tenants on a server share the same tables, and any logged-in tenant can see their names and columns, never their rows. Creating tables needs an operator credential, a separate kind of token for the application's operator. It can manage the schema but can never read or write tenant data, and an ordinary tenant token can never manage the schema.

What works today

AreaSupported
TablesCREATE TABLE [IF NOT EXISTS] with a primary key, NOT NULL, and WITH (storage = columnar) for column-oriented tables.
Typessmallint, int, bigint, real, double precision, numeric(p,s), text, boolean, uuid, date, timestamp, bytea.
WritesINSERT … VALUES with one or many rows; an existing primary key fails the whole statement with 23505. UPDATE … SET … WHERE and DELETE … WHERE, each applied as one atomic step. A primary key cannot be changed, and an UPDATE of a table with columns above your clearance is refused rather than risk overwriting them.
QueriesSELECT with WHERE, inner and left joins, comma joins, GROUP BY, count, sum, avg, min, max, ORDER BY, LIMIT and OFFSET. Lookups and ranges on the primary key read only the matching rows.
ExpressionsComparisons, AND/OR/NOT, IN, BETWEEN, IS [NOT] NULL, + - *, casts. Numbers follow PostgreSQL's typing: numeric(12,2) * 2 keeps two decimal places, and text compared with a number is an error.
ProtocolThe simple query protocol that psql uses, and prepared statements with $1 parameters, the extended protocol most drivers use by default, in text and binary formats. Parameter types are inferred as PostgreSQL infers them. The gateway allows 10 seconds to log in, small messages until the token is verified, and 200 connections by default.

The end-to-end test loads 10,000 rows for one tenant and 100 for another and checks that neither sees the other's rows. It compares five aggregate and filter queries with DuckDB on the same data, runs updates and deletes on one tenant and compares the result with DuckDB after the same statements, and repeats queries and inserts as prepared statements through the Go driver pgx. The answers are identical.

Not yet

Everything else fails at once with SQLSTATE 0A000 and names the missing feature. It is never silently approximated. The main gaps, in the order they are planned:

  • BEGIN/COMMIT, RETURNING and ON CONFLICT.
  • Subqueries, WITH, UNION, DISTINCT, HAVING, window functions, and SQL functions such as upper().
  • The pg_catalog tables that database GUIs and ORMs read when they connect.
  • Row-level policies. Today tenant isolation is the access boundary.
  • Division of real and double precision values, varchar(n) length limits and timestamp(p) precision.
  • Several statements in one query string when one of them writes, because there are no transactions yet to undo a half-applied string. Several reads in one string are fine.

Errors

SQLSTATEWhen
28P01The token was refused: bad signature, wrong issuer or audience, or expired.
0A000The statement uses something not supported yet.
23505The primary key already exists for your tenant.
42P01 / 42703No such table / no such column.
42883No operator for these types, for example text compared with a number.
42501Not allowed: creating a table without an operator credential, or an UPDATE of a table with columns above your clearance.
23502NULL into a NOT NULL column.
42P18Nothing in the statement says what type a $n parameter is (give it a cast, as in PostgreSQL).
22012 / 22003Division by zero / numeric overflow.
53200The query went over its memory budget.