use crate::{StoreError, error::database_error};
use rusqlite::Connection;
use std::path::Path;
pub(crate) mod layout;
pub const SCHEMA_VERSION: i64 = 4;
const UNSTAMPED_VERSION: i64 = 0;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Layout {
Empty,
Current,
}
pub(crate) fn stored_version_blocking(
connection: &Connection,
path: &Path,
) -> Result<i64, StoreError> {
connection
.query_row("PRAGMA user_version", [], |row| row.get(0))
.map_err(database_error(path, "read schema version"))
}
pub(crate) fn check_blocking(connection: &Connection, path: &Path) -> Result<Layout, StoreError> {
let found = stored_version_blocking(connection, path)?;
let retired = || StoreError::RetiredSchemaVersion {
path: path.to_path_buf(),
found,
current: SCHEMA_VERSION,
};
match found {
SCHEMA_VERSION => {}
UNSTAMPED_VERSION => {
let objects = layout::objects_blocking(connection, path)?;
if objects.is_empty() {
return Ok(Layout::Empty);
}
if !layout::has_table(&objects, "plan_state") {
layout::compare(path, found, &objects, layout::expected())?;
}
return Err(retired());
}
1..SCHEMA_VERSION => return Err(retired()),
_ => {
return Err(StoreError::UnsupportedSchemaVersion {
path: path.to_path_buf(),
found,
supported: SCHEMA_VERSION,
});
}
}
let objects = layout::objects_blocking(connection, path)?;
layout::compare(path, found, &objects, layout::expected())?;
Ok(Layout::Current)
}
pub(crate) fn create_blocking(connection: &Connection, path: &Path) -> Result<(), StoreError> {
connection
.execute_batch(&format!(
"{}; {}; {}; {}; PRAGMA user_version = {SCHEMA_VERSION};",
layout::PLAN_STATE,
layout::OPERATIONS,
layout::GENESIS,
layout::LINEAGE
))
.map_err(database_error(path, "initialize schema"))
}
#[cfg(test)]
mod tests;