7a80e88
ZhenYi
feat(schema): enhance schema synchronization with fingerprinting and state management
7a80e88
use std::hash::{DefaultHasher, Hash, Hasher};
use sea_orm::{ConnectionTrait, DatabaseBackend, DatabaseConnection, Statement};
const SCHEMA_PRE_SYNC: &str = include_str!("../../schema_pre_sync.sql");
const SCHEMA_EXTRAS: &str = include_str!("../../schema_extras.sql");
const SYNC_FORMAT_VERSION: u32 = 1;
const SYNC_STATE_DDL: &str = "CREATE TABLE IF NOT EXISTS schema_sync_state (
id smallint PRIMARY KEY CHECK (id = 1),
fingerprint text NOT NULL,
synced_at timestamptz NOT NULL DEFAULT now()
)";
pub async fn sync_schema(db: &DatabaseConnection) -> Result<(), sea_orm::DbErr> {
let builder = db.get_schema_registry("models::");
if db.get_database_backend() != DatabaseBackend::Postgres {
return builder.sync(db).await;
}
db.execute_unprepared(SYNC_STATE_DDL).await?;
let mut hasher = DefaultHasher::new();
SYNC_FORMAT_VERSION.hash(&mut hasher);
SCHEMA_PRE_SYNC.hash(&mut hasher);
SCHEMA_EXTRAS.hash(&mut hasher);
format!("{builder:?}").hash(&mut hasher);
let fingerprint = format!("{:016x}", hasher.finish());
let stored = db
.query_one_raw(Statement::from_string(
DatabaseBackend::Postgres,
"SELECT fingerprint FROM schema_sync_state WHERE id = 1",
))
.await?;
if let Some(row) = stored {
if row.try_get::<String>("", "fingerprint")? == fingerprint {
return Ok(());
}
}
db.execute_unprepared(SCHEMA_PRE_SYNC).await?;
builder.sync(db).await?;
db.execute_unprepared(SCHEMA_EXTRAS).await?;
let update = format!(
"INSERT INTO schema_sync_state (id, fingerprint) VALUES (1, '{fingerprint}') \
ON CONFLICT (id) DO UPDATE SET fingerprint = EXCLUDED.fingerprint, synced_at = now()"
);
db.execute_unprepared(&update).await?;
Ok(())
}