Postgres compatibility
Egres speaks the PostgreSQL wire protocol and parses SQL with Postgres’s own grammar (libpg_query), so every statement Postgres accepts parses. This page is a checklist of what executes in the current release. “Not yet” means exactly that: the list grows with each release.
Protocol and clients
Section titled “Protocol and clients”| Feature | Status | Notes |
|---|---|---|
| Simple query protocol | Yes | One statement per message. A message with several statements separated by ; runs the first one only. |
Extended query protocol (Parse, Bind, Describe, Execute, Close, Sync, Flush) |
Yes | Each session caches its parsed statements by SQL text. |
$1-style parameters |
Yes | In WHERE, SET and VALUES, up to $65535. |
| Parameters in text format | Yes | Any column type. |
| Parameters in binary format | Partial | bool, int2, int4, int8, float4, float8. |
Parameter type inference at Parse |
Not yet | The server echoes the OIDs the client sent. |
| Result columns in text format | Yes | |
| Result columns in binary format | Not yet | |
Portal row limits (Execute with a row count) |
Not yet | |
| Empty query | Yes | EmptyQueryResponse, as in Postgres. |
| TLS | Not yet | SSLRequest is answered with N; clients with sslmode=prefer or disable continue over plain TCP. |
| Authentication | Not yet | Every connection is accepted. |
CancelRequest |
Not yet | The request is accepted and ignored. |
pg_catalog, information_schema |
Not yet | psql’s backslash commands (\d, \dt, \l) depend on them. |
COPY |
Not yet | |
LISTEN / NOTIFY |
Not yet | |
Cursors, PREPARE as a statement |
Not yet | |
NoticeResponse |
Not yet |
The default SQL port is 3210. The database egres exists from the first boot, and a session that names no database lands in it. psql reports the server version as 13.0, the protocol version Egres speaks. The clients used in the quickstart and the benchmarks are psql and pgbench.
Statements
Section titled “Statements”| Statement | Status | Notes |
|---|---|---|
CREATE DATABASE name |
Yes | TEMPLATE source branches the source database. OWNER, ENCODING and the other options are accepted and ignored. |
DROP DATABASE name |
Yes | Except the default database egres. |
CREATE TABLE [IF NOT EXISTS] |
Partial | See Tables. |
CREATE TABLE dst AS TABLE src |
Yes | Branches src with its indexes. WITH NO DATA gives the definition and indexes, empty. AS SELECT * FROM src is the same statement. |
CREATE TABLE dst AS SELECT ... with a WHERE, column list or other clause |
Not yet | |
DROP TABLE [IF EXISTS] name |
Yes | One table per statement. |
CREATE [UNIQUE] INDEX [IF NOT EXISTS] |
Partial | See Indexes. |
DROP INDEX [IF EXISTS] name |
Yes | One index per statement. CONCURRENTLY is not needed: every index change is online. |
ALTER TABLE, ALTER INDEX, ALTER DATABASE |
Not yet | |
TRUNCATE |
Not yet | |
INSERT ... VALUES |
Yes | Multi-row VALUES and an explicit column list. |
INSERT ... SELECT, DEFAULT VALUES, DEFAULT as a value, ON CONFLICT, RETURNING |
Not yet | |
SELECT |
Partial | See Queries. |
UPDATE ... SET ... [WHERE] |
Yes | SET takes a literal, a parameter, a column, or + - * / % || over them. |
UPDATE ... FROM, RETURNING, WITH |
Not yet | |
DELETE FROM t [WHERE] |
Yes | Without WHERE it deletes every row. |
DELETE ... USING, RETURNING, WITH |
Not yet | |
BEGIN, START TRANSACTION, COMMIT, END, ROLLBACK |
Yes | Transaction modes such as ISOLATION LEVEL are accepted and ignored; isolation is snapshot isolation. |
SAVEPOINT, PREPARE TRANSACTION |
Not yet | |
| DDL inside a transaction block | Not yet | Returns 25001. Each DDL statement runs as its own transaction. |
SET, RESET |
Yes | Any parameter, accepted and ignored. |
SHOW |
Not yet | |
EXPLAIN, ANALYZE |
Not yet | |
VACUUM |
Not applicable | See No VACUUM. |
CREATE FUNCTION, CREATE PROCEDURE, DO (user-defined functions and procedures) |
Not yet | |
CREATE TRIGGER |
Not yet | |
CREATE VIEW, CREATE MATERIALIZED VIEW |
Not yet | |
CREATE SEQUENCE, nextval() |
Not yet | |
CREATE SCHEMA, CREATE EXTENSION |
Not yet | |
GRANT, REVOKE, CREATE ROLE |
Not yet |
Queries
Section titled “Queries”The shape a SELECT takes today:
SELECT column, ... | * | count(*)FROM one_table[WHERE predicate][FOR UPDATE][LIMIT n]| Clause | Status | Notes |
|---|---|---|
SELECT 1, constants with no FROM |
Yes | One row; a pool’s connection check. Aliases allowed. |
Column references and * in the target list |
Yes | The egid column can be selected by name. |
count(*) |
Yes | Alone in the target list. |
| Other aggregates, expressions, casts, aliases, functions in the target list | Not yet | |
One table in FROM, named directly |
Yes | |
JOIN, subqueries, VALUES in FROM, table aliases |
Not yet | |
WHERE |
Partial | See below. |
FOR UPDATE |
Yes | Locks every matching row for the rest of the transaction. Not with count(*). |
FOR SHARE, NOWAIT, SKIP LOCKED, FOR UPDATE OF |
Not yet | |
LIMIT n with an integer literal, LIMIT ALL |
Yes | |
LIMIT with a parameter or expression, OFFSET, FETCH ... WITH TIES |
Not yet | |
ORDER BY, GROUP BY, DISTINCT, HAVING |
Not yet |
Rows come back in egid order, which is insertion order, whether a scan or an index produced them.
| Form | Status | Notes |
|---|---|---|
column = literal |
Yes | A number, a string, a boolean or NULL. column = NULL matches nothing, as in Postgres. A literal that does not parse as the column’s type returns 22P02. |
column = $n |
Yes | |
column IS NULL, column IS NOT NULL |
Yes | |
AND, OR, NOT |
Yes | Nested to any depth. |
egid = N |
Yes | A single-hop point read; N is a positive integer. See Extensions. |
<, <=, >, >=, <> |
Not yet | |
BETWEEN, LIKE, ILIKE, IN (...), IS DISTINCT FROM |
Not yet | |
| A function or expression on either side, a bare boolean column, a column compared with a column | Not yet |
Any indexed column in the predicate drives the query through its bitmap index, and AND, OR and NOT over indexed columns become bitmap operations. A predicate on an unindexed column scans the table with the predicate applied to each row. A mixed predicate uses the indexed terms to pick a superset of the rows and re-checks the whole predicate on each. An index still being built is used once its backfill completes.
Tables
Section titled “Tables”CREATE TABLE [IF NOT EXISTS] name ( column type [PRIMARY KEY | UNIQUE], ...);| Feature | Status | Notes |
|---|---|---|
PRIMARY KEY on one column |
Yes | One per table. |
UNIQUE on one column |
Yes | NULL is allowed, and two NULLs do not collide, as in Postgres. |
NOT NULL, DEFAULT, CHECK, REFERENCES, GENERATED |
Not yet | |
Table-level constraints: PRIMARY KEY (a, b), UNIQUE (a, b), FOREIGN KEY |
Not yet | |
char(n), varchar(n) length modifiers |
Yes | Up to 10,485,760, as in Postgres. A bare char is char(1); a bare varchar is text. |
| Length modifiers on other types | Not yet | |
A table with no PRIMARY KEY |
Yes | Keyed by egid. |
NULL in the primary key column returns 23502. egid is a reserved column name. Table and index definitions live in reserved tables inside the database, so they branch with it.
Indexes
Section titled “Indexes”CREATE [UNIQUE] INDEX [IF NOT EXISTS] name ON table (column);| Feature | Status | Notes |
|---|---|---|
| Single-column index | Yes | One index per column, counting the index behind a PRIMARY KEY or UNIQUE column. |
UNIQUE index |
Yes | |
USING btree |
Yes | Accepted; every Egres index is a bitmap index. See Indexes. |
| Online creation | Yes | The index is backfilled while writes continue and becomes visible when the backfill completes, so CONCURRENTLY is not needed. |
| Composite, expression and partial indexes | Not yet | |
INCLUDE, DESC, NULLS FIRST, TABLESPACE |
Not yet | |
Other access methods (hash, gin, gist, brin) |
Not yet | |
Indexes on interval columns |
Not yet | As a primary key, a unique column or by CREATE INDEX. |
| Dropping the primary key’s index | Not yet |
Data types
Section titled “Data types”| Postgres type | Status | Notes |
|---|---|---|
boolean |
Yes | Literals t, true, 1, yes, on and their opposites. |
smallint, integer, bigint |
Yes | |
real, double precision |
Yes | |
text, varchar, varchar(n), char(n) |
Yes | Values are valid UTF-8 (22021 otherwise); a value longer than n returns 22001. |
bytea |
Yes | |
date, timestamp, timestamptz |
Yes | |
time, interval, uuid |
Partial | The column can be declared and holds NULL. Writing a value into it or comparing against it: not yet (0A000). |
time with time zone |
Not yet | |
numeric, decimal |
Not yet | |
serial, bigserial |
Not yet | egid gives an insertion-ordered id today. |
json, jsonb |
Not yet | |
Arrays, enum, composite, range, money, inet, bit, tsvector, geometric types |
Not yet |
Assignments in SET follow Postgres: any integer into any numeric column with a range check (22003 on overflow), floats into floats, strings into strings. A float into an integer column returns 42804. Division by zero returns 22012.
Functions and operators
Section titled “Functions and operators”| Feature | Status | Notes |
|---|---|---|
count(*) |
Yes | Alone in the target list. |
CURRENT_TIMESTAMP, LOCALTIMESTAMP |
Partial | In INSERT ... VALUES, into a timestamp or timestamptz column; the transaction’s start time, as in Postgres. In WHERE or SET: not yet. |
+, -, *, /, % on numeric types, unary minus |
Yes | In UPDATE ... SET, over literals, parameters and columns of the same row. |
|| on strings |
Yes | In UPDATE ... SET. |
now(), casts, string and date functions, COALESCE, CASE, every other function and operator |
Not yet | |
| User-defined functions, in SQL or any other language | Not yet |
Extensions
Section titled “Extensions”Things Egres accepts that Postgres does not.
egid. Every row has a 64-bit id, assigned at insert and increasing within the table.SELECT egid, ...reads it andWHERE egid = Nis a single-hop point read. It is assigned by the server, so an explicit value inINSERTorUPDATEis not accepted. It rides the wire asnumeric.- Unsigned integers.
uint2,uint4anduint8are column types. Clients seeuint2asint4,uint4asint8anduint8asnumeric. CREATE DATABASE new TEMPLATE srcbranches a database instantly instead of copying its files, andCREATE TABLE dst AS TABLE srcbranches a table with its indexes instead of copying rows without them. See Branching.
Behaviour today
Section titled “Behaviour today”- Isolation is snapshot isolation. Reads never block. A statement that locks a row committed after its snapshot re-runs itself at the newer snapshot; in a multi-statement transaction that may instead fail with
40001, the same serialization failure Postgres returns. See Transactions. - Deadlocks are detected at lock time. The lock request that would close a cycle fails immediately with
40P01, and that transaction’s locks are released. There is nodeadlock_timeoutand nolock_timeout. - Writes run on the Raft leader. This release runs a single node, which is always the leader. Multi-node clusters come in a later release.
- A failed statement fails the transaction, as in Postgres: only
COMMITandROLLBACKare accepted until the block ends (25P02otherwise), and both roll back. - A read below the retention floor returns
72000, snapshot too old. See Garbage collection.