v0.3.0 — Per-database schema support
Apps that keep tables outside public (a dedicated analytics schema, multi-tenant layouts, …) are now first-class citizens.
✨ New: per-database schema config option
[databases.events]
url = "${EVENTS_DATABASE_URL}"
schema = "analytics"- All metadata tools target the configured schema —
schema,describe,analyze,anomalies,trend,sample,erd, indexes, and foreign keys now parametrize the schema instead of hardcodingpublic(default remainspublic; existing configs are unaffected). - Raw queries work unqualified — for non-public schemas the connection sets
search_path=<schema>,public, soSELECT count(*) FROM eventsjust works;publicstays second so extensions (pgvector operators etc.) keep resolving. - Clearer errors — "table not found" and empty-schema messages now name the schema they looked in.
🔒 Safety
- The schema name is validated at config load as a plain lowercase identifier (
^[a-z_][a-z0-9_]*\Z) — it is the one identifier-shaped value embedded outsidepsycopg.sql.Identifier()(in the libpqsearch_pathoption), so it is held to a stricter shape than table/column identifiers. Lowercase-only because unquotedsearch_pathcase-folds: an uppercase schema would silently diverge from the case-preserving parametrized metadata queries.\Zanchor keeps a trailing newline out of the options string. - All schema-scoped catalog queries use
%sparameters — never string interpolation.
✅ Tests
- Test suite expanded 179 → 199: config validation (uppercase/trailing-newline/non-string rejection), per-tool schema scoping, anti-mutant "no hardcoded
public" asserts,search_pathoption assembly.
Backward-compatible: without schema in the config nothing changes.
Install: pip install dbecho