Sqitch: Database Change Management Without Version Numbers
How Sqitch plans, deploys, reverts and verifies database changes with named dependencies instead of version numbers, and when it is worth the learning curve.
A sqitch.plan with deploy, revert and verify scripts, and dependencies instead of version numbers. SchemaDrawer runs Sqitch v1.6.1 with DBD::Pg against a throw-away database of the engine you choose and returns an SVG of the schema it produces.
Upload the Sqitch project: sqitch.plan at the top with the deploy/, revert/ and verify/ directories. The engine, target and registry are set by SchemaDrawer, so a sqitch.conf may come along but is not obeyed.
The sample below is the PetClinic schema written for Sqitch, one of the sets SchemaDrawer's own tests draw on every engine:
sqitch/ ├── deploy/ │ ├── owners_and_pets.sql │ ├── vets.sql │ └── visits.sql ├── revert/ │ ├── owners_and_pets.sql │ ├── vets.sql │ └── visits.sql ├── verify/ │ ├── owners_and_pets.sql │ ├── vets.sql │ └── visits.sql ├── sqitch.conf └── sqitch.plan
SQL scripts for the chosen engine, plus the plan file.
sqitch deploy --mode change. The database is a fresh PostgreSQL, MySQL, MariaDB, SQL Server, Oracle or SQLite container in the version you ask for; nothing of yours is touched.
PostgreSQL 10, 11, 12, 13, 14, 15, 16, 17, 18 MySQL 8.0, 8.4, 9 MariaDB 10.6, 11.4, 11.8, 12 SQL Server Oracle 21, 23 SQLite
Not offered for Sqitch: SQL Server. The tool ships no driver for it, so nothing could run.
migrationType=SQITCHOwners, pets, types, vets, specialties and visits: the same seven tables every sample set on this site creates. These are the exact files the tests upload.
-- Deploy petclinic:owners_and_pets to pg
-- Deploy petclinic:owners_and_pets to mysql
CREATE TABLE owners
(
id INT NOT NULL,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
address VARCHAR(255),
city VARCHAR(80),
telephone VARCHAR(20),
CONSTRAINT pk_owners PRIMARY KEY (id)
);
CREATE TABLE types
(
id INT NOT NULL,
name VARCHAR(80) NOT NULL,
CONSTRAINT pk_types PRIMARY KEY (id)
);
CREATE TABLE pets
(
id INT NOT NULL,
name VARCHAR(50) NOT NULL,
birth_date DATE,
type_id INT NOT NULL,
owner_id INT NOT NULL,
CONSTRAINT pk_pets PRIMARY KEY (id),
CONSTRAINT fk_pets_types FOREIGN KEY (type_id) REFERENCES types (id),
CONSTRAINT fk_pets_owners FOREIGN KEY (owner_id) REFERENCES owners (id)
);
-- Deploy petclinic:vets
CREATE TABLE vets
(
id INT NOT NULL,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
CONSTRAINT pk_vets PRIMARY KEY (id)
);
CREATE TABLE specialties
(
id INT NOT NULL,
name VARCHAR(80) NOT NULL,
CONSTRAINT pk_specialties PRIMARY KEY (id)
);
CREATE TABLE vet_specialties
(
vet_id INT NOT NULL,
specialty_id INT NOT NULL,
CONSTRAINT pk_vet_specialties PRIMARY KEY (vet_id, specialty_id),
CONSTRAINT fk_vet_specialties_vets FOREIGN KEY (vet_id) REFERENCES vets (id),
CONSTRAINT fk_vet_specialties_specialties FOREIGN KEY (specialty_id) REFERENCES specialties (id)
);
-- Deploy petclinic:visits
CREATE TABLE visits
(
id INT NOT NULL,
pet_id INT NOT NULL,
visit_date DATE,
description VARCHAR(255),
CONSTRAINT pk_visits PRIMARY KEY (id),
CONSTRAINT fk_visits_pets FOREIGN KEY (pet_id) REFERENCES pets (id)
);
-- Revert petclinic:owners_and_pets
DROP TABLE pets;
DROP TABLE types;
DROP TABLE owners;
-- Revert petclinic:vets
DROP TABLE vet_specialties;
DROP TABLE specialties;
DROP TABLE vets;
-- Revert petclinic:visits
DROP TABLE visits;
[core]
engine = pg
# plan_file = sqitch.plan
# top_dir = .
# [engine "pg"]
# target = db:pg:
# registry = sqitch
# client = psql
%syntax-version=1.0.0
%project=petclinic
%uri=https://schemadrawer.com/petclinic
owners_and_pets 2024-01-01T00:00:00Z schemadrawer <[email protected]> # Create owners, types and pets.
vets [owners_and_pets] 2024-01-01T00:01:00Z schemadrawer <[email protected]> # Create vets, specialties and vet_specialties.
visits [vets] 2024-01-01T00:02:00Z schemadrawer <[email protected]> # Create visits.
-- Verify petclinic:owners_and_pets
SELECT id, first_name, last_name, address, city, telephone FROM owners WHERE 0 = 1;
SELECT id, name FROM types WHERE 0 = 1;
SELECT id, name, birth_date, type_id, owner_id FROM pets WHERE 0 = 1;
-- Verify petclinic:vets
SELECT id, first_name, last_name FROM vets WHERE 0 = 1;
SELECT id, name FROM specialties WHERE 0 = 1;
SELECT vet_id, specialty_id FROM vet_specialties WHERE 0 = 1;
-- Verify petclinic:visits
SELECT id, pet_id, visit_date, description FROM visits WHERE 0 = 1;
Every variant of the sample produces these seven tables. This diagram was drawn from the Flyway variant on PostgreSQL 18; on MySQL the types read INT and VARCHAR, on Oracle the names are upper case.
One multipart request queues the drawing; poll the job until it succeeds and download the SVG. Or let the GitHub Action do it and open a pull request with the diagram.
curl -X POST https://schemadrawer.com/api/v1/drawing \
-H "Request-Id: $(uuidgen)" \
-F "userId=5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f" \
-F "migrationType=SQITCH" \
-F "databaseType=POSTGRESQL" \
-F "version=18" \
-F "files=@sqitch/deploy/owners_and_pets.sql;filename=deploy/owners_and_pets.sql" \
-F "files=@sqitch/deploy/vets.sql;filename=deploy/vets.sql" \
-F "files=@sqitch/deploy/visits.sql;filename=deploy/visits.sql" \
-F "files=@sqitch/revert/owners_and_pets.sql;filename=revert/owners_and_pets.sql" \
-F "files=@sqitch/revert/vets.sql;filename=revert/vets.sql" \
-F "files=@sqitch/revert/visits.sql;filename=revert/visits.sql" \
-F "files=@sqitch/sqitch.conf" \
-F "files=@sqitch/sqitch.plan" \
-F "files=@sqitch/verify/owners_and_pets.sql;filename=verify/owners_and_pets.sql" \
-F "files=@sqitch/verify/vets.sql;filename=verify/vets.sql" \
-F "files=@sqitch/verify/visits.sql;filename=verify/visits.sql"
# {"jobId":"0b6e2f4c-8d1a-4c3e-9f7a-2a5b6c7d8e9f"}
Then poll GET https://schemadrawer.com/api/v1/drawing/{jobId} until status is SUCCESS and fetch diagramUrl. Details in the getting started guide and the API reference.
- uses: schemadrawercom/action@v1
with:
sql-directory: sqitch
output-path: docs/schema-diagram.svg
migration-type: SQITCH
database-type: POSTGRESQL
database-version: '18'
github-token: ${{ secrets.GITHUB_TOKEN }}
The action uploads the directory, waits for the diagram and opens a pull request with the SVG. See the action reference.
changes, dependencies, events, projects, releases, tags) are left out of the diagram. A table of your own with one of those names would disappear too.How Sqitch plans, deploys, reverts and verifies database changes with named dependencies instead of version numbers, and when it is worth the learning curve.
Editing applied migrations, environment-specific scripts, ORM auto-DDL in production, untested rollbacks: ten habits that break deployments, with a fix for each.
How Diesel manages a schema in Rust: the CLI, timestamped folders with up.sql and down.sql, the versions table, the generated schema.rs and embedded migrations.
Upload the migration directory, get an SVG back. Free while we build it.