1use crate::ast::identifiers::ObjectId;
3use crate::model::constraint::ConstraintState;
4use crate::model::function::FunctionState;
5use crate::model::relation::RelationState;
6use crate::model::role::RoleState;
7use crate::model::schema::SchemaState;
8use crate::model::sequence::SequenceState;
9use crate::model::trigger::TriggerEnableMode;
10use crate::model::types::TypeState;
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15pub struct ForeignKeyCache {
16 pub constraint_name: String,
17 pub from_table: ObjectId,
18 pub to_table: ObjectId,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
22pub struct IndexCache {
23 pub index_id: ObjectId,
24 pub table_id: ObjectId,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct TriggerCache {
29 pub trigger_id: ObjectId,
30 pub table_id: ObjectId,
31 pub function_id: ObjectId,
32 pub enabled_mode: TriggerEnableMode,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct DependencyCache {
37 pub classid: u32,
38 pub objid: u32,
39 pub objsubid: i32,
40 pub refclassid: u32,
41 pub refobjid: u32,
42 pub refobjsubid: i32,
43 pub deptype: String,
44 pub obj_schema: Option<String>,
45 pub obj_name: Option<String>,
46 pub ref_schema: Option<String>,
47 pub ref_name: Option<String>,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize, Default)]
51pub struct CacheMetadata {
52 pub created_at_unix_secs: Option<u64>,
55 pub source_database: Option<String>,
58 pub source_role: Option<String>,
61 pub source_session_role: Option<String>,
64 pub source_search_path: Option<Vec<String>>,
66 pub schemas: Option<Vec<String>>,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct DbCache {
73 pub pg_version_num: Option<u32>,
74 pub metadata: CacheMetadata,
75 pub search_path: Vec<String>,
76 pub relations: HashMap<ObjectId, RelationState>,
77 pub foreign_keys: Vec<ForeignKeyCache>,
78 pub indexes: Vec<IndexCache>,
79 pub constraints: Vec<ConstraintState>,
80 pub triggers: Vec<TriggerCache>,
81 pub functions: HashMap<ObjectId, FunctionState>,
82 pub types: HashMap<ObjectId, TypeState>,
83 pub roles: HashMap<ObjectId, RoleState>,
84 pub schemas: HashMap<String, SchemaState>,
85 pub sequences: HashMap<ObjectId, SequenceState>,
86 pub dependencies: Vec<DependencyCache>,
87}
88
89pub const CACHE_FORMAT_VERSION: u32 = 5;
90
91pub const CACHE_V5_MAGIC: &[u8] = b"SMCACHE05";
92
93#[derive(Debug, Clone, Serialize, Deserialize)]
94pub enum DbCacheVersioned {
95 V1,
99 V2,
100 V3,
101 V4,
102 V5(Box<DbCache>),
103}
104
105impl DbCacheVersioned {
106 pub fn format_version(&self) -> u32 {
107 match self {
108 DbCacheVersioned::V1 => 1,
109 DbCacheVersioned::V2 => 2,
110 DbCacheVersioned::V3 => 3,
111 DbCacheVersioned::V4 => 4,
112 DbCacheVersioned::V5(_) => 5,
113 }
114 }
115
116 pub fn into_cache(self) -> Result<DbCache, String> {
117 match self {
118 DbCacheVersioned::V1
119 | DbCacheVersioned::V2
120 | DbCacheVersioned::V3
121 | DbCacheVersioned::V4 => Err(
122 "This cache format is unsupported. Run `safe-migrate sync` to rebuild it."
123 .to_string(),
124 ),
125 DbCacheVersioned::V5(c) => Ok(*c),
126 }
127 }
128}
129
130impl Default for DbCache {
131 fn default() -> Self {
132 Self::new()
133 }
134}
135
136impl DbCache {
137 pub fn new() -> Self {
138 Self {
139 pg_version_num: None,
140 metadata: CacheMetadata::default(),
141 search_path: vec!["public".to_string()],
142 relations: HashMap::new(),
143 foreign_keys: Vec::new(),
144 indexes: Vec::new(),
145 constraints: Vec::new(),
146 triggers: Vec::new(),
147 functions: HashMap::new(),
148 types: HashMap::new(),
149 roles: HashMap::new(),
150 schemas: HashMap::new(),
151 sequences: HashMap::new(),
152 dependencies: Vec::new(),
153 }
154 }
155
156 pub fn insert_baseline(&mut self, id: ObjectId, state: RelationState) {
157 self.relations.insert(id, state);
158 }
159
160 pub fn baseline_relations(&self) -> impl Iterator<Item = (&ObjectId, &RelationState)> {
161 self.relations.iter()
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168
169 #[test]
170 fn every_legacy_cache_variant_is_rejected_generically() {
171 for (versioned, expected_version) in [
172 (DbCacheVersioned::V1, 1),
173 (DbCacheVersioned::V2, 2),
174 (DbCacheVersioned::V3, 3),
175 (DbCacheVersioned::V4, 4),
176 ] {
177 assert_eq!(versioned.format_version(), expected_version);
178 assert_eq!(
179 versioned.into_cache().unwrap_err(),
180 "This cache format is unsupported. Run `safe-migrate sync` to rebuild it."
181 );
182 }
183 }
184
185 #[test]
186 fn current_cache_format_is_v5() {
187 assert_eq!(CACHE_FORMAT_VERSION, 5);
188 assert_eq!(DbCacheVersioned::V5(Box::default()).format_version(), 5);
189 assert_eq!(CACHE_V5_MAGIC, b"SMCACHE05");
190 }
191}