Fix a few things with migrations
This commit is contained in:
parent
07785918c1
commit
6bbb559356
22
src/db.rs
22
src/db.rs
@ -2,16 +2,26 @@ use crate::migrations::{Migrations, SimpleMigration};
|
||||
|
||||
pub fn create_migrations() -> Migrations {
|
||||
let mut migs = Migrations::new();
|
||||
migs.add(
|
||||
migs.add(SimpleMigration::new_box(
|
||||
1,
|
||||
SimpleMigration::new_box(
|
||||
"CREATE TABLE sections (
|
||||
"CREATE TABLE sections (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
interface_id INTEGER NOT NULL
|
||||
);",
|
||||
"DROP TABLE sections;",
|
||||
),
|
||||
);
|
||||
"DROP TABLE sections;",
|
||||
));
|
||||
migs.add(SimpleMigration::new_box(
|
||||
2,
|
||||
"INSERT INTO sections (id, name, interface_id)
|
||||
VALUES
|
||||
(1, 'Front Yard Middle', 0),
|
||||
(2, 'Front Yard Left', 1),
|
||||
(3, 'Front Yard Right', 2),
|
||||
(4, 'Back Yard Middle', 3),
|
||||
(5, 'Back Yard Sauna', 4),
|
||||
(6, 'Garden', 5);",
|
||||
"DELETE FROM sections;",
|
||||
));
|
||||
migs
|
||||
}
|
||||
|
@ -1,55 +1,76 @@
|
||||
use log::debug;
|
||||
use rusqlite::NO_PARAMS;
|
||||
use rusqlite::{params, Connection};
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::Bound::{Excluded, Unbounded};
|
||||
use thiserror::Error;
|
||||
use log::debug;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum MigrationError {
|
||||
#[error("sql error: {0}")]
|
||||
SqlError(#[from] rusqlite::Error),
|
||||
#[error("migration version {0} up failed, sql error: {1}")]
|
||||
MigrationUpFailed(MigrationVersion, rusqlite::Error),
|
||||
#[error("migration version {0} down failed, sql error: {1}")]
|
||||
MigrationDownFailed(MigrationVersion, rusqlite::Error),
|
||||
#[error("database version {0} too new to migrate")]
|
||||
VersionTooNew(MigrationVersion),
|
||||
}
|
||||
|
||||
pub type MigrationResult<T> = Result<T, MigrationError>;
|
||||
|
||||
pub type MigrationVersion = u32;
|
||||
|
||||
pub trait Migration {
|
||||
fn version(&self) -> MigrationVersion;
|
||||
fn up(&self, conn: &Connection) -> MigrationResult<()>;
|
||||
fn down(&self, conn: &Connection) -> MigrationResult<()>;
|
||||
}
|
||||
|
||||
pub struct SimpleMigration {
|
||||
pub version: MigrationVersion,
|
||||
pub up_sql: String,
|
||||
pub down_sql: String,
|
||||
}
|
||||
|
||||
impl SimpleMigration {
|
||||
pub fn new<T1: ToString, T2: ToString>(up_sql: T1, down_sql: T2) -> Self {
|
||||
pub fn new<T1: ToString, T2: ToString>(
|
||||
version: MigrationVersion,
|
||||
up_sql: T1,
|
||||
down_sql: T2,
|
||||
) -> Self {
|
||||
Self {
|
||||
version,
|
||||
up_sql: up_sql.to_string(),
|
||||
down_sql: down_sql.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_box<T1: ToString, T2: ToString>(up_sql: T1, down_sql: T2) -> Box<dyn Migration> {
|
||||
Box::new(Self::new(up_sql, down_sql))
|
||||
pub fn new_box<T1: ToString, T2: ToString>(
|
||||
version: MigrationVersion,
|
||||
up_sql: T1,
|
||||
down_sql: T2,
|
||||
) -> Box<dyn Migration> {
|
||||
Box::new(Self::new(version, up_sql, down_sql))
|
||||
}
|
||||
}
|
||||
|
||||
impl Migration for SimpleMigration {
|
||||
fn version(&self) -> MigrationVersion {
|
||||
self.version
|
||||
}
|
||||
fn up(&self, conn: &Connection) -> MigrationResult<()> {
|
||||
conn.execute(&self.up_sql, NO_PARAMS)?;
|
||||
conn.execute_batch(&self.up_sql)
|
||||
.map_err(|sql_err| MigrationError::MigrationUpFailed(self.version, sql_err))?;
|
||||
Ok(())
|
||||
}
|
||||
fn down(&self, conn: &Connection) -> MigrationResult<()> {
|
||||
conn.execute(&self.down_sql, NO_PARAMS)?;
|
||||
conn.execute_batch(&self.down_sql)
|
||||
.map_err(|sql_err| MigrationError::MigrationDownFailed(self.version, sql_err))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub type MigrationVersion = u32;
|
||||
pub const NO_MIGRATIONS: MigrationVersion = 0;
|
||||
|
||||
pub fn get_db_version(conn: &Connection) -> MigrationResult<MigrationVersion> {
|
||||
@ -77,12 +98,15 @@ pub fn set_db_version(conn: &Connection, version: MigrationVersion) -> Migration
|
||||
CREATE TABLE IF NOT EXISTS db_version (
|
||||
id INTEGER PRIMARY KEY,
|
||||
version INTEGER
|
||||
);", NO_PARAMS)?;
|
||||
);",
|
||||
NO_PARAMS,
|
||||
)?;
|
||||
conn.execute(
|
||||
"
|
||||
INSERT OR REPLACE INTO db_version (id, version)
|
||||
VALUES (1, ?1);",
|
||||
params![version])?;
|
||||
params![version],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -97,8 +121,9 @@ impl Migrations {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, version: MigrationVersion, migration: Box<dyn Migration>) {
|
||||
self.migrations.insert(version, migration);
|
||||
pub fn add(&mut self, migration: Box<dyn Migration>) {
|
||||
assert!(migration.version() != NO_MIGRATIONS, "migration has bad vesion");
|
||||
self.migrations.insert(migration.version(), migration);
|
||||
}
|
||||
|
||||
pub fn apply(&self, conn: &mut Connection) -> MigrationResult<()> {
|
||||
@ -106,16 +131,17 @@ impl Migrations {
|
||||
if db_version != 0 && !self.migrations.contains_key(&db_version) {
|
||||
return Err(MigrationError::VersionTooNew(db_version));
|
||||
}
|
||||
let mig_range = self.migrations.range(
|
||||
(Excluded(db_version), Unbounded));
|
||||
let mig_range = self.migrations.range((Excluded(db_version), Unbounded));
|
||||
let mut trans = conn.transaction()?;
|
||||
let mut last_ver: MigrationVersion = 0;
|
||||
let mut last_ver: MigrationVersion = NO_MIGRATIONS;
|
||||
for (ver, mig) in mig_range {
|
||||
debug!("applying migration version {}", ver);
|
||||
mig.up(&mut trans)?;
|
||||
last_ver = *ver;
|
||||
}
|
||||
set_db_version(&trans, last_ver)?;
|
||||
if last_ver != NO_MIGRATIONS {
|
||||
set_db_version(&trans, last_ver)?;
|
||||
}
|
||||
trans.commit()?;
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user