Skip to the content.

Identifier policy

Which object names MCPg accepts, and why a few tools are stricter than the rest. Everything below was checked against the code (src/mcpg/identifiers.py and the modules named in each row); if you change a validator, update this page.

The rule for SQL tools

Any name PostgreSQL accepts as a delimited (double-quoted) identifier works in tools whose name reaches PostgreSQL as a SQL identifier — hyphens (adm-pgbench), spaces, mixed case, unicode, even an embedded ".

mcpg.identifiers.quote_identifier makes that safe by escaping, not forbidding: an embedded " is doubled (" → ""), so the value cannot close the quoted identifier and inject SQL, and the name round-trips to the exact object it names.

Only values that are not addressable identifiers at all are rejected (ensure_identifier):

Rejected Why
empty string "" is not a legal identifier
embedded NUL (\x00) cannot cross the wire protocol
longer than 63 bytes (UTF-8) PostgreSQL silently truncates, so …_v1 and …_v2 would become the same object

The error reads invalid <kind> name: ….

Plain-identifier-only sinks

Some tools deliberately keep the strict [A-Za-z_][A-Za-z0-9_]* rule, because the name is not (only) spliced as a quoted SQL identifier:

Sink Modules Why it stays strict
ORM / schema exporters (generate_prisma_schema, Drizzle, SQLAlchemy, sqlc, Diesel, jOOQ, Ent, Ecto) prisma, drizzle, sqlalchemy_export, sqlc, diesel, jooq, ent, ecto The name becomes an identifier in generated source code (Rust, Go, Elixir, TypeScript, …), where a hyphen or space is not valid.
Apache AGE labels and graph projection graph, graph_projection Labels/graph names are catalog-derived and interpolated into generated Cypher / SQL.
SQL/PGQ property graphs pgq Schema and graph names feed a CREATE PROPERTY GRAPH statement.
NL→SQL schema selection and audit nl2sql, audit_nl2sql Kept strict so a schema name cannot smuggle text into the LLM prompt; reader-role / backend names go into DDL.
Text-search configuration textsearch The regconfig name is embedded as a single-quoted string literal (to_tsvector('<config>', …)); the plain rule is what keeps that literal from being closed. Table and column arguments are quoted.
Filesystem paths and database names for scheduled backups cron Reach a shell command line, so they use a path allowlist ([A-Za-z0-9_./-]) instead.

A rejection from an exporter says so explicitly:

invalid schema name 'adm-pgbench'; this exporter only accepts plain identifiers
[A-Za-z_][A-Za-z0-9_]* because the name becomes a source identifier in generated
code. SQL tools (export_table, dump_database, …) accept delimited names via
quoting — see docs/identifier-policy.md.

If you need to export a schema whose name is not a plain identifier, use a SQL tool (export_table, dump_database) for it, or rename the object.

For contributors