# Compatibility and status

> Postgres 18.3 behavior and on-disk compatibility, regression-oracle model, non-production status, and extension/PL boundaries that block generic Postgres extensions.

- Repository: malisper/pgrust
- GitHub: https://github.com/malisper/pgrust
- Human docs: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21
- Complete Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/llms-full.txt

## Source Files

- `README.md`
- `crates/common/controldata_utils/src/lib.rs`
- `crates/backend/postmaster/postmaster/src/main_entry.rs`
- `crates/pl/plpgsql/src/handler/src/lib.rs`
- `crates/backend/utils/fmgr/dfmgr_seams/src/lib.rs`
- `scripts/run-regression`

---

---
title: "Compatibility and status"
description: "Postgres 18.3 behavior and on-disk compatibility, regression-oracle model, non-production status, and extension/PL boundaries that block generic Postgres extensions."
---

pgrust ships a single `postgres` server binary that targets **PostgreSQL 18.3** wire and SQL behavior, reuses the **same on-disk control and catalog version** as stock 18.3, and treats the **vendored 18.3 regression expected files** as the behavioral oracle. It is **not production-ready**, is **not performance-optimized**, and does **not** load arbitrary third-party or unported C extensions the way stock Postgres does.

## Status snapshot

| Area | Current contract |
|------|------------------|
| Target version | PostgreSQL **18.3** (`server_version` / `server_version_num` = `18.3` / `180003`) |
| Behavioral oracle | Vendored `vendor/postgres-18.3/regress/{sql,expected}` via `scripts/run-regression` |
| Launch claim | Matched expected output across **46,000+** regression queries |
| Production | **Not production-ready**; not performance-optimized |
| On-disk | Boots a **Postgres 18.3** `PGDATA` when control/catalog layout matches |
| Generic extensions | **Not generally compatible** — only in-process **builtin** libraries |
| Other PLs | **PL/Python, PL/Perl, PL/Tcl** not generally available |
| Ported PL | **PL/pgSQL** registered as builtin library `plpgsql` |
| Runtime I/O | Supported path requires **`io_method=sync`** (default GUC is `worker`) |

<Warning>
Use pgrust for exploration, demos, regression work, and architecture experiments — not as a drop-in production replacement for PostgreSQL.
</Warning>

## Version identity

Preset GUCs report stock 18.3 numbers; `version()` is deliberately branded.

| Surface | Value |
|---------|--------|
| `server_version` | `18.3` |
| `server_version_num` / `PG_VERSION_NUM` | `180003` |
| `PG_VERSION` / `PG_MAJORVERSION` (data dir) | `18.3` string / major **`18`** |
| `PG_CONTROL_VERSION` | `1800` |
| `CATALOG_VERSION_NO` | `202506291` |
| SQL `version()` | Banner **`pgrust 18.3 on …`** (not `"PostgreSQL 18.3 …"`) |

`server_version` and `server_version_num` stay Postgres-shaped for clients and tooling. The `version()` text is a known cosmetic divergence; regression tests only regex platform tokens such as `linux-gnu`, not the product name.

```sql
SELECT version();
-- e.g. pgrust 18.3 on aarch64-darwin, compiled by clang-21.0.0, 64-bit

SHOW server_version;
-- 18.3
```

## On-disk compatibility

pgrust can create a cluster with `postgres --initdb` against `vendor/postgres-18.3/share`, or start against an **existing Postgres 18.3** data directory when the same major version and control-file layout apply.

### What is checked

```text
PGDATA/
├── PG_VERSION              → major must equal this binary's major (18)
└── global/pg_control       → fixed LP64 ControlFileData image + CRC
                              pg_control_version == 1800
                              catalog_version_no == 202506291
                              BLCKSZ, MAXALIGN, float format, segment sizes, …
```

| Check | Owner behavior | Failure signal |
|-------|----------------|----------------|
| `PG_VERSION` major | `ValidatePgVersion` | `database files are incompatible with server` / invalid data directory |
| `pg_control` present | postmaster `checkControlFile` | cannot find system; exit path |
| `pg_control_version` | `get_controlfile` | wrong version or **byte-ordering mismatch** |
| CRC | returned as `crc_ok` (not always fatal at read) | catalog SQL / recovery paths report checksum mismatch |
| Catalog / compile constants | control-file compatibility checks at startup | `database files are incompatible with server: CATALOG_VERSION_NO …` (and peers) |

