retrofit import
Import one or more .sql files into an oplog. Walks DDL in
retrofit's vocabulary and appends the equivalent op-add calls. A missing
schema is registered lazily, a populated oplog is extended by default, and
--force replaces its prior content. DML is set aside in data files; other
statements outside the vocabulary (sequences, GRANT/REVOKE, publications) are
reported on the stdout skip manifest and not recorded.
Source argument: one or more filesystem paths with a `.sql` extension,
or `-` for stdin (one source per invocation when reading stdin). When
multiple sources are passed they are imported as a single batch in
argv order with one validate, one event flush, and one atomic write.
Run with --help-all for the main in-vocabulary statements and walker
semantics.
Usage:
retrofit import <source>... [--schema <name>] [--from-file <path>] [flags]
Flags:
--allow-capability-gaps import anyway when capability-gap skips drop content: statements retrofit has no SMO for (exit 0 + warning instead of exit 2; the import-summary record is byte-identical either way); the one carve-out is validation-errors and rule-* refusals, which still abort
--allow-sealed-rewrite opid [sealed history] attest each Seal this run would demote or replace, by its full opid (repeatable, comma-separated); a value attests the Seal it names and selects nothing, and a Seal left unnamed refuses seal-removal-refused
--data-root data_root root that overrides the schema's data_root
--ddl-only decline every preserved-DML statement in the run (authored DML, dump data bands and synthesized floors): nothing is minted, the declined tables are listed, and the run exits on its DDL alone; orthogonal to --allow-capability-gaps, which covers DDL retrofit cannot represent
--disposition <replace|append> asserted <replace|append> disposition on every preserved-DML record
--dry-run run the full pipeline without writing the oplog file
--engine-version <15|16|17|18> supported major <15|16|17|18> for the imported schema (default: project engine_version)
--extra-seed-path seed_paths root that appends to the declared seed_paths
--force overwrite an existing populated oplog (mint fresh; a bare import extends instead)
--from-file <path> newline-delimited <path> listing .sql files (relative to the list's directory; # comments and blank lines ignored)
-h, --help help for import
--help-all show extended help (vocabulary, semantics, caveats)
--label <label> opaque <label> stamped onto every preserved-DML record (repeatable set; key:value accepted, not required)
--purge-orphaned-corpus consent to remove every corpus record whose anchor no longer exists in the replaced oplog (--force only; a same-anchor re-capture replaces its version in place)
--purge-superseded remove earlier same-chain data blocks superseded by this ingest (positional supersession within one continuous oplog; the anchor is alive at both positions); never a witnessed record, sealed or in the tail; each is reported kept. Default keeps them as --at render assets
--purpose <transition|cleanse> asserted <transition|cleanse> purpose on every preserved-DML record (one of: transition, cleanse; name a seed cohort with --label, and declare reference tables with --ref <table> or retrofit schema ref-tables add <table>)
--ref <table> declare <table> reference data (repeatable): a CreateTable this run mints is written reference_data=true; a table live at the run's starting cut gets SetReferenceData(value=true) appended after the run's DDL ops
--schema <name> registered schema <name> (lazy-creates when unregistered)
--seal --seal=<text> append a Seal op after the imported prefix (bare flag auto-derives reason from source(s); --seal=<text> overrides); refused as `op seal` refuses when the range holds an unwitnessed reference record, including one this import mints
--seal-per-file emit one Seal op after each source that minted ops (sources minting none are not sealed; equivalent to a bare '--seal' import per file)
--seed-path seed_paths root that replaces the declared seed_paths
--skip-manifest <path> write one JSONL record per skipped statement to <path> (kind/schema.name/line/raw_stmt_kind/raw_text)
--summary-file <path> write the aggregate import-summary record to <path> as one JSON object (imported/run_raw_script/skipped/skipped_by_kind[/skipped_by_class][/sealed][/sources_zero_op])
Global Flags:
--as string output mode: text | json (default "text")
-C, --chdir string treat <dir> as the working directory for the entire run
--config string retrofit.toml path (overrides discovery; also accepts $RETROFIT_CONFIG)
-f, --file string oplog file path; selects a registered [[schema]] path (overrides discovery)
--no-consolidate render every advisory occurrence instead of collapsing repeats
-q, --quiet count suppress the advisory tier on stderr; stack (-qq) to drop it whole (errors and results still print)
-v, --verbose reveal info-tier advisories on stderr
Docs: https://docs.retrofit.sh/current/commands/retrofit-import/retrofit import --help-all adds:
Extended help:
Schema registration is lazy: naming an unregistered target with
`--schema <name>` creates its registry entry and oplog before walking the SQL.
Use `retrofit schema create <schema>` first only when you want to choose schema
settings before import.
Multiple sources in one invocation: `retrofit import 01.sql 02.sql 03.sql --schema <name>`
imports all sources as a single batch in argv order (this is what the
shell produces from a glob like `retrofit import schema/*.sql --schema lib`).
The batch is all-or-nothing: parse and validation run over the
combined op stream, `appended` events flush only after the whole
batch passes, and the oplog file is written atomically once. If any
file fails, no ops are written and no events flush. Separate per-file
runs give the same ops in the same order as one multi-file run on a
fresh schema, though not the same bytes: each run mints its own element
ids.
Re-running import on a populated oplog EXTENDS it: the new ops chain
off the existing tail, and no flag is needed (e.g. `02_indexes.sql`
added after an initial `01_tables.sql` import). To re-bootstrap from
a different `.sql` instead, pass `--force` (the prior content is
discarded); it applies to the whole batch. A pg_dump of a schema
offered against a populated oplog refuses with
`state-source-on-populated-oplog`, and an oplog whose content does
not declare `--schema` refuses with `schema-not-in-oplog`.
The main in-vocabulary statements (the import walker mints ops for
these; docs/import.md lists them all):
- CREATE TABLE, including inline column constraints, generated
columns (GENERATED ALWAYS AS (<expr>) STORED), and identity
columns (GENERATED { BY DEFAULT | ALWAYS } AS IDENTITY).
- CREATE INDEX: btree, gin, gist, brin, hash, spgist; predicate
(WHERE) and access method (USING) bind as first-class params.
- ALTER TABLE … ADD CONSTRAINT { PRIMARY KEY | FOREIGN KEY |
CHECK (… IS NOT NULL …) }, including the legacy named NOT NULL
shape, which folds to one AddNotNull per column.
- ALTER TABLE … { ADD | DROP } COLUMN: cross-table DROP+ADD pairs
of an unreferenced column coalesce into a single MoveColumn op.
- ALTER TABLE … ALTER COLUMN: SET DEFAULT, DROP DEFAULT,
SET NOT NULL → AddNotNull, DROP NOT NULL → DropNotNull,
DROP EXPRESSION (clears a STORED generated expression; PG13+),
ADD GENERATED, DROP GENERATED, SET GENERATED (identity mutations).
- ALTER TABLE … ALTER CONSTRAINT … { DEFERRABLE | NOT DEFERRABLE }
and ALTER TABLE … RENAME CONSTRAINT.
- ALTER TABLE … REPLICA IDENTITY ….
- CREATE VIEW / CREATE OR REPLACE VIEW / DROP VIEW: bare
(single-schema) forms only; cross-schema views are skipped.
- ALTER TABLE … RENAME TO / ALTER TABLE … RENAME COLUMN /
ALTER INDEX … RENAME TO / ALTER VIEW … RENAME TO.
- COMMENT ON TABLE | COLUMN.
Preserved DML (the data corpus):
Custom DML (INSERT/UPDATE/DELETE/MERGE) is outside
retrofit's vocabulary but is NOT dropped. A run mints ONE DATA FILE
PER TABLE under the schema's declared data_root (the [[schema]]
data_root field; default: the oplog directory). The mint key is
(schema, table, label set, anchor-op, disposition), and a capture
larger than the page bound splits into deterministic .p<K> pages.
Bodies are verbatim and self-annotated. Each record carries a purpose
(classified --purpose=transition|cleanse > declared reference data
(--ref or schema ref-tables add) > --label cohort > unclassified), a
disposition (replace = full observed table state, append = increment;
a TRUNCATE/DELETE-then-INSERT reseed infers replace), an anchor-op
(a zero-DDL data import anchors at the current oplog head), and any
--label values. A pg_dump
-- TABLE DATA band is NOT captured: the run lists the band's tables
(data-band-listed) with a hint to `retrofit data capture <source>`
and mints nothing for them. data capture accepts INSERT-style dumps;
regenerate COPY input with pg_dump --inserts or --column-inserts first.
snapshot and migrate discover the corpus
automatically; re-capturing an occupied, unwitnessed mint key replaces
the version in place as a set (git retains the old bytes); a witnessed
key refuses. import never runs the data files.