Sequences
PostgreSQL is the only supported database with standalone sequences. Besu models one as a
Sequence and diffs it like any other object; SQLite and MySQL have none and render nothing for
one. The sequence behind a SERIAL or IDENTITY column is a different thing, modelled by the
column's auto_increment flag.
Declaring a sequence
Sequence::new fills in PostgreSQL's defaults: bigint, start 1, increment 1, cache 1, no cycle,
and no explicit bounds. The fields are:
min and max are Option<i64>. None means "the default for this data type", which is what
PostgreSQL reports back after creating the sequence, so a declaration that leaves them out
converges against a database that has a concrete bound.
What push does
A sequence is created before the tables that might default to it, and dropped after them. The
alter statement describes the whole desired sequence, including NO MINVALUE, NO MAXVALUE and
NO CYCLE when a bound has been removed: there is no partial form that converges otherwise.
What introspection recognises
pg_sequences lists every sequence outside the system schemas. A sequence that a SERIAL or an
identity column owns is skipped: it belongs to its column, and push would otherwise try to
create or drop a sequence the column needs. The distinction is pg_depend: an auto dependency
(a) is a SERIAL, an internal one (i) is an identity column.
A SERIAL or identity column itself is still reported, as auto_increment:
information_schema.columns.is_identityisYES, which is a modern identity column;column_defaultstarts withnextval(, which is what aSERIALcolumn looks like.
Both clear the column's default, because the sequence owns the value.
Calling nextval and setval
There is no typed builder for a sequence function; a sequence.next() expression would be a later
addition. Both are reachable through sql!:
nextval and setval return bigint, so i64 is the row type. The sequence name is a string
literal inside the SQL, not a bound parameter, because PostgreSQL resolves it as a regclass.
Gotchas
pushnever emitsSERIAL. A pulledSERIALcolumn is re-created as an identity column on a fresh database. That is a different catalog representation with the same behaviour, but it is not the DDL you wrote originally.- A hand-written
DEFAULT nextval(...)is reinterpreted. Introspection sees the default, marks the columnauto_increment, and drops the default from the snapshot.pushandgeneratetherefore cannot reproduce that spelling; keep it in a migration. OWNED BYis not modelled. A sequence tied to a column withOWNED BYis skipped by introspection (it is the column's sequence), andpushnever writesOWNED BYitself.nextvalis not transactional. A value consumed inside a transaction that rolls back is gone, so gaps are normal and should not be read as missing rows.- SQLite and MySQL render no sequence statements. A snapshot shared with PostgreSQL still pushes to them: the sequence changes are simply skipped. Neither database introspects one, so a snapshot declaring one will report the difference on every push.
- Altering a sequence is
Risky. Narrowing a bound or changing the type can makenextvalfail for values that are already handed out.
