use super::exec;
use crate::database::migrations::CustomMigrationError;
use crate::database::DatabaseConnection;
pub const LEDGER_TABLE: &str = "_rustyroad_migrations";
const COLUMNS: &str = "name {name_type} NOT NULL,
applied_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
direction {direction_type} NOT NULL,
UNIQUE (name, direction)";
fn column_types(connection: &DatabaseConnection) -> (&'static str, &'static str, &'static str) {
match connection {
DatabaseConnection::Pg(_) => ("id SERIAL PRIMARY KEY", "VARCHAR(255)", "VARCHAR(10)"),
DatabaseConnection::MySql(_) => {
("id INT AUTO_INCREMENT PRIMARY KEY", "VARCHAR(255)", "VARCHAR(10)")
}
DatabaseConnection::Sqlite(_) => {
("id INTEGER PRIMARY KEY AUTOINCREMENT", "TEXT", "TEXT")
}
}
}
pub async fn ensure_table(connection: &DatabaseConnection) -> Result<(), CustomMigrationError> {
let (primary_key, name_type, direction_type) = column_types(connection);
let columns = COLUMNS
.replace("{name_type}", name_type)
.replace("{direction_type}", direction_type);
let sql = format!("CREATE TABLE IF NOT EXISTS {LEDGER_TABLE} ({primary_key}, {columns})");
exec::execute(connection, &sql, &[]).await?;
add_unique_index(connection).await;
Ok(())
}
async fn add_unique_index(connection: &DatabaseConnection) {
let guard = match connection {
DatabaseConnection::MySql(_) => "",
_ => "IF NOT EXISTS ",
};
let sql = format!(
"CREATE UNIQUE INDEX {guard}_rustyroad_migrations_name_direction_key \
ON {LEDGER_TABLE} (name, direction)"
);
if let Err(error) = exec::execute(connection, &sql, &[]).await {
eprintln!(
"Note: could not enforce UNIQUE (name, direction) on {LEDGER_TABLE}. \
The idempotency gate still works. Deduplicate the table to make it \
authoritative. Details: {error}"
);
}
}