ic_sqlite_vfs/db/
migrate.rs1use crate::db::connection::Connection;
7use crate::db::DbError;
8use std::collections::BTreeSet;
9
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11pub struct Migration {
12 pub version: u64,
13 pub sql: &'static str,
14}
15
16pub fn apply(connection: &Connection, migrations: &[Migration]) -> Result<(), DbError> {
17 validate_unique_versions(migrations)?;
18 connection.execute_batch(
19 "CREATE TABLE IF NOT EXISTS __ic_sqlite_migrations (
20 version INTEGER PRIMARY KEY NOT NULL
21 )",
22 )?;
23
24 for migration in migrations {
25 let version = sqlite_version(migration.version)?;
26 let exists = connection.query_scalar::<i64>(
27 &format!(
28 "SELECT EXISTS(SELECT 1 FROM __ic_sqlite_migrations WHERE version = {version})"
29 ),
30 crate::params![],
31 )?;
32 if exists != 0 {
33 continue;
34 }
35 connection.execute_batch(migration.sql)?;
36 connection.execute_batch(&format!(
37 "INSERT INTO __ic_sqlite_migrations(version) VALUES ({version})"
38 ))?;
39 }
40
41 Ok(())
42}
43
44fn validate_unique_versions(migrations: &[Migration]) -> Result<(), DbError> {
45 let mut seen = BTreeSet::new();
46 for migration in migrations {
47 if !seen.insert(migration.version) {
48 return Err(DbError::DuplicateMigrationVersion(migration.version));
49 }
50 }
51 Ok(())
52}
53
54fn sqlite_version(version: u64) -> Result<i64, DbError> {
55 i64::try_from(version).map_err(|_| DbError::MigrationVersionOutOfRange(version))
56}