use crate::ast::identifiers::ObjectId;
use crate::model::constraint::ConstraintState;
use crate::model::function::FunctionState;
use crate::model::relation::RelationState;
use crate::model::replication::{PublicationState, SubscriptionState};
use crate::model::role::RoleState;
use crate::model::schema::SchemaState;
use crate::model::sequence::SequenceState;
use crate::model::trigger::TriggerEnableMode;
use crate::model::types::TypeState;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ForeignKeyCache {
pub constraint_name: String,
pub from_table: ObjectId,
pub to_table: ObjectId,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct IndexCache {
pub index_id: ObjectId,
pub table_id: ObjectId,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TriggerCache {
pub trigger_id: ObjectId,
pub table_id: ObjectId,
pub function_id: ObjectId,
pub enabled_mode: TriggerEnableMode,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DependencyCache {
pub classid: u32,
pub objid: u32,
pub objsubid: i32,
pub refclassid: u32,
pub refobjid: u32,
pub refobjsubid: i32,
pub deptype: String,
pub obj_schema: Option<String>,
pub obj_name: Option<String>,
pub ref_schema: Option<String>,
pub ref_name: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CacheMetadata {
pub created_at_unix_secs: Option<u64>,
pub source_database: Option<String>,
pub source_role: Option<String>,
pub source_session_role: Option<String>,
pub source_search_path: Option<Vec<String>>,
pub source_lock_timeout_ms: u64,
pub source_statement_timeout_ms: u64,
pub schemas: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DbCache {
pub pg_version_num: Option<u32>,
pub metadata: CacheMetadata,
pub search_path: Vec<String>,
pub relations: HashMap<ObjectId, RelationState>,
pub foreign_keys: Vec<ForeignKeyCache>,
pub indexes: Vec<IndexCache>,
pub constraints: Vec<ConstraintState>,
pub triggers: Vec<TriggerCache>,
pub functions: HashMap<ObjectId, FunctionState>,
pub types: HashMap<ObjectId, TypeState>,
pub roles: HashMap<ObjectId, RoleState>,
pub schemas: HashMap<String, SchemaState>,
pub sequences: HashMap<ObjectId, SequenceState>,
pub dependencies: Vec<DependencyCache>,
pub publications: HashMap<String, PublicationState>,
pub subscriptions: HashMap<String, SubscriptionState>,
}
pub const CACHE_FORMAT_VERSION: u32 = 6;
pub const CACHE_V6_MAGIC: &[u8] = b"SMCACHE06";
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DbCacheVersioned {
V1,
V2,
V3,
V4,
V5(Box<DbCache>),
V6(Box<DbCache>),
}
impl DbCacheVersioned {
pub fn format_version(&self) -> u32 {
match self {
DbCacheVersioned::V1 => 1,
DbCacheVersioned::V2 => 2,
DbCacheVersioned::V3 => 3,
DbCacheVersioned::V4 => 4,
DbCacheVersioned::V5(_) => 5,
DbCacheVersioned::V6(_) => 6,
}
}
pub fn into_cache(self) -> Result<DbCache, String> {
match self {
DbCacheVersioned::V1
| DbCacheVersioned::V2
| DbCacheVersioned::V3
| DbCacheVersioned::V4
| DbCacheVersioned::V5(_) => Err(
"This cache format is unsupported. Run `safe-migrate sync` to rebuild it."
.to_string(),
),
DbCacheVersioned::V6(c) => Ok(*c),
}
}
}
impl Default for DbCache {
fn default() -> Self {
Self::new()
}
}
impl DbCache {
pub fn new() -> Self {
Self {
pg_version_num: None,
metadata: CacheMetadata::default(),
search_path: vec!["public".to_string()],
relations: HashMap::new(),
foreign_keys: Vec::new(),
indexes: Vec::new(),
constraints: Vec::new(),
triggers: Vec::new(),
functions: HashMap::new(),
types: HashMap::new(),
roles: HashMap::new(),
schemas: HashMap::new(),
sequences: HashMap::new(),
dependencies: Vec::new(),
publications: HashMap::new(),
subscriptions: HashMap::new(),
}
}
pub fn insert_baseline(&mut self, id: ObjectId, state: RelationState) {
self.relations.insert(id, state);
}
pub fn baseline_relations(&self) -> impl Iterator<Item = (&ObjectId, &RelationState)> {
self.relations.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_legacy_cache_variant_is_rejected_generically() {
for (versioned, expected_version) in [
(DbCacheVersioned::V1, 1),
(DbCacheVersioned::V2, 2),
(DbCacheVersioned::V3, 3),
(DbCacheVersioned::V4, 4),
] {
assert_eq!(versioned.format_version(), expected_version);
assert_eq!(
versioned.into_cache().unwrap_err(),
"This cache format is unsupported. Run `safe-migrate sync` to rebuild it."
);
}
let v5 = DbCacheVersioned::V5(Box::default());
assert_eq!(v5.format_version(), 5);
assert_eq!(
v5.into_cache().unwrap_err(),
"This cache format is unsupported. Run `safe-migrate sync` to rebuild it."
);
}
#[test]
fn current_cache_format_is_v6() {
assert_eq!(CACHE_FORMAT_VERSION, 6);
assert_eq!(DbCacheVersioned::V6(Box::default()).format_version(), 6);
assert_eq!(CACHE_V6_MAGIC, b"SMCACHE06");
}
}