Skip to content

Extension file structure


Installed files

File Location
pg_vault_tables.so $(pg_config --pkglibdir)
pg_vault_tables.control $(pg_config --sharedir)/extension
pg_vault_tables--1.0.sql $(pg_config --sharedir)/extension

Source layout

pg_vault_tables/
├── pg_vault_tables.control
├── Makefile
├── src/
│   ├── vault_tableam.c          the access method routine and its callbacks
│   ├── vault_enforce.c          permission, retention and insert-once decisions
│   ├── vault_reloptions.c       option parsing and catalogue storage
│   ├── vault_utility_hook.c     ProcessUtility: creation-time option handling
│   ├── vault_alter_gate.c       ALTER TABLE, policies and reserved schemas
│   ├── vault_object_access.c    object_access: DROP and TRUNCATE
│   ├── vault_catalog_guard.c    executor + per-statement and pre-commit
│   │                            integrity check on a table's own rows
│   ├── vault_violation.c        the violation record
│   └── vault_options_accessor.c the SQL-callable options accessor
├── sql/
│   └── pg_vault_tables--1.0.sql
├── test/
│   ├── sql/                     pg_regress inputs, plus plain_plans.sql,
│   │                            which is not in REGRESS and is consumed only
│   │                            by the with-and-without comparison
│   ├── expected/                pg_regress expected output
│   └── specs/                   isolation specifications
├── t/                           TAP tests
└── docs/                        this site

SQL objects created

All in the pgvault_tables schema, except the access method.

Object Kind Notes
vault access method Not schema-scoped; lives in pg_am
vault_tableam_handler(internal) function Named by CREATE ACCESS METHOD
pgvault_tables schema USAGE granted to PUBLIC
_table_options(regclass) function Internal accessor; owner only
vault_tables view Internal; owner only
view_vault_tables() function The public registry call; SECURITY DEFINER
purge_vault(name, name) procedure Owner only; deletes rows

Why pgvault_tables and not vault

Supabase's own vault extension installs into a schema literally named vault on every Supabase project. Using the bare name would be a genuine installation conflict, not a style preference.

The access method itself keeps the name vault, because access methods live in pg_am — a separate namespace — and are conventionally named for what they do rather than which package installed them, as heap, btree and gin are.


Test suites

Suite Harness Covers
vault_scaffold pg_regress Delegation to heap
vault_transparency pg_regress Ordinary PostgreSQL is unaffected
vault_reloptions pg_regress Option parsing and storage
vault_enforcement pg_regress Row-level enforcement
vault_statement pg_regress ALTER, DROP, TRUNCATE, cascades
vault_purge pg_regress Registry and purge procedure
vault_matrix pg_regress Every refusal, as three different roles
vault_maintenance pg_regress Vacuum, indexes, triggers
vault_definition pg_regress The definition-level permissions
vault_columns pg_regress A narrowed update grant, and coldefault
vault_catalog pg_regress Definition integrity, and its scoping
vault_catalog_merge pg_regress The same via MERGE in a CTE, split out because its syntax is PostgreSQL 17+
vault_convert pg_regress Converting an existing table in place
plain_tables pg_regress Ordinary tables in depth — every CREATE TABLE and ALTER TABLE form, partitioning, inheritance, foreign keys, maintenance
plain_dml pg_regress Ordinary data operations — joins, CTEs, window functions, COPY, MERGE, cursors, locking, isolation levels
plain_ddl pg_regress Every object type PostgreSQL offers, created, altered, commented and dropped — including event triggers
plain_roles pg_regress The same as an unprivileged role, and the privilege system itself
plain_pg17, plain_pg18 pg_regress Ordinary SQL only newer majors can parse, split out so their alternative expected files stay small
insertonce isolation Concurrent first insert
001_dump_restore TAP Dump and restore round trip
002_fail_closed TAP Behaviour without the library loaded
003_replication TAP Physical replication
004_logical_replication TAP Logical replication
005_violations TAP Violation record completeness
006_prepared_transaction TAP Definition integrity under two-phase commit
007_ordinary_sql TAP Ordinary SQL is byte-identical with and without the extension

Proving ordinary PostgreSQL is unaffected

The plain_* files run last and use no vault tables at all. They assert that ordinary SQL still works; they cannot on their own assert that it is unchanged, because their expected output was captured from a server that already had the extension loaded.

007_ordinary_sql closes that gap. It builds two clusters from the same binaries differing in one line of configuration, feeds them the identical scripts, and requires byte-identical output — once with no vault tables present and once with them live. It also compares query plans, which no stored expected file could, since plan shape moves between major versions.

The same comparison is run against PostgreSQL's own regression suite in CI, on 16, 17 and 18: core's complete suite is executed twice against clusters identical but for shared_preload_libraries, and every result file must match. Identity between the two runs is the assertion rather than "core's suite passes", so a core test that fails for reasons of its own — a platform quirk, a packager's build options — cannot be mistaken for a problem with this extension.

All are run by make installcheck, which invokes all three harnesses in one target — pg_regress, then pg_isolation_regress, then prove. PGXS has no separate installcheck-isolation or installcheck-tap targets.

The whole of it runs on every platform in the matrix: PostgreSQL 16, 17 and 18, across Rocky Linux 8 and 9, Debian 12, Ubuntu 22.04, 24.04 and 26.04 LTS, and Fedora 44. See Version and platform coverage and Tested Platforms.