retrofit0.10.0-wip.8

PostgreSQL versions

This page says which PostgreSQL majors this build accepts, what each verb does at each one, and what the engine version governs. Only the major is modeled. Each guide states its own verb’s row and links here.

Levels

MajorLevelWhat it means
17Default, supportedThe value init writes to [project] engine_version, and the one a schema resolves to when nothing names another.
15, 16SupportedEvery verb works at the pin, and snapshot and migrate emit the same SQL as at 17. The known gaps apply.
18Accepted, not supportedTaken only as an explicit pin. op add, validate, snapshot and migrate honor it; import and drift read PostgreSQL 17 syntax only, and several PostgreSQL 18 surfaces refuse or do not render.
Any other valueRefusedunknown-engine-version. The value is a bare major: 14, 19, 18.1, 018, v18 and 18 all refuse.

An explicit pin is any of:

  • schema create --engine-version <major> or import --engine-version <major>, which set a new schema’s pin and emit target together;
  • schema engine <major>, which moves an existing schema’s pin and emit target (--config-only moves the target alone);
  • engine_version under [project] (the value a new schema takes when no flag names one) or under a [[schema]] row (that schema’s emit target);
  • --using=postgres:<major> on snapshot or migrate, which sets the emit target for that run.

What the engine version governs

Each schema carries two values.

  • The pin is the oplog’s # engine-version: <major> header: the major production runs. op add refuses an op gated above the pin with engine-version-required, validate reports one already in the log with unsupported-on-engine-version, and drift refuses a candidate gated above it with unsupported-on-engine-version.
  • The emit target is the major snapshot and migrate write SQL for. It resolves from --using=postgres:<major>, then the schema’s [[schema]] engine_version, then [project] engine_version, then 17. The snapshot banner names it (engine_version=<major>) and every migrate file opens with -- engine: postgres <major>. A cut holding an op gated above the target refuses with unsupported-on-engine-version.

The target may not lag the pin: a schema whose [[schema]] engine_version is below its oplog’s header fails at config load with engine-version-target-below-oplog.

No command connects to a database, so neither value is read from a server. The header line states the major the script was written for; PostgreSQL at that major decides whether its DDL is valid.

Per verb

15, 16 and 17

No operator or parameter is gated inside this range. snapshot and migrate emit the same SQL at each of the three majors; only the banner and the header line name the major. The known gaps for 15 and 16 are the exceptions.

18

The PostgreSQL 18 surfaces are the SetConstraintEnforced and SetConstraintInherit operators, and these parameter values: generated_kind=virtual on AddColumn; enforced=false on AddForeignKey and AddCheck; period=true on AddForeignKey; and without_overlaps=true on AddPrimaryKey, AddUnique and ChangePrimaryKey. Below 18, op add refuses each of them with engine-version-required.

VerbAt 18
op addAppends SetConstraintEnforced (on a foreign key), SetConstraintInherit and a VIRTUAL generated column. The enforced=false, period=true and without_overlaps=true values still refuse, with constraint-facet-uncarried: the oplog has no place to carry them. A DropForeignKey whose reverse re-adds a partitioned table’s key NOT VALID appends; below 18 it refuses with reverse-policy-partitioned-fk.
validatePasses a log holding the PostgreSQL 18 surfaces. Under a lower pin it reports each with unsupported-on-engine-version.
snapshotRenders SetConstraintEnforced value=false as NOT ENFORCED on the foreign key. Emits nothing for SetConstraintInherit. Writes a VIRTUAL generated column as STORED.
migrateRenders a VIRTUAL generated column as VIRTUAL. Renders neither SetConstraintEnforced nor SetConstraintInherit: forward, a window holding one refuses with migrate-blocked-on-unsupported-smo, and so does a --down that would set a key’s enforceability back; a --down over a window holding SetConstraintInherit refuses with migrate-non-reversible. Renders a NOT VALID foreign key on a partitioned table, which 15 to 17 refuse with migrate-blocked-on-partitioned-fk-not-valid.
import, driftAccept an 18 pin, but parse with the PostgreSQL 17 grammar. PostgreSQL 18 syntax (NOT ENFORCED, VIRTUAL generated columns, WITHOUT OVERLAPS, PERIOD foreign keys, ALTER CONSTRAINT … [NO] INHERIT) does not parse: the run exits 1 with parse-error.

SetConstraintInherit records lineage only: no verb emits DDL for it.

Testing

Continuous integration runs the live-server test suite once per major, against a PostgreSQL 15, 16, 17 and 18 server. The SQL a test emits comes from the oplog’s engine version, not from the server, and most tests use the default 17, so on 15, 16 and 18 the run shows that the server accepts the SQL 17 does. The tests that pin 18 to apply a PostgreSQL 18 surface run against the 18 server only.

Known gaps

15 and 16:

  • The AttachPartition column check applies PostgreSQL 17’s rules at every pin. A partition that brings its own identity column attaches on 15 and 16, and a STORED generated partition column under a plain parent column attaches on 15; import and op add refuse both, naming attach-partition-columns-mismatch.
  • The same check does not compare generated expressions, so a 15 pin accepts a partition whose generated expression differs from its parent’s. PostgreSQL 15 refuses that ATTACH, so the emitted script fails on a 15 server.

18:

  • enforced=false, period=true and without_overlaps=true refuse with constraint-facet-uncarried.
  • migrate renders neither SetConstraintEnforced nor SetConstraintInherit, and no verb emits DDL for SetConstraintInherit.
  • snapshot writes a VIRTUAL generated column as STORED.
  • import and drift cannot read PostgreSQL 18 syntax.

Before 1.0, any release may change behavior; the release notes list what changed.