SQL logoGovernance

Who Signs Off on a Generated `CREATE TABLE`?

A pull request lands. It contains a DDL file generated from a governed model, destined for a database real customers depend on. A reviewer has to decide whether to approve it.

Who Signs Off on a Generated CREATE TABLE?

A pull request lands. It contains a DDL file generated from a governed model, destined for a database real customers depend on. A reviewer has to decide whether to approve it.

That decision is the whole governance question, and it is a useful one to design backwards from. What would a careful reviewer need in order to say yes? That nothing was applied behind their back. That the generator invented no constraints. That anything the target could not express was declared rather than dropped. That if the model changed, a human chose the change. And that the same inputs produce the same file tomorrow.

Those five requirements are how the SQL path through CoreModels is built.

Nothing is applied to your database. Ever.

Export returns text. That is the entire mechanism: no connection to your database, no credential held, no migration executed, no ALTER issued at three in the morning by a scheduler.

The consequence is a clean split of authority. The read side — generating DDL from the governed model — needs only the Viewer role, which makes it safe to automate: run it in CI, commit the output, diff it, attach it to a ticket. We produce a reviewable artifact; your change management applies it.

That is why generated DDL belongs in a pull request. It is a text file, and diffing it is the review.

Import is a deliberate, role-gated write

Getting DDL into a governed project changes that project, and writes are treated with suspicion by design.

Schema import requires the Admin role on the target project; a Viewer token cannot alter your model under any circumstances. What lands is additive in shape — Types for tables, Elements for columns, governed vocabularies for enumerated columns, references for foreign keys — and every Type and Element the decoder produces is stamped with provenance: source format SQL, plus the table or column it came from. Re-running the same import upserts by id and guards each edge write, so a second run leaves no duplicates behind.

A project is touched only when somebody with the right role explicitly posts to it. Our own operating guidance is blunt about the rest: run writes against a test project first, read what came back, then do it for real.

Meaning changes pass through a human gate

Importing fresh structure into an empty space is one thing. Mapping a SQL schema onto a model you already govern — deciding that this legacy column is that governed element — is a change to meaning, and meaning changes get a gate.

The surface is shaped so the safe path is the default path. The stateless mapping call never writes anything: every call is inherently a dry run, returning both the proposed result and the executed plan as a reviewable artifact. The map-and-import call takes an explicit dry-run flag, and the documented contract is dry-run first, read the lossiness, then import.

Every plan — hand-authored, inferred by label and type matching, or proposed by the AI-assisted mode — passes the identical validation gate before anything executes. The AI mode earns no shortcut: a rejected proposal gets at most one repair attempt, a rejected repair is a rejection, and the model's own confidence score is advisory only.

After the gate, execution is deterministic. Store the plan, feed it back later with the same source, and you get the same output — the property that makes "the reviewer approved this plan" a meaningful statement rather than a hopeful one.

Honest lossiness, specifically for SQL

The rule we ask everyone to internalise: success: true means it ran, not nothing changed. The lossiness ledger is the honest list of what the target could not hold exactly, and reading it is the review step, not optional hygiene.

For SQL the ledger earns its keep in two directions.

On import, a table-level PRIMARY KEY, UNIQUE, or CHECK constraint has no representation in the governed model; each is recorded as a constraint relaxation naming the table it came from. It does not disappear into silence, and it is not fabricated back on export. A generated CREATE TABLE does not sprout a primary key we never modeled, because an invented constraint is a claim we cannot back.

On export, the case that bites most often is the governed vocabulary. MySQL can express it inline as an ENUM. PostgreSQL and SQL Server, in this coder, cannot — so the column is emitted as VARCHAR and the ledger says exactly that: the allowed-value constraint is not enforced. Any exporter targeting an engine with no inline enum construct has to make the same substitution. The difference is whether it tells you.

Each entry carries one of four kinds — a structural drop, a type approximation, a constraint relaxation, or a semantic narrowing — plus a path pointing at the specific type or element and a plain-English explanation. An empty ledger means a clean conversion; a non-empty ledger on a successful call is by design.

Failure is honest too: empty input is reported as empty, and input with no CREATE TABLE in it is reported as such rather than as a cheerful success over an empty model.

Drift becomes evidence

Once the governed model rather than any single database is the authority, a new question becomes answerable: does the running estate still match what we agreed?

Because export is deterministic — the same governed model regenerates the same DDL every time — regenerated DDL is diffable against what is deployed. For warehouse estates the loop closes more formally: read-only audits compare a platform's own catalog export against the governed model and return findings with stable codes and severities — a type that drifted, a dataset that disappeared, a field removed, an allowed-value list narrowed or stripped of its constraint. The findings are structured, so a CI job can gate on them: an error count above zero fails the build, and the run can be recorded into a history.

Drift stops being a rumor discovered during an incident and becomes a dated finding with a subject, a code, and a severity.

What trust actually means here

Not that nothing will be lost. Between one governed model and three SQL dialects, loss is a certainty — an enum cannot be enforced where the engine has no enum.

Trust is that every loss is declared with a path and a reason, every write is role-gated, every change to meaning is reviewable before it lands, every plan is replayable, and touching a real database is still done by your people through your process. That is what makes the reviewer's yes an informed one.

The endpoint reference, role matrix, and dry-run workflow are in the Schema Transformation guide in the CoreModels docs.