Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support naming migrations sequentially #2602

Merged
merged 3 commits into from Jul 24, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion sqlx-cli/src/lib.rs
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
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
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.
vmax marked this conversation as resolved.
Show resolved Hide resolved
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
abonander marked this conversation as resolved.
Show resolved Hide resolved
#[clap(short, long)]
timestamp: bool,

/// If true, migrations are named sequentially
vmax marked this conversation as resolved.
Show resolved Hide resolved
#[clap(short, long, conflicts_with = "timestamp")]
sequential: bool,
},

/// Run all pending migrations.
Expand Down