Native-endian decode of the control image means a cluster written on a different byte order fails with an explicit **byte ordering mismatch** rather than silent corruption.

### Practical boundaries

- Same **major** (18) and **catalog version** (`202506291`) as stock 18.3 are required.
- A clean match of layout/constants is the compatibility bar — not a guarantee for every recovery, extension, or WAL edge case.
- Wrong share / timezone trees break init and boot even when `PGDATA` is valid; bake or pass `-L` to `vendor/postgres-18.3/share`.

Operational detail for reusing an existing cluster: [Boot from existing Postgres data](/existing-data-dir).

## Behavioral oracle (regression model)

Compatibility is measured against **unmodified** PostgreSQL 18.3 SQL and expected outputs, not against a hand-written pgrust suite.

| Input | Role |
|-------|------|
| `vendor/postgres-18.3/regress/sql/*.sql` | Test scripts |
| `vendor/postgres-18.3/regress/expected/*.out` (+ `_1` / `_2` variants) | Oracle outputs |
| `vendor/postgres-18.3/regress/parallel_schedule` | Default full-suite order |
| `target/release/postgres` | Server under test (`--initdb` + postmaster) |
| PostgreSQL 18 `psql` | Client only (external dependency) |

`scripts/run-regression`:

1. Runs pgrust `--initdb` with `-L vendor/postgres-18.3/share`.
2. Starts postmaster with **`io_method=sync`**, raised stack settings, and a short Unix socket path.
3. Creates database `regression` and runs each SQL file through `psql`.
4. Diffs stdout against the best matching expected file (`PASS` only when diff length is 0).
5. Treats server death as `CRASHED` and non-zero `psql` as `PSQL_FAIL`.

```bash
PGRUST_BIN="$PWD/target/release/postgres" scripts/run-regression
# optional: scripts/run-regression select test names…
```

Vendored data is **verbatim** from PostgreSQL 18.3 (`REL_18_3`): installed `share/`, regress SQL/expected/schedule, and isolation assets for separate harnesses. Isolation/auth/recovery TAP runners exist alongside the clone-only main suite; they are not the primary 46k-query claim surface.

## Extension and PL boundaries

### Loader model

Stock Postgres resolves `LANGUAGE C` / `MODULE_PATHNAME` via `dlopen` of `$libdir/<module>.so`. pgrust’s Rust backend **does not expose a C ABI** for arbitrary shared libraries. Ported modules register into an in-process **builtin library** table (`dfmgr_seams::register_builtin_library`). `load_file` / `load_external_function` consult that table first; only unregistered names fall through to the OS loader path.

```text
CREATE EXTENSION / LOAD / prosrc
        │
        ▼
  strip path + .so → basename (e.g. citext, plpgsql, regress)
        │
        ├─ builtin registry hit → in-process Rust functions + optional pg_init
        │
        └─ miss → OS dlopen/dlsym path (generic C extensions fail here)
```

Unported modules therefore fail at load or symbol resolution even when SQL scripts exist on disk. Output plugins (for example `test_decoding`) use a parallel **builtin output-plugin** registry for the same reason.

### Ported contrib / test builtins

Registered **simple library names** (suffix-free):

| Builtin name | Notes |
|--------------|--------|
| `citext` | Vendored `.control` / SQL under `share/extension/` |
| `hstore` | Same |
| `ltree` | Same |
| `pg_trgm` | Same |
| `pgcrypto` | Same; custom GUC `pgcrypto.builtin_crypto_enabled` |
| `pg_stat_statements` | Same |
| `uuid-ossp` | Same |
| `injection_points` | Same |
| `plpgsql` | Language handler; vendored `plpgsql.control` |
| `pg_prewarm` | Builtin library registered; not all autoprewarm surfaces may be complete |
| `regress` | In-process regress helpers for the SQL suite (path need not exist on disk) |
| `test_decoding` | Builtin **output plugin**, not a generic `.so` |

