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 {
|
pub fn create_migrations() -> Migrations {
|
||||||
let mut migs = Migrations::new();
|
let mut migs = Migrations::new();
|
||||||
migs.add(
|
migs.add(SimpleMigration::new_box(
|
||||||
1,
|
1,
|
||||||
SimpleMigration::new_box(
|
"CREATE TABLE sections (
|
||||||
"CREATE TABLE sections (
|
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
interface_id INTEGER 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
|
migs
|
||||||
}
|
}
|
||||||
|
@ -1,55 +1,76 @@
|
|||||||
|
use log::debug;
|
||||||
use rusqlite::NO_PARAMS;
|
use rusqlite::NO_PARAMS;
|
||||||
use rusqlite::{params, Connection};
|
use rusqlite::{params, Connection};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::ops::Bound::{Excluded, Unbounded};
|
use std::ops::Bound::{Excluded, Unbounded};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use log::debug;
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum MigrationError {
|
pub enum MigrationError {
|
||||||
#[error("sql error: {0}")]
|
#[error("sql error: {0}")]
|
||||||
SqlError(#[from] rusqlite::Error),
|
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")]
|
#[error("database version {0} too new to migrate")]
|
||||||
VersionTooNew(MigrationVersion),
|
VersionTooNew(MigrationVersion),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type MigrationResult<T> = Result<T, MigrationError>;
|
pub type MigrationResult<T> = Result<T, MigrationError>;
|
||||||
|
|
||||||
|
pub type MigrationVersion = u32;
|
||||||
|
|
||||||
pub trait Migration {
|
pub trait Migration {
|
||||||
|
fn version(&self) -> MigrationVersion;
|
||||||
fn up(&self, conn: &Connection) -> MigrationResult<()>;
|
fn up(&self, conn: &Connection) -> MigrationResult<()>;
|
||||||
fn down(&self, conn: &Connection) -> MigrationResult<()>;
|
fn down(&self, conn: &Connection) -> MigrationResult<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SimpleMigration {
|
pub struct SimpleMigration {
|
||||||
|
pub version: MigrationVersion,
|
||||||
pub up_sql: String,
|
pub up_sql: String,
|
||||||
pub down_sql: String,
|
pub down_sql: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SimpleMigration {
|
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 {
|
Self {
|
||||||
|
version,
|
||||||
up_sql: up_sql.to_string(),
|
up_sql: up_sql.to_string(),
|
||||||
down_sql: down_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> {
|
pub fn new_box<T1: ToString, T2: ToString>(
|
||||||
Box::new(Self::new(up_sql, down_sql))
|
version: MigrationVersion,
|
||||||
|
up_sql: T1,
|
||||||
|
down_sql: T2,
|
||||||
|
) -> Box<dyn Migration> {
|
||||||
|
Box::new(Self::new(version, up_sql, down_sql))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Migration for SimpleMigration {
|
impl Migration for SimpleMigration {
|
||||||
|
fn version(&self) -> MigrationVersion {
|
||||||
|
self.version
|
||||||
|
}
|
||||||
fn up(&self, conn: &Connection) -> MigrationResult<()> {
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn down(&self, conn: &Connection) -> MigrationResult<()> {
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type MigrationVersion = u32;
|
|
||||||
pub const NO_MIGRATIONS: MigrationVersion = 0;
|
pub const NO_MIGRATIONS: MigrationVersion = 0;
|
||||||
|
|
||||||
pub fn get_db_version(conn: &Connection) -> MigrationResult<MigrationVersion> {
|
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 (
|
CREATE TABLE IF NOT EXISTS db_version (
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
version INTEGER
|
version INTEGER
|
||||||
);", NO_PARAMS)?;
|
);",
|
||||||
|
NO_PARAMS,
|
||||||
|
)?;
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"
|
"
|
||||||
INSERT OR REPLACE INTO db_version (id, version)
|
INSERT OR REPLACE INTO db_version (id, version)
|
||||||
VALUES (1, ?1);",
|
VALUES (1, ?1);",
|
||||||
params![version])?;
|
params![version],
|
||||||
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,8 +121,9 @@ impl Migrations {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(&mut self, version: MigrationVersion, migration: Box<dyn Migration>) {
|
pub fn add(&mut self, migration: Box<dyn Migration>) {
|
||||||
self.migrations.insert(version, migration);
|
assert!(migration.version() != NO_MIGRATIONS, "migration has bad vesion");
|
||||||
|
self.migrations.insert(migration.version(), migration);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply(&self, conn: &mut Connection) -> MigrationResult<()> {
|
pub fn apply(&self, conn: &mut Connection) -> MigrationResult<()> {
|
||||||
@ -106,16 +131,17 @@ impl Migrations {
|
|||||||
if db_version != 0 && !self.migrations.contains_key(&db_version) {
|
if db_version != 0 && !self.migrations.contains_key(&db_version) {
|
||||||
return Err(MigrationError::VersionTooNew(db_version));
|
return Err(MigrationError::VersionTooNew(db_version));
|
||||||
}
|
}
|
||||||
let mig_range = self.migrations.range(
|
let mig_range = self.migrations.range((Excluded(db_version), Unbounded));
|
||||||
(Excluded(db_version), Unbounded));
|
|
||||||
let mut trans = conn.transaction()?;
|
let mut trans = conn.transaction()?;
|
||||||
let mut last_ver: MigrationVersion = 0;
|
let mut last_ver: MigrationVersion = NO_MIGRATIONS;
|
||||||
for (ver, mig) in mig_range {
|
for (ver, mig) in mig_range {
|
||||||
debug!("applying migration version {}", ver);
|
debug!("applying migration version {}", ver);
|
||||||
mig.up(&mut trans)?;
|
mig.up(&mut trans)?;
|
||||||
last_ver = *ver;
|
last_ver = *ver;
|
||||||
}
|
}
|
||||||
set_db_version(&trans, last_ver)?;
|
if last_ver != NO_MIGRATIONS {
|
||||||
|
set_db_version(&trans, last_ver)?;
|
||||||
|
}
|
||||||
trans.commit()?;
|
trans.commit()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user