Diesel Migrations for Rust: up.sql, down.sql and schema.rs
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.
Rust's Diesel migrations: one directory per change with up.sql and down.sql. SchemaDrawer runs diesel_cli 2.3.13 with the postgres against a throw-away database of the engine you choose and returns an SVG of the schema it produces.
Upload the migrations directory: one folder per migration named <version>_<title>, each holding up.sql and, optionally, down.sql.
The sample below is the PetClinic schema written for Diesel, one of the sets SchemaDrawer's own tests draw on every engine:
migrations/
├── 2026-09-21-120000_create_owners_and_pets/
│ ├── down.sql
│ └── up.sql
├── 2026-09-21-120100_create_vets/
│ ├── down.sql
│ └── up.sql
└── 2026-09-21-120200_create_visits/
├── down.sql
└── up.sql
Plain SQL.
diesel migration run. 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 SQLite
Not offered for Diesel: SQL Server, Oracle. The tool ships no driver for them, so nothing could run.
migrationType=DIESELOwners, 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.
DROP TABLE pets;
DROP TABLE types;
DROP TABLE owners;
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)
);
DROP TABLE vet_specialties;
DROP TABLE specialties;
DROP TABLE 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)
);
DROP TABLE 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)
);
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=DIESEL" \
-F "databaseType=POSTGRESQL" \
-F "version=18" \
-F "files=@migrations/2026-09-21-120000_create_owners_and_pets/down.sql;filename=2026-09-21-120000_create_owners_and_pets/down.sql" \
-F "files=@migrations/2026-09-21-120000_create_owners_and_pets/up.sql;filename=2026-09-21-120000_create_owners_and_pets/up.sql" \
-F "files=@migrations/2026-09-21-120100_create_vets/down.sql;filename=2026-09-21-120100_create_vets/down.sql" \
-F "files=@migrations/2026-09-21-120100_create_vets/up.sql;filename=2026-09-21-120100_create_vets/up.sql" \
-F "files=@migrations/2026-09-21-120200_create_visits/down.sql;filename=2026-09-21-120200_create_visits/down.sql" \
-F "files=@migrations/2026-09-21-120200_create_visits/up.sql;filename=2026-09-21-120200_create_visits/up.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: migrations
output-path: docs/schema-diagram.svg
migration-type: DIESEL
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.
down.sql is never executed, a diesel.toml in the upload is not read and no schema.rs is generated.__diesel_schema_migrations table is left out of the diagram.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.
Editing applied migrations, environment-specific scripts, ORM auto-DDL in production, untested rollbacks: ten habits that break deployments, with a fix for each.
Doctrine Migrations in Symfony from setup to deploy: diff versus generate, the Version class, the versions table, reviewing generated SQL and running safely.
Upload the migration directory, get an SVG back. Free while we build it.