1use crate::ast::identifiers::ObjectId;
2use crate::model::constraint::ConstraintState;
3use crate::model::function::FunctionState;
4use crate::model::relation::RelationState;
5use crate::model::replication::{PublicationState, SubscriptionState};
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 source_lock_timeout_ms: u64,
70 pub source_statement_timeout_ms: u64,
74 pub schemas: Option<Vec<String>>,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
80pub struct DbCache {
81 pub pg_version_num: Option<u32>,
82 pub metadata: CacheMetadata,
83 pub search_path: Vec<String>,
84 pub relations: HashMap<ObjectId, RelationState>,
85 pub foreign_keys: Vec<ForeignKeyCache>,
86 pub indexes: Vec<IndexCache>,
87 pub constraints: Vec<ConstraintState>,
88 pub triggers: Vec<TriggerCache>,
89 pub functions: HashMap<ObjectId, FunctionState>,
90 pub types: HashMap<ObjectId, TypeState>,
91 pub roles: HashMap<ObjectId, RoleState>,
92 pub schemas: HashMap<String, SchemaState>,
93 pub sequences: HashMap<ObjectId, SequenceState>,
94 pub dependencies: Vec<DependencyCache>,
95 pub publications: HashMap<String, PublicationState>,
96 pub subscriptions: HashMap<String, SubscriptionState>,
97}
98
99pub const CACHE_FORMAT_VERSION: u32 = 6;
100
101pub const CACHE_V6_MAGIC: &[u8] = b"SMCACHE06";
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
104pub enum DbCacheVersioned {
105 V1,
109 V2,
110 V3,
111 V4,
112 V5(Box<DbCache>),
113 V6(Box<DbCache>),
114}
115
116impl DbCacheVersioned {
117 pub fn format_version(&self) -> u32 {
118 match self {
119 DbCacheVersioned::V1 => 1,
120 DbCacheVersioned::V2 => 2,
121 DbCacheVersioned::V3 => 3,
122 DbCacheVersioned::V4 => 4,
123 DbCacheVersioned::V5(_) => 5,
124 DbCacheVersioned::V6(_) => 6,
125 }
126 }
127
128 pub fn into_cache(self) -> Result<DbCache, String> {
129 match self {
130 DbCacheVersioned::V1
131 | DbCacheVersioned::V2
132 | DbCacheVersioned::V3
133 | DbCacheVersioned::V4
134 | DbCacheVersioned::V5(_) => Err(
135 "This cache format is unsupported. Run `safe-migrate sync` to rebuild it."
136 .to_string(),
137 ),
138 DbCacheVersioned::V6(c) => Ok(*c),
139 }
140 }
141}
142
143impl Default for DbCache {
144 fn default() -> Self {
145 Self::new()
146 }
147}
148
149impl DbCache {
150 pub fn new() -> Self {
151 Self {
152 pg_version_num: None,
153 metadata: CacheMetadata::default(),
154 search_path: vec!["public".to_string()],
155 relations: HashMap::new(),
156 foreign_keys: Vec::new(),
157 indexes: Vec::new(),
158 constraints: Vec::new(),
159 triggers: Vec::new(),
160 functions: HashMap::new(),
161 types: HashMap::new(),
162 roles: HashMap::new(),
163 schemas: HashMap::new(),
164 sequences: HashMap::new(),
165 dependencies: Vec::new(),
166 publications: HashMap::new(),
167 subscriptions: HashMap::new(),
168 }
169 }
170
171 pub fn insert_baseline(&mut self, id: ObjectId, state: RelationState) {
172 self.relations.insert(id, state);
173 }
174
175 pub fn baseline_relations(&self) -> impl Iterator<Item = (&ObjectId, &RelationState)> {
176 self.relations.iter()
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 #[test]
185 fn every_legacy_cache_variant_is_rejected_generically() {
186 for (versioned, expected_version) in [
187 (DbCacheVersioned::V1, 1),
188 (DbCacheVersioned::V2, 2),
189 (DbCacheVersioned::V3, 3),
190 (DbCacheVersioned::V4, 4),
191 ] {
192 assert_eq!(versioned.format_version(), expected_version);
193 assert_eq!(
194 versioned.into_cache().unwrap_err(),
195 "This cache format is unsupported. Run `safe-migrate sync` to rebuild it."
196 );
197 }
198 let v5 = DbCacheVersioned::V5(Box::default());
199 assert_eq!(v5.format_version(), 5);
200 assert_eq!(
201 v5.into_cache().unwrap_err(),
202 "This cache format is unsupported. Run `safe-migrate sync` to rebuild it."
203 );
204 }
205
206 #[test]
207 fn current_cache_format_is_v6() {
208 assert_eq!(CACHE_FORMAT_VERSION, 6);
209 assert_eq!(DbCacheVersioned::V6(Box::default()).format_version(), 6);
210 assert_eq!(CACHE_V6_MAGIC, b"SMCACHE06");
211 }
212}