| Crates.io | parsql-migrations |
| lib.rs | parsql-migrations |
| version | 0.5.0 |
| created_at | 2025-09-29 15:15:04.804492+00 |
| updated_at | 2025-09-29 15:15:04.804492+00 |
| description | Migration system for parsql - type-safe database migrations |
| homepage | |
| repository | https://github.com/yazdostum-nettr/parsql |
| max_upload_size | |
| id | 1859774 |
| size | 298,042 |
A simple, type-safe database migration system for Rust that works with PostgreSQL and SQLite.
Add to your Cargo.toml:
[dependencies]
parsql-migrations = { version = "0.5", features = ["postgres", "sqlite"] }
use parsql_migrations::prelude::*;
pub struct CreateUsersTable;
impl Migration for CreateUsersTable {
fn version(&self) -> i64 {
20240101120000
}
fn name(&self) -> &str {
"create_users_table"
}
fn up(&self, conn: &mut dyn MigrationConnection) -> Result<()> {
conn.execute(
"CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)"
)
}
fn down(&self, conn: &mut dyn MigrationConnection) -> Result<()> {
conn.execute("DROP TABLE IF EXISTS users")
}
}
use postgres::{Client, NoTls};
use parsql_migrations::postgres_simple::PostgresMigrationConnection;
use parsql_migrations::{MigrationRunner, MigrationConfig};
let mut client = Client::connect("postgresql://localhost/mydb", NoTls)?;
let mut conn = PostgresMigrationConnection::new(&mut client);
let mut runner = MigrationRunner::new();
runner.add_migration(Box::new(CreateUsersTable));
let report = runner.run(&mut conn)?;
println!("Applied {} migrations", report.successful_count());
use rusqlite::Connection;
use parsql_migrations::sqlite_simple::SqliteMigrationConnection;
use parsql_migrations::MigrationRunner;
let mut sqlite_conn = Connection::open("app.db")?;
let mut conn = SqliteMigrationConnection::new(&mut sqlite_conn);
let mut runner = MigrationRunner::new();
runner.add_migration(Box::new(CreateUsersTable));
let report = runner.run(&mut conn)?;
Create migration files with timestamps:
migrations/20240101120000_create_users_table.up.sqlmigrations/20240101120000_create_users_table.down.sqlLoad from directory:
use parsql_migrations::FileMigration;
let migrations = FileMigration::from_directory("migrations")?;
for migration in migrations {
runner.add_migration(Box::new(migration));
}
use parsql_migrations::{MigrationConfig, TableConfig};
let config = MigrationConfig {
table: TableConfig {
table_name: "schema_migrations".to_string(),
version_column: "version".to_string(),
name_column: "name".to_string(),
applied_at_column: "applied_at".to_string(),
checksum_column: "checksum".to_string(),
execution_time_column: "execution_time_ms".to_string(),
},
verify_checksums: true,
allow_out_of_order: false,
transaction_per_migration: true,
};
let runner = MigrationRunner::with_config(config);
// Rollback to a specific version
let report = runner.rollback(&mut conn, 20240101000000)?;
println!("Rolled back {} migrations", report.successful_count());
// Get migration status
let statuses = runner.status(&mut conn)?;
for status in statuses {
println!("{}: {} - {}",
status.version,
status.name,
if status.is_applied { "Applied" } else { "Pending" }
);
}
// Validate migrations
runner.validate()?; // Checks for gaps and checksum mismatches
Install the CLI tool:
cargo install parsql-cli
Create a parsql.toml configuration file:
[database]
url = "postgresql://localhost/mydb"
[migrations]
directory = "migrations"
table_name = "schema_migrations"
verify_checksums = true
allow_out_of_order = false
Commands:
# Create a new migration
parsql migrate create "add users table" --migration-type sql
# Run pending migrations
parsql migrate run
# Check migration status
parsql migrate status --detailed
# Rollback to a specific version
parsql migrate rollback --to 20240101000000
# Validate migrations
parsql migrate validate --verify-checksums
Implement MigrationConnection for custom database adapters:
use parsql_migrations::{MigrationConnection, MigrationRecord, Result};
struct MyCustomConnection;
impl MigrationConnection for MyCustomConnection {
fn execute(&mut self, sql: &str) -> Result<()> {
// Execute SQL
Ok(())
}
fn database_type(&self) -> &str {
"custom"
}
fn query_migrations(&mut self, table_name: &str) -> Result<Vec<MigrationRecord>> {
// Query applied migrations
Ok(vec![])
}
}
Due to dyn trait limitations, async support is currently limited. Use sync adapters for now.
All operations return Result<T, MigrationError>:
match runner.run(&mut conn) {
Ok(report) => {
println!("Success: {} migrations applied", report.successful_count());
}
Err(MigrationError::GapDetected { missing_version }) => {
eprintln!("Missing migration version: {}", missing_version);
}
Err(e) => {
eprintln!("Migration failed: {}", e);
}
}
This project is part of the parsql workspace and shares its license.