Skip to content

Commit

Permalink
Support naming migrations sequentially and inferring naming scheme
Browse files Browse the repository at this point in the history
  • Loading branch information
vmax committed Jul 15, 2023
1 parent c70cfaf commit e275cd0
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 5 deletions.
4 changes: 3 additions & 1 deletion sqlx-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ pub async fn run(opt: Opt) -> Result<()> {
source,
description,
reversible,
} => migrate::add(&source, &description, reversible).await?,
sequential,
timestamp,
} => migrate::add(&source, &description, reversible, sequential, timestamp).await?,
MigrateCommand::Run {
source,
dry_run,
Expand Down
67 changes: 64 additions & 3 deletions sqlx-cli/src/migrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,70 @@ fn create_file(
Ok(())
}

enum MigrationOrdering {
Timestamp(String),
Sequential(String),
}

impl MigrationOrdering {
fn timestamp() -> MigrationOrdering {
Self::Timestamp(Utc::now().format("%Y%m%d%H%M%S").to_string())
}

fn sequential(version: i64) -> MigrationOrdering {
Self::Sequential(format!("{:04}", version))
}

fn file_prefix(&self) -> &str {
match self {
MigrationOrdering::Timestamp(prefix) => prefix,
MigrationOrdering::Sequential(prefix) => prefix,
}
}

fn infer(sequential: bool, timestamp: bool, migrator: &Migrator) -> Self {
match (timestamp, sequential) {
(true, true) => panic!("Impossible to specify both timestamp and sequential mode"),
(true, false) => MigrationOrdering::timestamp(),
(false, true) => MigrationOrdering::sequential(
migrator
.iter()
.last()
.map_or(1, |last_migration| last_migration.version + 1),
),
(false, false) => {
// inferring the naming scheme
let migrations = migrator.iter().rev().take(2).collect::<Vec<_>>();
if let [last, pre_last] = &migrations[..] {
// there are at least two migrations, compare the last twothere's only one existing migration
if last.version - pre_last.version == 1 {
// their version numbers differ by 1, infer sequential
MigrationOrdering::sequential(last.version + 1)
} else {
MigrationOrdering::timestamp()
}
} else if let [last] = &migrations[..] {
// there is only one existing migration
if last.version == 0 || last.version == 1 {
// infer sequential if the version number is 0 or 1
MigrationOrdering::sequential(last.version + 1)
} else {
MigrationOrdering::timestamp()
}
} else {
MigrationOrdering::timestamp()
}
}
}
}
}

pub async fn add(
migration_source: &str,
description: &str,
reversible: bool,
sequential: bool,
timestamp: bool,
) -> anyhow::Result<()> {
fs::create_dir_all(migration_source).context("Unable to create migrations directory")?;

Expand All @@ -50,15 +110,16 @@ pub async fn add(
.unwrap_or(false);

let migrator = Migrator::new(Path::new(migration_source)).await?;
// This checks if all existing migrations are of the same type as the reverisble flag passed
// This checks if all existing migrations are of the same type as the reversible flag passed
for migration in migrator.iter() {
if migration.migration_type.is_reversible() != reversible {
bail!(MigrateError::InvalidMixReversibleAndSimple);
}
}

let dt = Utc::now();
let file_prefix = dt.format("%Y%m%d%H%M%S").to_string();
let ordering = MigrationOrdering::infer(sequential, timestamp, &migrator);
let file_prefix = ordering.file_prefix();

if reversible {
create_file(
migration_source,
Expand Down
10 changes: 9 additions & 1 deletion sqlx-cli/src/opt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ pub struct MigrateOpt {
#[derive(Parser, Debug)]
pub enum MigrateCommand {
/// Create a new migration with the given description,
/// and the current time as the version.
/// and (by default) the current time as the version.
Add {
description: String,

Expand All @@ -121,6 +121,14 @@ pub enum MigrateCommand {
/// else creates a single sql file
#[clap(short)]
reversible: bool,

/// If true, migrations are named by timestamp
#[clap(short, long)]
timestamp: bool,

/// If true, migrations are named sequentially
#[clap(short, long, conflicts_with = "timestamp")]
sequential: bool,
},

/// Run all pending migrations.
Expand Down

0 comments on commit e275cd0

Please sign in to comment.