Skip to content
Discord Get Started

SQL Reference

DB9 implements a PostgreSQL-compatible SQL engine with pgwire protocol v3, a cost-based optimizer, and TiKV-backed distributed storage. Most PostgreSQL clients, ORMs, and drivers work without changes.

TaskWhere to look
Create tables, indexes, views, typesDDL
Insert, update, delete, upsertDML and Queries
JOINs, CTEs, window functions, subqueriesDML and Queries
Transactions, savepoints, isolation levelsTransactions
Data types and coercion rulesData Types
100+ built-in functionsBuilt-in Functions
PL/pgSQL, triggers, sequences, custom typesAdvanced SQL
Roles, users, grantsAuth and Roles
Session parameters and GUC settingsSession Parameters
Engine and extension limitsLimits
System catalog tablesSystem Catalog
  • Wire protocol — pgwire v3 with Simple Query and Extended Query (Parse/Bind/Describe/Execute)
  • DDL — CREATE/ALTER/DROP TABLE, indexes (btree, hash, GIN, HNSW), views, materialized views, schemas, sequences, functions, triggers, types, collations
  • DML — INSERT, UPDATE, DELETE with RETURNING; INSERT ON CONFLICT (upsert)
  • Queries — JOIN (inner, left, right, full outer, cross, lateral), CTEs (including recursive), window functions, subqueries (correlated, EXISTS, IN/ANY/ALL), set operations (UNION/INTERSECT/EXCEPT)
  • Transactions — BEGIN/COMMIT/ROLLBACK, savepoints, autocommit
  • Data types — boolean, integer, bigint, double precision, numeric, text, varchar, bytea, timestamp/timestamptz, date, time, interval, uuid, json/jsonb, arrays, serial/bigserial, vector, tsvector/tsquery
  • PL/pgSQL — functions, control flow, dynamic EXECUTE, EXCEPTION handlers and nested blocks, in a CREATE FUNCTION body, a CREATE PROCEDURE body run with CALL, and a DO block (WHILE, FOREACH and cursors are not supported — see Advanced SQL)
  • Triggers — BEFORE/AFTER on INSERT/UPDATE/DELETE, with gaps: a BEFORE trigger function cannot use counted FOR loops, bare LOOPs, CASE statements, EXECUTE, exception handlers or nested blocks (CREATE TRIGGER refuses it; a FOR r IN SELECT … LOOP query loop is allowed, over a declared record variable). FOR EACH STATEMENT, row-level WHEN (...) conditions, UPDATE OF column lists and event triggers are supported — see Advanced SQL
  • Indexes — btree (default), GIN, partial indexes, expression indexes, CREATE INDEX CONCURRENTLY. Hash indexes are built and used by the planner, on a single integer or OID column and not as a UNIQUE index. HNSW (vector) indexes are built and used by the planner (for a literal or bound-parameter probe, with a LIMIT and no WHERE clause), with structural requirements of their own — see pgvector. GiST, SP-GiST, and BRIN are rejected at CREATE INDEX time — see DDL — CREATE INDEX
