0630 — A Postgres schema is one Vulkan installation
Edit this pageContext. Vulkan had no installation boundary. Its shared control-plane tables and the ten tables every topic creates all landed in whatever schema the connection defaulted to — public, beside the caller’s own tables. The question arrived from the other end: [0625] chunk 15 wanted vulkantest.NewClient(t) to isolate one test from another, and there was nothing to isolate with. The same gap explains why system_config is a singleton row with no way to run two systems, and why river, pgmq, and graphile-worker all default to a schema of their own.
Decision. One schema holds exactly one Vulkan installation. PostgresConnectionConfig.Schema defaults to DefaultSchema = "vulkan", and NewPostgresDatastore sets the pool’s search_path to "<schema>, public"; PostgresDatastore carries the resolved name for the operations that must spell it. No SQL changes: 314 shared-table references across 62 files and 207 SQL literals would each have become a Sprintf, which contradicts the ## SQL rule that a literal is a constant raw string. public trails the schema because InTransaction runs the caller’s own SQL on Vulkan’s pool, so INSERT INTO orders must keep finding public.orders; the cost is that a caller’s CREATE TABLE inside that closure lands in Vulkan’s schema. RegisterSystem runs CREATE SCHEMA IF NOT EXISTS before any CREATE TABLE — with the schema absent the search_path falls through and every table would land in public silently — and 42501 raises system.ErrSchemaNotCreatable (VK0064). The name is validated against a lowercase unquoted-identifier pattern rather than quoted, since it reaches both CREATE SCHEMA and the runtime parameter as written. DestroySystem drops the tables it created and leaves the schema standing: the namespace may be shared or DBA-created, and dropping it is past what the verb promises.
Consequences. The singleton system_config row and System()’s nameless handle are correct permanently — multiple systems come from multiple schemas, not from a system_id dimension growing through every read, which answers the open question [0625] left. Advisory lock keys become the one thing still blind to the boundary: they are database-scoped and keyed by name only, so two installations serialize on locks neither needs — a contention fix, not a correctness one, and its own change. An existing database’s tables stay in public and become invisible rather than corrupted: reads before RegisterSystem fall through and may still find them. Pre-v1, so no expand/contract dance; a dev database is dropped and recreated. tools/compat compares a pinned build writing to public against a working tree writing to vulkan and must pin the new build to public for the comparison to mean anything. Proven by just schema-lab. Its search_path is removed outright by [0632], and its “no SQL changes” clause and the rejection below are superseded by [0631], which qualifies every literal — the search_path this record sets ends in public, so an unqualified name resolves to whatever another installation left there; the rest of this record stands. Rejected: schema-qualifying every table name (see above, and reversed by [0631]); an ephemeral schema per test, which does not isolate because the locks do not (ephemeral databases do).