use crate::{schema, DBResult, Transaction};
pub fn check_schema<S: schema::Schema>(txn: &mut Transaction) -> DBResult<()> {
let raw_schema = crate::db::get_raw_schema(txn.lease())?;
let generated = schema::build::generate_from_schema::<S>();
let metagenerated = schema::build::generate_single_entity_table::<schema::meta::MicrormMeta>();
let mut tablecount = 0;
let mut indexcount = 0;
for rs in raw_schema {
if rs.type_ == "table" {
if rs.tbl_name == "microrm_meta" {
if metagenerated != rs.sql {
log::error!("Metadata schema inconsistent between DB and schema");
return Err(crate::Error::ConsistencyError(
"Table SQL mismatch for metadata".to_string(),
));
}
} else if Some(&rs.sql) != generated.table_queries().get(&rs.tbl_name) {
log::error!("Mismatch in SQL for {}", rs.tbl_name);
log::trace!(
"Should be: {:?}",
generated.table_queries().get(&rs.tbl_name)
);
log::trace!("Found in DB: {:?}", rs.sql);
return Err(crate::Error::ConsistencyError(format!(
"Table SQL mismatch for {}",
rs.tbl_name
)));
} else {
tablecount += 1;
}
} else if rs.type_ == "index" {
if rs.name.starts_with("sqlite_autoindex") {
continue;
}
log::trace!("processing index {} on {}", rs.name, rs.tbl_name);
let Some(generated_sql) = generated.index_queries().get(&rs.name) else {
log::trace!("index queries: {:?}", generated.index_queries());
return Err(crate::Error::ConsistencyError(format!(
"Index SQL not present in generated queries for index {}",
rs.name
)));
};
if generated_sql == &rs.sql {
indexcount += 1;
} else {
log::debug!("generated sql: {generated_sql}");
log::debug!("present sql: {}", rs.sql);
return Err(crate::Error::ConsistencyError(format!(
"Index SQL mismatch for index {}",
rs.name
)));
}
} else {
log::warn!(
"unknown sqlite schema element type {} for {}",
rs.type_,
rs.name
);
}
}
if tablecount != generated.table_queries().len() {
log::error!(
"Incorrect number of tables in DB schema: should be {}, is {}",
generated.table_queries().len(),
tablecount
);
Err(crate::Error::ConsistencyError(
"Table count mismatch".to_string(),
))
} else if indexcount != generated.index_queries().len() {
log::error!(
"Incorrect number of indicies in DB schema: should be {}, is {}",
generated.index_queries().len(),
indexcount
);
Err(crate::Error::ConsistencyError(
"Index count mismatch".to_string(),
))
} else {
cfg_if::cfg_if! {
if #[cfg(feature = "track_typesig")] {
use crate::schema::{self, meta, DatabaseItem, typesig};
use crate::{query::Queryable, Error};
use std::collections::HashSet;
let meta = meta::MetadataDB::build(schema::BuildSeal::new());
let Some(stored_json) = meta
.metastore
.keyed(typesig::TYPE_SIGNATURES_KEY)
.get(txn)?
else {
log::warn!("No schema type signatures stored in database.");
return Ok(());
};
let stored_typesigs: typesig::BorrowedSignatureMap =
serde_json::from_str(stored_json.value.as_str()).map_err(|_| {
Error::InternalError("could not deserialize stored type signatures")
})?;
let csigs = typesig::collect_type_info::<S>();
let mut equal = HashSet::<&str>::new();
let mut mismatch = false;
for (name, tsig) in stored_typesigs.into_iter() {
let Some(csig) = csigs.get(name) else {
continue;
};
if &tsig != csig {
log::debug!("Different type signatures:");
log::debug!("stored: {tsig:#?}");
log::debug!("current: {csig:#?}");
mismatch = true;
} else {
equal.insert(name);
}
}
if mismatch {
Err(Error::IncompatibleSchema)
} else {
Ok(())
}
} else {
Ok(())
}
}
}
}