@crvouga/postgres-mem 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +159 -0
- package/COMPATIBILITY-AUDIT.md +155 -0
- package/COMPATIBILITY.md +87 -0
- package/LICENSE +21 -0
- package/README.md +325 -0
- package/compat/coverage.json +1081 -0
- package/compat/divergences.json +191 -0
- package/compat/gate-report.json +80 -0
- package/compat/requirements.json +1105 -0
- package/compat/requirements.raw.html +484 -0
- package/compat/scenario-types.ts +98 -0
- package/compat/scenarios.ts +98 -0
- package/compat/sections/agg.ts +45 -0
- package/compat/sections/api.ts +28 -0
- package/compat/sections/arr.ts +45 -0
- package/compat/sections/cat.ts +47 -0
- package/compat/sections/con.ts +32 -0
- package/compat/sections/cpy.ts +24 -0
- package/compat/sections/cte.ts +29 -0
- package/compat/sections/dat.ts +60 -0
- package/compat/sections/ddl.ts +55 -0
- package/compat/sections/det.ts +20 -0
- package/compat/sections/dml.ts +37 -0
- package/compat/sections/eco.ts +16 -0
- package/compat/sections/err.ts +16 -0
- package/compat/sections/exp.ts +53 -0
- package/compat/sections/fun.ts +58 -0
- package/compat/sections/fzz.ts +36 -0
- package/compat/sections/guc.ts +27 -0
- package/compat/sections/joi.ts +33 -0
- package/compat/sections/jsn.ts +46 -0
- package/compat/sections/lim.ts +13 -0
- package/compat/sections/par.ts +61 -0
- package/compat/sections/pre.ts +26 -0
- package/compat/sections/sch.ts +31 -0
- package/compat/sections/sel.ts +43 -0
- package/compat/sections/seq.ts +31 -0
- package/compat/sections/snp.ts +21 -0
- package/compat/sections/tok.ts +53 -0
- package/compat/sections/trg.ts +57 -0
- package/compat/sections/tsr.ts +37 -0
- package/compat/sections/txn.ts +39 -0
- package/compat/sections/typ.ts +55 -0
- package/compat/sections/uni.ts +13 -0
- package/compat/sections/win.ts +43 -0
- package/compat/smoke-baseline.json +3 -0
- package/compat/unsupported-register.json +2526 -0
- package/dist/api/bind.d.ts +10 -0
- package/dist/api/database.d.ts +69 -0
- package/dist/api/statement.d.ts +51 -0
- package/dist/ast/nodes.d.ts +810 -0
- package/dist/constraints/enforce.d.ts +39 -0
- package/dist/errors/error.d.ts +24 -0
- package/dist/executor/ddl.d.ts +23 -0
- package/dist/executor/dml.d.ts +10 -0
- package/dist/executor/execute.d.ts +11 -0
- package/dist/executor/relation.d.ts +60 -0
- package/dist/executor/select.d.ts +41 -0
- package/dist/executor/session.d.ts +21 -0
- package/dist/executor/triggers-exec.d.ts +1 -0
- package/dist/executor/triggers.d.ts +16 -0
- package/dist/executor/window.d.ts +13 -0
- package/dist/expressions/context.d.ts +23 -0
- package/dist/expressions/eval.d.ts +38 -0
- package/dist/expressions/operators.d.ts +8 -0
- package/dist/expressions/pattern.d.ts +20 -0
- package/dist/functions/aggregates.d.ts +17 -0
- package/dist/functions/array-fns.d.ts +2 -0
- package/dist/functions/datetime-fns.d.ts +26 -0
- package/dist/functions/datetime-registry.d.ts +2 -0
- package/dist/functions/json-fns.d.ts +7 -0
- package/dist/functions/math-fns.d.ts +2 -0
- package/dist/functions/misc-fns.d.ts +8 -0
- package/dist/functions/scalar.d.ts +10 -0
- package/dist/functions/srf.d.ts +12 -0
- package/dist/functions/string-fns.d.ts +4 -0
- package/dist/functions/tsearch-fns.d.ts +2 -0
- package/dist/functions/util.d.ts +14 -0
- package/dist/functions/window.d.ts +7 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +19591 -0
- package/dist/index.js.map +7 -0
- package/dist/lexer/tokenize.d.ts +8 -0
- package/dist/parser/index.d.ts +1 -0
- package/dist/parser/parser.d.ts +96 -0
- package/dist/runtime/clock.d.ts +14 -0
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/options.d.ts +25 -0
- package/dist/runtime/prng.d.ts +48 -0
- package/dist/schema/catalog-tables.d.ts +1 -0
- package/dist/schema/catalog.d.ts +12 -0
- package/dist/serialization/codec.d.ts +29 -0
- package/dist/serialization/index.d.ts +1 -0
- package/dist/storage/database-state.d.ts +216 -0
- package/dist/transactions/manager.d.ts +25 -0
- package/dist/tsearch/stem.d.ts +5 -0
- package/dist/tsearch/tsearch.d.ts +59 -0
- package/dist/types/cast.d.ts +26 -0
- package/dist/types/compare.d.ts +18 -0
- package/dist/types/datetime.d.ts +68 -0
- package/dist/types/jsonb.d.ts +45 -0
- package/dist/types/numeric.d.ts +69 -0
- package/dist/types/resolve.d.ts +15 -0
- package/dist/types/timezone.d.ts +9 -0
- package/dist/types/value.d.ts +99 -0
- package/dist/unstable.d.ts +15 -0
- package/dist/unstable.js +19153 -0
- package/dist/unstable.js.map +7 -0
- package/package.json +120 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# AGENTS.md — contributing to postgres-mem
|
|
2
|
+
|
|
3
|
+
Guidance for humans and coding agents editing this repository. For install and consumer API, see [README.md](README.md). For the feature matrix, see [COMPATIBILITY.md](COMPATIBILITY.md).
|
|
4
|
+
|
|
5
|
+
## Mission and non-goals
|
|
6
|
+
|
|
7
|
+
**Mission:** PostgreSQL 18 **SQL dialect** behavioral parity vs **PostgreSQL 18.3** (PGlite, `@electric-sql/pglite`). Same statements → same observable results (rows, errors, SQLSTATEs, row counts), proven by differential contracts and a fail-closed gate.
|
|
8
|
+
|
|
9
|
+
**Allowed intentional differences:**
|
|
10
|
+
|
|
11
|
+
1. Custom snapshot codec (`PGMM`), not `pg_dump` / on-disk clusters
|
|
12
|
+
2. Deterministic `random()` / `gen_random_uuid()` and fixed `now()` by default (injectable)
|
|
13
|
+
3. Single-session engine: no MVCC across connections, no wire protocol, no aborted-transaction (`25P02`) state
|
|
14
|
+
4. `NOT APPLICABLE` items: roles/auth enforcement, replication, VACUUM internals, storage params, PL/pgSQL
|
|
15
|
+
|
|
16
|
+
**Non-goals:** speaking the wire protocol, matching `pg`/`postgres.js` client APIs, PL/pgSQL, or multi-session concurrency.
|
|
17
|
+
|
|
18
|
+
## SQL pipeline
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
SQL string
|
|
22
|
+
→ tokenize() src/lexer/tokenize.ts
|
|
23
|
+
→ parse() src/parser/index.ts → parser.ts
|
|
24
|
+
→ Statement[] AST src/ast/nodes.ts
|
|
25
|
+
→ Statement.execute() src/api/statement.ts
|
|
26
|
+
→ executeStatement() src/executor/execute.ts
|
|
27
|
+
→ per-stmt executor select / dml / ddl / session / triggers
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Public entry points: `Database.exec` / `query` / `prepare` / `copyFrom` in [`src/api/database.ts`](src/api/database.ts).
|
|
31
|
+
|
|
32
|
+
Everything is **typed values** (`TypedValue = { t: TypeId, v: Datum }` in [`src/types/value.ts`](src/types/value.ts)) — there is no SQLite-style affinity. Type resolution/casting lives in [`src/types/resolve.ts`](src/types/resolve.ts) and [`src/types/cast.ts`](src/types/cast.ts); `numeric` is an in-repo arbitrary-precision implementation ([`src/types/numeric.ts`](src/types/numeric.ts)).
|
|
33
|
+
|
|
34
|
+
## `src/` map
|
|
35
|
+
|
|
36
|
+
| Directory | Role |
|
|
37
|
+
| --- | --- |
|
|
38
|
+
| `api/` | Public `Database` / `Statement` facade, bind-value conversion |
|
|
39
|
+
| `ast/` | Discriminated-union AST (`nodes.ts`) |
|
|
40
|
+
| `lexer/` | Tokenizer (dollar quoting, E-strings, `::`, operators) |
|
|
41
|
+
| `parser/` | Recursive-descent parser |
|
|
42
|
+
| `executor/` | Statement dispatch, SELECT, DML, DDL, session (SET/SHOW/COPY/PREPARE), triggers, window functions |
|
|
43
|
+
| `expressions/` | `evalExpr`, operators, pattern matching, `EngineCtx` |
|
|
44
|
+
| `functions/` | Scalar / aggregate / window / datetime / JSON / array / SRF / tsearch registries |
|
|
45
|
+
| `types/` | `TypedValue`, casts, comparison, numeric, datetime, jsonb, timezone |
|
|
46
|
+
| `storage/` | In-memory schemas, tables, sequences, `DatabaseState` |
|
|
47
|
+
| `schema/` | `pg_catalog` + `information_schema` virtual catalogs, `search_path` |
|
|
48
|
+
| `constraints/` | NOT NULL / PK / UNIQUE / CHECK / FK with referential actions |
|
|
49
|
+
| `transactions/` | BEGIN / COMMIT / SAVEPOINT (clones state + PRNG) |
|
|
50
|
+
| `runtime/` | Clock, PRNG, `DatabaseOptions` |
|
|
51
|
+
| `serialization/` | `PGMM` snapshot codec |
|
|
52
|
+
| `tsearch/` | `tsvector` / `tsquery` text search |
|
|
53
|
+
| `errors/` | `PostgresError` with SQLSTATE, `unsupported()` |
|
|
54
|
+
|
|
55
|
+
Hot / large files: `parser/parser.ts`, `executor/select.ts`, `executor/dml.ts`, `types/numeric.ts`.
|
|
56
|
+
|
|
57
|
+
## Critical conventions
|
|
58
|
+
|
|
59
|
+
- **AST `type` tags** are snake_case (`"create_table"`, `"drop_index"`). TypeScript interfaces are PascalCase (`CreateTableStmt`).
|
|
60
|
+
- **Identifiers** fold to **lowercase** unless double-quoted (PostgreSQL rule); quoted identifiers are case-sensitive.
|
|
61
|
+
- **Engine values** are `TypedValue` with PG internal type names (`int4`, `float8`, `numeric`, `timestamptz`, `_int4` for arrays). **Harness `SqlValue`** ([`tests/harness/types.ts`](tests/harness/types.ts)) is the normalized compare type — do not confuse them.
|
|
62
|
+
- **API `Statement`** vs **AST `Statement`**: the API class aliases the AST union as `AstStatement`.
|
|
63
|
+
- **`Database`** (API) vs **`DatabaseState`** (engine storage).
|
|
64
|
+
- Throw **`PostgresError`** with an `ErrorCategory` and a five-character **SQLSTATE**. Missing SQL must fail loud via `unsupported()` (`0A000`) — the inventory gate fails if the oracle exposes an unimplemented builtin/operator that isn't in `compat/unsupported-register.json`.
|
|
65
|
+
- The public API is **sync**; the oracle (PGlite) is **async** — contract helpers `await` both sides through the `ContractDb` adapter interface.
|
|
66
|
+
- TypeScript: `strict` + `noUncheckedIndexedAccess`. Imports use `.ts` extensions. Biome: 2-space, double quotes, 120 columns.
|
|
67
|
+
- Determinism invariants (seeded PRNG, fixed clock, PRNG rollback with transactions, byte-identical snapshots) must hold — see README.
|
|
68
|
+
|
|
69
|
+
## Change checklists
|
|
70
|
+
|
|
71
|
+
### New SQL statement
|
|
72
|
+
|
|
73
|
+
1. Add union member + interface in [`src/ast/nodes.ts`](src/ast/nodes.ts)
|
|
74
|
+
2. Parse in [`src/parser/parser.ts`](src/parser/parser.ts) (`parseStatement` dispatch)
|
|
75
|
+
3. Handle in [`src/executor/execute.ts`](src/executor/execute.ts) (+ `ddl.ts` / `dml.ts` / `session.ts` as needed)
|
|
76
|
+
4. Mutate `DatabaseState` if schema changes; keep `pg_catalog` / `information_schema` views consistent
|
|
77
|
+
5. Add differential contract under `tests/contract/<area>/`
|
|
78
|
+
6. Add/extend a scenario row in `compat/sections/*.ts` and its catalog test in `tests/contract/catalog/`
|
|
79
|
+
|
|
80
|
+
### New SQL function or operator
|
|
81
|
+
|
|
82
|
+
1. Implement and register in the right map under `src/functions/*` (or `src/expressions/operators.ts`) so [`scripts/postgres-inventory.ts`](scripts/postgres-inventory.ts) sees it; remove any matching entry from `compat/unsupported-register.json`
|
|
83
|
+
2. Contract tests under `tests/contract/functions/` (and related areas)
|
|
84
|
+
3. Run `bun run inventory` / `bun run test:postgres-compat` — oracle builtins must not be silently missing
|
|
85
|
+
|
|
86
|
+
### New contract test
|
|
87
|
+
|
|
88
|
+
1. Prefer helpers in [`tests/contract/helpers.ts`](tests/contract/helpers.ts):
|
|
89
|
+
- `parity` — query both engines, compare rows
|
|
90
|
+
- `parityTyped` — also compare column type names
|
|
91
|
+
- `execParity` — writes (row counts + final state)
|
|
92
|
+
- `sequenceParity` — multi-step (optional final-state compare)
|
|
93
|
+
- `errorParity` / `queryErrorParity` — both must fail with the same SQLSTATE class
|
|
94
|
+
- `rankParity` — REAL epsilon compare (ts_rank etc.)
|
|
95
|
+
- `divergence` — engine-only assertion pinned to a `compat/divergences.json` entry
|
|
96
|
+
2. Or `matrixBoth` + `expectParity` from `tests/harness/`
|
|
97
|
+
3. **Do not** treat isolated internal unit tests as PostgreSQL proof. The differential suite is authoritative.
|
|
98
|
+
4. Gate: `bun run test:postgres-compat`
|
|
99
|
+
|
|
100
|
+
## Test layout
|
|
101
|
+
|
|
102
|
+
| Path | Role |
|
|
103
|
+
| --- | --- |
|
|
104
|
+
| `tests/contract/` | Differential SQL vs PGlite (**authoritative**) |
|
|
105
|
+
| `tests/fuzz/` | fast-check property tests (seeded); differential, NoREC/TLP metamorphic, stateful DST |
|
|
106
|
+
| `tests/harness/` | Compare/normalize/classify helpers + harness unit tests |
|
|
107
|
+
| `tests/adapters/` | Wrappers for postgres-mem and PGlite (`ContractDb`) |
|
|
108
|
+
| `tests/corpus/` | Fuzz regression corpus |
|
|
109
|
+
| `tests/meta/` | Canary definitions, skip register |
|
|
110
|
+
|
|
111
|
+
Examples of public API usage: `tests/contract/api/`, `tests/contract/parameters/`, `tests/contract/determinism/`, `examples/react-vite`.
|
|
112
|
+
|
|
113
|
+
### Fuzz replay
|
|
114
|
+
|
|
115
|
+
Default seed `0x5a17e0e1`. On failure the seed is printed:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
bun test tests/fuzz
|
|
119
|
+
POSTGRES_MEM_FUZZ_SEED=12345 bun test tests/fuzz
|
|
120
|
+
POSTGRES_MEM_FUZZ_SEED=12345 POSTGRES_MEM_FUZZ_PATH='0:1' bun test tests/fuzz
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Compat system
|
|
124
|
+
|
|
125
|
+
| Command | Role |
|
|
126
|
+
| --- | --- |
|
|
127
|
+
| `bun run test:postgres-compat` | Requirements + fail-closed gate + construct catalog + smoke ratchet + contract/fuzz/harness |
|
|
128
|
+
| `bun run inventory` | Oracle `pg_proc` / `pg_operator` vs engine registries (+ `--write-register`) |
|
|
129
|
+
| `bun run scenarios` | Construct-level scenario catalog (`compat/scenarios.ts`) + smoke gate |
|
|
130
|
+
| `bun run requirements` | Refresh PostgreSQL 18 SQL-commands docs → `compat/requirements.json` + `compat/coverage.json` |
|
|
131
|
+
| `bun run canaries` | Apply deliberate sabotages, assert the suite catches them |
|
|
132
|
+
|
|
133
|
+
Statuses: **VERIFIED** / **PARTIALLY VERIFIED** / **UNSUPPORTED** / **NOT APPLICABLE**. Do not market PARTIAL as complete. Coverage evidence is directory paths (e.g. `tests/contract/joins/`), not automatic from test filenames.
|
|
134
|
+
|
|
135
|
+
**Catalog vs proof:** `tests/contract/catalog/` IDs must execute; trivial probes are tracked in `compat/smoke-baseline.json` (ratchet — no new smoke stubs). Documented divergences bind to `compat/divergences.json` (regenerate `DIVERGENCES.md` with `bun run divergences`). Generated operator/CAST matrices: `tests/contract/matrices/`. Stateful dump-after-each fuzz: `tests/fuzz/stateful.test.ts`. Oracle `server_version` must be on the allow-list in `tests/harness/oracle-versions.ts` (18.3).
|
|
136
|
+
|
|
137
|
+
**PGlite quirk:** PGlite's WASM boot leaks `process.exitCode = 99` under Bun ([pglite#975](https://github.com/electric-sql/pglite/issues/975)); the adapter and scripts reset it — keep that workaround when touching `tests/adapters/pglite.ts`.
|
|
138
|
+
|
|
139
|
+
Details: [COMPATIBILITY.md](COMPATIBILITY.md), audit: [COMPATIBILITY-AUDIT.md](COMPATIBILITY-AUDIT.md).
|
|
140
|
+
|
|
141
|
+
## Local gates
|
|
142
|
+
|
|
143
|
+
Requires [Bun](https://bun.sh).
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
bun install
|
|
147
|
+
bun run ci:local # same gates as GitHub Actions CI (except publish)
|
|
148
|
+
bun run check # format + lint + typecheck + postgres-compat suite
|
|
149
|
+
bun run format
|
|
150
|
+
bun run lint
|
|
151
|
+
bun run typecheck
|
|
152
|
+
bun run test:postgres-compat
|
|
153
|
+
bun test # contract + fuzz + harness
|
|
154
|
+
bun run build
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## PR and commits
|
|
158
|
+
|
|
159
|
+
Use [Conventional Commits](https://www.conventionalcommits.org/) for commits and PR titles (enforced in CI). Prefer squash merges with a conventional title. Releasing is automated via semantic-release — see [README.md](README.md#releasing).
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# PostgreSQL 18 Compatibility Audit
|
|
2
|
+
|
|
3
|
+
```text
|
|
4
|
+
PostgreSQL 18 Compatibility Audit
|
|
5
|
+
=================================
|
|
6
|
+
|
|
7
|
+
Reference PostgreSQL version:
|
|
8
|
+
PGlite (@electric-sql/pglite) — real PostgreSQL 18.3 compiled to WASM,
|
|
9
|
+
in-process, no Docker. server_version pinned in
|
|
10
|
+
tests/harness/oracle-versions.ts and asserted by the gate.
|
|
11
|
+
|
|
12
|
+
postgres-mem version:
|
|
13
|
+
0.0.0-development (package.json; publish via semantic-release)
|
|
14
|
+
|
|
15
|
+
Scope:
|
|
16
|
+
Every oracle-exposed SQL construct is in-scope except NOT APPLICABLE
|
|
17
|
+
(roles/auth enforcement, replication, wire protocol, storage internals,
|
|
18
|
+
PL/pgSQL, extensions). The gate fails closed on silence.
|
|
19
|
+
|
|
20
|
+
PostgreSQL requirements reviewed:
|
|
21
|
+
183 SQL commands (from the PostgreSQL 18 SQL-commands documentation
|
|
22
|
+
index → compat/requirements.json)
|
|
23
|
+
|
|
24
|
+
Requirements classification:
|
|
25
|
+
NOT APPLICABLE: 56
|
|
26
|
+
SQL_BEHAVIOR: 127
|
|
27
|
+
unknown: 0 (gate fails if non-zero)
|
|
28
|
+
|
|
29
|
+
Coverage statuses (SQL_BEHAVIOR):
|
|
30
|
+
VERIFIED: 43 / PARTIALLY_VERIFIED: 26 / UNSUPPORTED: 58 (fail-loud,
|
|
31
|
+
registered) — see `bun run requirements` / compat/coverage.json
|
|
32
|
+
|
|
33
|
+
Oracle builtin inventory:
|
|
34
|
+
pg_catalog functions exposed by oracle: 2787
|
|
35
|
+
implemented in engine registries: 301
|
|
36
|
+
registered unsupported (with reasons): 2486
|
|
37
|
+
silently missing: 0 (gate fails otherwise)
|
|
38
|
+
pg_catalog operators exposed by oracle: 74
|
|
39
|
+
implemented: 41
|
|
40
|
+
registered unsupported: 33
|
|
41
|
+
|
|
42
|
+
Construct catalog:
|
|
43
|
+
945 scenarios across 33 sections (compat/scenarios.ts), all promoted
|
|
44
|
+
to executing catalog tests; smoke baseline EMPTY (0 trivial stubs).
|
|
45
|
+
|
|
46
|
+
SQL grammar / operators / expressions:
|
|
47
|
+
VERIFIED — contracts + generated operator matrices from pg_operator
|
|
48
|
+
|
|
49
|
+
Types / casts / NULL:
|
|
50
|
+
VERIFIED — bool/int2/4/8, float4/8, numeric (in-repo arbitrary
|
|
51
|
+
precision), text/varchar/char, bytea, uuid, date/time/timestamp[tz],
|
|
52
|
+
interval, json/jsonb, arrays; cast matrices generated from pg_cast;
|
|
53
|
+
3VL through operators/aggregates/DISTINCT
|
|
54
|
+
|
|
55
|
+
Functions (oracle surface):
|
|
56
|
+
VERIFIED for the implemented 301; every other oracle builtin is an
|
|
57
|
+
explicit register entry (never silent)
|
|
58
|
+
|
|
59
|
+
JSON / JSONB:
|
|
60
|
+
VERIFIED — operator + function surface, jsonpath subset
|
|
61
|
+
|
|
62
|
+
Aggregates / windows:
|
|
63
|
+
VERIFIED — FILTER, ORDER BY in aggregates, string_agg/array_agg/
|
|
64
|
+
jsonb_agg, full frame specs incl. GROUPS/RANGE + EXCLUDE
|
|
65
|
+
|
|
66
|
+
CTEs / transactions / savepoints / constraints / FK:
|
|
67
|
+
VERIFIED — recursive + data-modifying CTEs; FK actions (CASCADE/SET
|
|
68
|
+
NULL/SET DEFAULT/RESTRICT/NO ACTION); DEFERRABLE parsed, checked at
|
|
69
|
+
statement end (commit-time deferral is a documented gap)
|
|
70
|
+
|
|
71
|
+
Schemas / search_path / catalogs:
|
|
72
|
+
VERIFIED — pg_catalog + information_schema commonly-queried subset
|
|
73
|
+
|
|
74
|
+
Sequences / serial / identity / enums / domains / generated columns:
|
|
75
|
+
VERIFIED
|
|
76
|
+
|
|
77
|
+
Triggers / LANGUAGE sql functions:
|
|
78
|
+
PARTIALLY VERIFIED — row-level triggers fire in creation order
|
|
79
|
+
(PostgreSQL: name order), UPDATE OF ignored, INSTEAD OF unsupported —
|
|
80
|
+
all pinned divergences
|
|
81
|
+
|
|
82
|
+
Text search:
|
|
83
|
+
PARTIALLY VERIFIED — tsvector/tsquery/@@/ts_rank with simple-style
|
|
84
|
+
config; no ispell/synonym dictionaries
|
|
85
|
+
|
|
86
|
+
COPY / PREPARE / EXECUTE / SET / SHOW:
|
|
87
|
+
VERIFIED — text + csv COPY via copyFrom API hook; GUC subset
|
|
88
|
+
|
|
89
|
+
Collation:
|
|
90
|
+
Pinned C semantics; locale/ICU out of scope (documented)
|
|
91
|
+
|
|
92
|
+
Snapshots / determinism:
|
|
93
|
+
VERIFIED — PGMM logical round-trip, byte-identical snapshots, PRNG
|
|
94
|
+
rollback with transactions, fixed clock, post-restore lockstep
|
|
95
|
+
|
|
96
|
+
Differential tests (2026-08-21 initial audit):
|
|
97
|
+
Total: 3400 under `bun test` / `bun run test:postgres-compat`
|
|
98
|
+
(contract + fuzz + harness; Bun 1.4.0, PGlite/PostgreSQL 18.3)
|
|
99
|
+
Passed: 3400
|
|
100
|
+
Failed: 0
|
|
101
|
+
expect() calls: 8968
|
|
102
|
+
Files: 273 (246 contract)
|
|
103
|
+
|
|
104
|
+
Stateful / fuzz:
|
|
105
|
+
Seeds: 0x5a17e0e1 (+ POSTGRES_MEM_FUZZ_SEED override, PATH replay)
|
|
106
|
+
Differential grammar fuzz, per-area fuzz (17 areas), TLP + NoREC
|
|
107
|
+
metamorphic, stateful DST with minimizer/repro, mixed-stateful,
|
|
108
|
+
robustness (PostgresError-only), corpus regressions
|
|
109
|
+
Mismatches: 0
|
|
110
|
+
|
|
111
|
+
Harness integrity:
|
|
112
|
+
PGlite adapter compares rows (canonical text), column names/types,
|
|
113
|
+
SQLSTATE class + normalized messages, rowCount/command tags, and
|
|
114
|
+
logical state dumps after write sequences. Comparator meta-tests:
|
|
115
|
+
tests/harness/*.test.ts. Canaries: 6 deliberate sabotages, each
|
|
116
|
+
caught by the suite (bun run canaries). Skip register: empty.
|
|
117
|
+
|
|
118
|
+
Engine bugs found & fixed by the differential/fuzz process (this build):
|
|
119
|
+
1. sum() crashed on certain inputs surfaced by grammar fuzz
|
|
120
|
+
2. min()/max() returned unnormalized numeric cells
|
|
121
|
+
3. NULL IN (empty subquery) returned NULL instead of false
|
|
122
|
+
4. FULL JOIN did not enforce the join condition on one side
|
|
123
|
+
(regression tests added under tests/contract/*; area corpus scripts
|
|
124
|
+
replayed by tests/fuzz/corpus.test.ts)
|
|
125
|
+
|
|
126
|
+
Remaining known differences / intentional:
|
|
127
|
+
Custom PGMM snapshots; deterministic random()/now() by default
|
|
128
|
+
({ random: "os" } / { now: "system" } match PostgreSQL entropy and
|
|
129
|
+
wall clock); sync single-session API; COPY payloads via copyFrom;
|
|
130
|
+
no 25P02 aborted-transaction state; trigger creation-order firing;
|
|
131
|
+
UPDATE OF ignored; no INSTEAD OF triggers; round(float8) ties away
|
|
132
|
+
from zero; '1e400'::float8 saturates; DROP CASCADE retains dependent
|
|
133
|
+
views; COMMENT ON not stored; version() banner; pg_get_viewdef
|
|
134
|
+
missing; EXPLAIN stubs. Machine-readable: compat/divergences.json
|
|
135
|
+
(18 entries, each pinned).
|
|
136
|
+
|
|
137
|
+
Final assessment:
|
|
138
|
+
Verified against PostgreSQL 18.3 (PGlite on Bun 1.4.0). Oracle
|
|
139
|
+
function/operator inventory is closed (0 silently missing).
|
|
140
|
+
Requirements matrix ingested with zero unknown statuses. Gate:
|
|
141
|
+
`bun run test:postgres-compat`. Triggers, text search, collation,
|
|
142
|
+
numeric extremes, and catalog long tail remain PARTIALLY VERIFIED
|
|
143
|
+
honestly — not “fully compatible because green.”
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Verification commands:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
bun run test:postgres-compat
|
|
150
|
+
bun run inventory
|
|
151
|
+
bun run requirements
|
|
152
|
+
bun run scenarios
|
|
153
|
+
bun run canaries
|
|
154
|
+
bun run typecheck
|
|
155
|
+
```
|
package/COMPATIBILITY.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Compatibility
|
|
2
|
+
|
|
3
|
+
Goal: **PostgreSQL 18 SQL dialect behavioral parity** as a drop-in for the same statements against the reference oracle. Compatibility is proven by the differential contract suite and the fail-closed gate:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
bun run test:postgres-compat
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
See [COMPATIBILITY-AUDIT.md](COMPATIBILITY-AUDIT.md) for the latest evidence-based audit report.
|
|
10
|
+
|
|
11
|
+
Reference oracle: **PostgreSQL 18.3** via PGlite (`@electric-sql/pglite`, real Postgres compiled to WASM, in-process). Inventory: `bun run inventory`. Construct catalog: `bun run scenarios` → [`compat/scenarios.ts`](compat/scenarios.ts). Divergences: [`compat/divergences.json`](compat/divergences.json). Requirements matrix: `bun run requirements` → `compat/requirements.json` + `compat/coverage.json`.
|
|
12
|
+
|
|
13
|
+
## Proof surface
|
|
14
|
+
|
|
15
|
+
Differential tests compare a **tuple** per statement: rows (normalized to canonical PostgreSQL text where typed), column names and type names where requested, error SQLSTATE class + normalized message, command tag / `rowCount`, and transaction status, plus a **logical state dump** (catalog names, column definitions, row payloads, sequence values) after write sequences.
|
|
16
|
+
|
|
17
|
+
A catalog ID appearing in a test file is **not** proof by itself. Trivial probes are tracked in [`compat/smoke-baseline.json`](compat/smoke-baseline.json) and ratcheted downward (currently **0 smoke stubs** across 945 catalog scenarios). Generated operator/cast matrices live under [`tests/contract/matrices/`](tests/contract/matrices/). Observed mem≠oracle diffs must bind to a `compat/divergences.json` entry or be a **FAILURE** — unexplained diffs are not allowed.
|
|
18
|
+
|
|
19
|
+
Intentional differences are finite and machine-readable in `compat/divergences.json` (PGMM snapshots, seeded `random()`/`now()`, sync single-session API, no aborted-transaction state, EXPLAIN stubs, trigger-order/`UPDATE OF` edges, float8 rounding/overflow edges, …). Human-readable: [DIVERGENCES.md](DIVERGENCES.md).
|
|
20
|
+
|
|
21
|
+
## Status vocabulary
|
|
22
|
+
|
|
23
|
+
| Status | Meaning |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| **VERIFIED** | Differential contracts (+ fuzz where applicable) cover happy path **and** meaningful edges vs oracle |
|
|
26
|
+
| **PARTIALLY VERIFIED** | Implemented; coverage thin or known edges remain |
|
|
27
|
+
| **UNSUPPORTED** | Missing from SQL surface (must fail loud; gate fails if oracle-exposed and unregistered) |
|
|
28
|
+
| **NOT APPLICABLE** | Outside the in-memory single-session dialect surface (roles/auth, replication, storage, wire protocol) |
|
|
29
|
+
|
|
30
|
+
## Scope bound
|
|
31
|
+
|
|
32
|
+
Anything a PostgreSQL application can invoke through SQL against the PGlite **18.3** oracle must match observable behavior, except:
|
|
33
|
+
|
|
34
|
+
1. **Snapshot format** — custom binary codec (`PGMM`), not `pg_dump` / on-disk clusters (logical state still round-trips).
|
|
35
|
+
2. **Deterministic `random()` / `now()`** — seeded PRNG and fixed clock by default (injectable).
|
|
36
|
+
3. **Single session** — no MVCC across connections, no isolation levels beyond one session, no `25P02` aborted-transaction state (documented divergence).
|
|
37
|
+
4. **NOT APPLICABLE** rows in `compat/coverage.json` (roles, replication, VACUUM internals, LISTEN/NOTIFY, cursors, PL/pgSQL, extensions).
|
|
38
|
+
|
|
39
|
+
The oracle exposes **2787 builtin functions** and **74 operators** in `pg_catalog`; postgres-mem implements **301 functions** and **41 operators**, and every remaining item is an explicit entry in [`compat/unsupported-register.json`](compat/unsupported-register.json) with a reason (trigger/internal plumbing, admin/monitoring, unsupported type families, …). The gate fails closed on silence.
|
|
40
|
+
|
|
41
|
+
## Requirements coverage (PostgreSQL 18 SQL commands)
|
|
42
|
+
|
|
43
|
+
`bun run requirements` ingests the PostgreSQL 18 SQL-commands documentation index: **183 commands** → 56 NOT APPLICABLE, 127 SQL-behavior. Of the SQL-behavior commands: **43 VERIFIED**, **26 PARTIALLY VERIFIED**, **58 UNSUPPORTED** (fail-loud, registered). Full detail: `compat/coverage.json`.
|
|
44
|
+
|
|
45
|
+
## Feature matrix (summary)
|
|
46
|
+
|
|
47
|
+
| Area | Status | Notes |
|
|
48
|
+
| --- | --- | --- |
|
|
49
|
+
| Core DML / SELECT / joins / CTE / ON CONFLICT / RETURNING | VERIFIED | Contract + fuzz |
|
|
50
|
+
| Types: bool/int2/4/8, float4/8, numeric, text/varchar/char, bytea, uuid | VERIFIED | numeric is in-repo arbitrary precision |
|
|
51
|
+
| Date/time: date, time, timestamp[tz], interval + arithmetic | VERIFIED | Timezone conversions for named zones; some interval corners partial |
|
|
52
|
+
| Casts (implicit/assignment/explicit) | VERIFIED | Generated cast matrices from oracle |
|
|
53
|
+
| Arrays + unnest + subscripts/slices | VERIFIED | |
|
|
54
|
+
| JSON / JSONB operators + functions | VERIFIED | |
|
|
55
|
+
| Window functions (frames, EXCLUDE) | VERIFIED | |
|
|
56
|
+
| GROUPING SETS / ROLLUP / CUBE, DISTINCT ON, LATERAL, set ops | VERIFIED | |
|
|
57
|
+
| Recursive + data-modifying CTEs | VERIFIED | |
|
|
58
|
+
| Constraints: PK / UNIQUE / NOT NULL / CHECK / FK actions | VERIFIED | DEFERRABLE parsed, checked at statement end |
|
|
59
|
+
| Sequences / serial / identity | VERIFIED | |
|
|
60
|
+
| Schemas + search_path + pg_catalog / information_schema | VERIFIED | Catalog columns are the commonly-queried subset |
|
|
61
|
+
| Enums, domains, generated columns | VERIFIED | |
|
|
62
|
+
| Triggers (row-level, LANGUAGE sql-expressible) | PARTIALLY VERIFIED | Creation-order firing, `UPDATE OF` ignored, no INSTEAD OF (documented) |
|
|
63
|
+
| CREATE FUNCTION LANGUAGE sql | PARTIALLY VERIFIED | Scalar + set-returning; no polymorphic/variadic edges |
|
|
64
|
+
| Text search (tsvector / tsquery / @@ / ts_rank) | PARTIALLY VERIFIED | `simple`-style config; no ispell/synonym dictionaries |
|
|
65
|
+
| COPY FROM/TO (text, csv) | VERIFIED | Via `copyFrom` API hook / rows out |
|
|
66
|
+
| PREPARE / EXECUTE / DEALLOCATE, SET / SHOW / RESET | VERIFIED | GUC subset |
|
|
67
|
+
| Transactions / savepoints | VERIFIED | No `25P02` aborted state (documented divergence) |
|
|
68
|
+
| Collation / ordering | PARTIALLY VERIFIED | `C` semantics pinned; locale/ICU out of scope |
|
|
69
|
+
| Regex (`~`, `~*`, POSIX functions) | PARTIALLY VERIFIED | JS regex flavor mapped to POSIX ERE; documented edges |
|
|
70
|
+
| EXPLAIN | PARTIALLY VERIFIED | Stub plan shapes |
|
|
71
|
+
| MERGE / CALL / cursors / LISTEN / PL/pgSQL | UNSUPPORTED | Fail loud `0A000`, registered |
|
|
72
|
+
| Roles / GRANT / VACUUM / ANALYZE / LOCK | NOT APPLICABLE | Parsed no-ops where harmless |
|
|
73
|
+
| Wire protocol / multi-session MVCC / on-disk format | NOT APPLICABLE | |
|
|
74
|
+
|
|
75
|
+
## How to verify
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
bun run test:postgres-compat # requirements + gate + contract/fuzz/harness
|
|
79
|
+
bun run inventory # oracle pg_proc/pg_operator inventory
|
|
80
|
+
bun run requirements # refresh PostgreSQL 18 requirements + coverage
|
|
81
|
+
bun run scenarios # catalog + smoke ratchet
|
|
82
|
+
bun run build # ESM browser build
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Do not treat isolated unit tests of internal modules as proof of PostgreSQL compatibility. The differential suite is authoritative for SQL behavior; `test:postgres-compat` is the release gate.
|
|
86
|
+
|
|
87
|
+
**Parity claim:** Verified against **PostgreSQL 18.3** (PGlite). Features marked **VERIFIED** are oracle-proven. **PARTIALLY VERIFIED** rows must not be marketed as complete. **NOT APPLICABLE** is the only allowed permanent omission from the SQL drop-in claim.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Chris Vouga
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|