Workspace also contains partial contrib work (for example `earthdistance` without full fmgr/`CREATE EXTENSION` wiring, `amcheck` crates without the same builtin registration pattern). Treat those as **incomplete ports**, not drop-in extensions.

### What is blocked

| Workload | Status |
|----------|--------|
| Arbitrary `CREATE EXTENSION` of community C modules | **Blocked** without a pgrust builtin port |
| PL/Python, PL/Perl, PL/Tcl | **Not generally compatible** |
| PL/pgSQL | **Ported as builtin** `plpgsql` (with remaining loud seams for some SPI/exec/GUC init edges) |
| Preload of unported `.so` via `shared_preload_libraries` | **Fails** unless the name is a registered builtin |
| Logical decoding plugins other than registered builtins | **Builtin registry only** for ported plugins |

Vendored `share/extension/*.control` today covers the ported set above (plus `plpgsql`), not the full upstream contrib catalog. `CREATE EXTENSION` still needs matching control/SQL in the share tree **and** a registered builtin body.

## Required runtime posture (compatibility-affecting)

These settings are not optional for a working multiuser boot path in the documented runners and Docker image:

| Setting | Required / typical | Why it matters |
|---------|-------------------|----------------|
| `io_method` | **`sync`** | Only I/O method the port supports end-to-end; GUC default remains `worker` |
| `max_stack_depth` | e.g. `60000` | Deep recursion / plan paths |
| `RUST_MIN_STACK` | e.g. `33554432` | Rust thread stack floor |
| `ulimit -s` | e.g. `65520` | Process stack headroom |
| Share dir | `PGRUST_PGSHAREDIR` at build and/or `-L` at init | Bootstrap catalogs, TZ, extensions |

Without `io_method=sync`, AIO worker paths are not a supported compatibility surface.

## Compatibility matrix (summary)

| Capability | Compatible? |
|------------|-------------|
| SQL / catalog behavior vs PG 18.3 regress oracle | Yes, as measured by `scripts/run-regression` |
| Protocol clients / `psql` 18 | Yes (client is external) |
| On-disk 18.3 `PGDATA` (same arch/endian, matching control constants) | Intended yes |
| Cross-endian or non-18 major data | No |
| Generic C extensions / other PLs | No (builtins only) |
| Production HA / performance SLAs | No claim |
| Multithreaded postmaster, pooling, experimental storage | Roadmap only |

## Failure modes

| Symptom | Likely cause |
|---------|----------------|
| `byte ordering mismatch in control file` | Endian / wrong machine’s `pg_control` |
| `PG_CONTROL_VERSION … but the server was compiled with …` | Control version or byte-swap misread |
| `database files are incompatible with server` + `CATALOG_VERSION_NO` | Not a 18.3 catalog, or wrong binary |
| `could not load library` / missing function in file | Unported extension; not in builtin registry |
| Extension SQL not found | Control/SQL not under vendored `share/extension/` |
| Boot hangs / AIO errors | Missing `-c io_method=sync` |
| Stack overflow | Missing `RUST_MIN_STACK` / `max_stack_depth` / `ulimit -s` |
| Root refused | Same root check as Postgres for normal modes |

## Next

<CardGroup cols={2}>
  <Card title="Boot from existing Postgres data" href="/existing-data-dir">
    Control-file and major-version constraints when reusing a stock 18.3 PGDATA.
  </Card>
  <Card title="Use contrib extensions" href="/contrib-extensions">
    Builtin modules, CREATE EXTENSION against vendored control/SQL, and unported gaps.
  </Card>
  <Card title="Run regression tests" href="/regression-tests">
    Clone-only scripts/run-regression, psql client, selective tests, TAP harnesses.
  </Card>
  <Card title="Seams and builtin libraries" href="/seams-and-builtins">
    How registration replaces dlopen for ported contrib and regress helpers.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    io_method, stack, share/tz paths, initdb gaps, and unported extension loads.
  </Card>
  <Card title="Overview" href="/overview">
    Who should use pgrust, status limits, and first docs routes.
  </Card>
</CardGroup>