FeatureDB9 behavior
SERIALIZABLE isolationNot implemented — downgraded to REPEATABLE READ with a warning on the wire protocol, rejected with an error over the HTTP SQL API.
SMALLINT / INT2Aliased to INTEGER (32-bit).
REAL / FLOAT4Aliased to DOUBLE PRECISION (64-bit).
CHAR(n) fixed-lengthAccepted but silently aliased to VARCHAR(n) with the same length. Padding semantics differ from standard PostgreSQL.
Timestamp precisionMicrosecond precision, matching PostgreSQL. TIMESTAMP(n) / TIMESTAMPTZ(n) / TIME(n) columns round to the declared precision on insert, update and ALTER COLUMN ... TYPE, and a cast such as ::timestamp(3) rounds the same way. TIMETZ / TIME WITH TIME ZONE has basic support only — see the compatibility matrix.
GIN index executionFunctional for a literal or bound-parameter probe — for @@, a tsquery value or a *_tsquery() call that names its configuration. The planner produces GIN scan plans and the executor uses the index — EXPLAIN ANALYZE reports KV Table Scan Pairs: 0. A probe computed by an expression falls back to a sequential scan.
Advisory lockspg_advisory_lock() family is supported, but lock coordination is node-local (not cross-process/global).
EncodingUTF-8 only. No other server or client encoding.
Logical replicationLogical decoding only: a wal2json slot returns the changes made to tables in a publication, plus the rows a CREATE TABLE ... AS or SELECT ... INTO loads, read with pg_logical_slot_get_changes(...) and format-version 2. DB9 cannot be a subscriber — CREATE SUBSCRIPTION is not available. Creating a slot turns on Realtime capture for the database permanently, after which ADD PRIMARY KEY on a non-empty table, dropping a non-empty table’s primary key, and ALTER TYPE ... RENAME VALUE on a label that stored rows use fail with 55000 — see the compatibility matrix.

These PostgreSQL features are not available in DB9:

  • Logical replication as a subscriber (CREATE SUBSCRIPTION); logical decoding is available, see above
  • Foreign data wrappers (postgres_fdw, etc.)
  • Tablespaces (all storage is TiKV-managed)
  • MERGE — rejected with 0A000 (MERGE is not supported); write upserts with INSERT ... ON CONFLICT

LISTEN / NOTIFY is supported, but only a pgwire session can receive — a session has to stay open. The stateless HTTP SQL API can NOTIFY, but a LISTEN there is accepted and never receives anything.

When you send a SQL query to DB9, it follows this pipeline:

  1. Parse — SQL text is parsed into an AST
  2. Route — Statement classified as DDL, DML, transaction control, or settings
  3. Expand views — View references inlined recursively (up to 64 levels)
  4. Analyze — Semantic analysis resolves names, infers types, checks scope
  5. Optimize — Cost-based optimizer (DPccp algorithm) chooses join order, access methods, and physical operators
  6. Execute — Pull-based (Volcano) operator tree streams results row by row
  7. Return — Results sent over pgwire

The optimizer uses table statistics from ANALYZE for cardinality estimation, with predicate pushdown and subquery decorrelation.

LimitValue
Statement timeout12 h (default)
Idle transaction timeout300s / 5 min (default)
View nesting depth64 levels
Pending portals per connection32
Timestamp precisionMicroseconds
EncodingUTF-8 only

See Limits for extension-specific limits (HTTP, fs9, pg_cron).

DB9 uses standard PostgreSQL connection strings:

Terminal
# psql
psql "postgresql://dbname.admin:password@pg.db9.io:5433/postgres"
# Or via the CLI
db9 db sql <db> -q "SELECT now()"

Any PostgreSQL client library works: psycopg2, node-postgres, JDBC, Go pgx, etc. See Connect for full details.

  • Data Types — 23+ supported types with coercion and casting rules
  • DDL — CREATE, ALTER, DROP for tables, indexes, views, schemas, types, triggers
  • DML and Queries — INSERT, UPDATE, DELETE, JOINs, CTEs, window functions, subqueries
  • Transactions — isolation levels, savepoints, COPY
  • Built-in Functions — string, math, date/time, JSON, array, aggregate, window, vector, FTS
  • System Catalog — pg_class, pg_attribute, information_schema, and more
  • Auth and Roles — users, roles, grants, and permissions
  • Advanced SQL — PL/pgSQL, triggers, sequences, custom types, collations
  • Session Parameters — SET/SHOW/RESET and GUC compatibility
  • Limits — engine, extension, and timing constraints
  • Connect — Connection strings, TLS, and driver configuration
  • Extensions — fs9, HTTP, vector, pg_cron, embedding, and more
  • Architecture — TiKV storage, pgwire, and query engine internals
  • CLI Reference — db9 db sql for terminal SQL execution
  • Compatibility Matrix — Full supported/unsupported feature matrix across SQL, types, indexes, ORMs, and extensions