1use crate::ast::identifiers::ObjectId;
3use crate::model::constraint::ConstraintState;
4use crate::model::function::FunctionState;
5use crate::model::relation::RelationState;
6use crate::model::trigger::TriggerEnableMode;
7use crate::model::types::TypeState;
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12pub struct ForeignKeyCache {
13 pub constraint_name: String,
14 pub from_table: ObjectId,
15 pub to_table: ObjectId,
16}
17
18#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
19pub struct IndexCache {
20 pub index_id: ObjectId,
21 pub table_id: ObjectId,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct TriggerCache {
26 pub trigger_id: ObjectId,
27 pub table_id: ObjectId,
28 pub function_id: ObjectId,
29 pub enabled_mode: TriggerEnableMode,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct LegacyTriggerCache {
34 pub trigger_id: ObjectId,
35 pub table_id: ObjectId,
36 pub function_id: ObjectId,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct DependencyCache {
41 pub classid: u32,
42 pub objid: u32,
43 pub objsubid: i32,
44 pub refclassid: u32,
45 pub refobjid: u32,
46 pub refobjsubid: i32,
47 pub deptype: String,
48 pub obj_schema: Option<String>,
49 pub obj_name: Option<String>,
50 pub ref_schema: Option<String>,
51 pub ref_name: Option<String>,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct DbCacheV1 {
56 pub pg_version_num: Option<u32>,
57 pub relations: HashMap<ObjectId, RelationState>,
58 #[serde(default)]
59 pub foreign_keys: Vec<ForeignKeyCache>,
60 #[serde(default)]
61 pub indexes: Vec<IndexCache>,
62 #[serde(default)]
63 pub triggers: Vec<LegacyTriggerCache>,
64 #[serde(default)]
65 pub functions: HashMap<ObjectId, FunctionState>,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct DbCacheV2 {
70 pub pg_version_num: Option<u32>,
71 pub relations: HashMap<ObjectId, RelationState>,
72 pub foreign_keys: Vec<ForeignKeyCache>,
73 pub indexes: Vec<IndexCache>,
74 pub triggers: Vec<LegacyTriggerCache>,
75 pub functions: HashMap<ObjectId, FunctionState>,
76 pub dependencies: Vec<DependencyCache>,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
80pub struct DbCache {
81 pub pg_version_num: Option<u32>,
82 pub search_path: Vec<String>,
83 pub relations: HashMap<ObjectId, RelationState>,
84 pub foreign_keys: Vec<ForeignKeyCache>,
85 pub indexes: Vec<IndexCache>,
86 pub constraints: Vec<ConstraintState>,
87 pub triggers: Vec<TriggerCache>,
88 pub functions: HashMap<ObjectId, FunctionState>,
89 pub types: HashMap<ObjectId, TypeState>,
90 pub dependencies: Vec<DependencyCache>,
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct DbCacheV3 {
95 pub pg_version_num: Option<u32>,
96 pub search_path: Vec<String>,
97 pub relations: HashMap<ObjectId, RelationState>,
98 pub foreign_keys: Vec<ForeignKeyCache>,
99 pub indexes: Vec<IndexCache>,
100 pub triggers: Vec<LegacyTriggerCache>,
101 pub functions: HashMap<ObjectId, FunctionState>,
102 pub dependencies: Vec<DependencyCache>,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct DbCacheV4 {
107 pub pg_version_num: Option<u32>,
108 pub search_path: Vec<String>,
109 pub relations: HashMap<ObjectId, RelationState>,
110 pub foreign_keys: Vec<ForeignKeyCache>,
111 pub indexes: Vec<IndexCache>,
112 pub constraints: Vec<ConstraintState>,
113 pub triggers: Vec<TriggerCache>,
114 pub functions: HashMap<ObjectId, FunctionState>,
115 pub dependencies: Vec<DependencyCache>,
116}
117
118pub const CACHE_FORMAT_VERSION: u32 = 5;
119
120const _: () = assert!(
121 CACHE_FORMAT_VERSION == 5,
122 "CACHE_FORMAT_VERSION must be updated when new DbCacheVersioned variants are added",
123);
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
126pub enum DbCacheVersioned {
127 V1(DbCacheV1),
128 V2(DbCacheV2),
129 V3(DbCacheV3),
130 V4(DbCacheV4),
131 V5(DbCache),
132}
133
134impl DbCacheVersioned {
135 pub fn format_version(&self) -> u32 {
136 match self {
137 DbCacheVersioned::V1(_) => 1,
138 DbCacheVersioned::V2(_) => 2,
139 DbCacheVersioned::V3(_) => 3,
140 DbCacheVersioned::V4(_) => 4,
141 DbCacheVersioned::V5(_) => 5,
142 }
143 }
144
145 pub fn into_cache(self) -> Result<DbCache, String> {
146 match self {
147 DbCacheVersioned::V1(c) => Ok(DbCache {
148 pg_version_num: c.pg_version_num,
149 relations: c.relations,
150 foreign_keys: c.foreign_keys,
151 indexes: c.indexes,
152 constraints: Vec::new(),
153 triggers: upgrade_legacy_triggers(c.triggers),
154 functions: c.functions,
155 types: HashMap::new(),
156 dependencies: Vec::new(),
157 search_path: vec!["public".to_string()],
158 }),
159 DbCacheVersioned::V2(c) => Ok(DbCache {
160 pg_version_num: c.pg_version_num,
161 search_path: vec!["public".to_string()],
162 relations: c.relations,
163 foreign_keys: c.foreign_keys,
164 indexes: c.indexes,
165 constraints: Vec::new(),
166 triggers: upgrade_legacy_triggers(c.triggers),
167 functions: c.functions,
168 types: HashMap::new(),
169 dependencies: c.dependencies,
170 }),
171 DbCacheVersioned::V3(c) => Ok(DbCache {
172 pg_version_num: c.pg_version_num,
173 search_path: c.search_path,
174 relations: c.relations,
175 foreign_keys: c.foreign_keys,
176 indexes: c.indexes,
177 constraints: Vec::new(),
178 triggers: upgrade_legacy_triggers(c.triggers),
179 functions: c.functions,
180 types: HashMap::new(),
181 dependencies: c.dependencies,
182 }),
183 DbCacheVersioned::V4(c) => Ok(DbCache {
184 pg_version_num: c.pg_version_num,
185 search_path: c.search_path,
186 relations: c.relations,
187 foreign_keys: c.foreign_keys,
188 indexes: c.indexes,
189 constraints: c.constraints,
190 triggers: c.triggers,
191 functions: c.functions,
192 types: HashMap::new(),
193 dependencies: c.dependencies,
194 }),
195 DbCacheVersioned::V5(c) => Ok(c),
196 }
197 }
198}
199
200fn upgrade_legacy_triggers(triggers: Vec<LegacyTriggerCache>) -> Vec<TriggerCache> {
201 triggers
202 .into_iter()
203 .map(|trigger| TriggerCache {
204 trigger_id: trigger.trigger_id,
205 table_id: trigger.table_id,
206 function_id: trigger.function_id,
207 enabled_mode: TriggerEnableMode::Origin,
208 })
209 .collect()
210}
211
212impl Default for DbCache {
213 fn default() -> Self {
214 Self::new()
215 }
216}
217
218impl DbCache {
219 pub fn new() -> Self {
220 Self {
221 pg_version_num: None,
222 search_path: vec!["public".to_string()],
223 relations: HashMap::new(),
224 foreign_keys: Vec::new(),
225 indexes: Vec::new(),
226 constraints: Vec::new(),
227 triggers: Vec::new(),
228 functions: HashMap::new(),
229 types: HashMap::new(),
230 dependencies: Vec::new(),
231 }
232 }
233
234 pub fn insert_baseline(&mut self, id: ObjectId, state: RelationState) {
235 self.relations.insert(id, state);
236 }
237
238 pub fn baseline_relations(&self) -> impl Iterator<Item = (&ObjectId, &RelationState)> {
239 self.relations.iter()
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246
247 #[test]
248 fn test_bug019_into_cache_succeeds_for_v1() {
249 let cache = DbCacheV1 {
251 pg_version_num: None,
252 relations: HashMap::new(),
253 foreign_keys: Vec::new(),
254 indexes: Vec::new(),
255 triggers: Vec::new(),
256 functions: HashMap::new(),
257 };
258 let versioned = DbCacheVersioned::V1(cache);
259 assert_eq!(versioned.format_version(), 1);
260 let result = versioned.into_cache();
261 assert!(
262 result.is_ok(),
263 "into_cache() should succeed for V1: {:?}",
264 result
265 );
266 }
267
268 #[test]
269 fn test_bug019_format_version_constant_matches_v1_variant() {
270 let versioned = DbCacheVersioned::V1(DbCacheV1 {
272 pg_version_num: None,
273 relations: HashMap::new(),
274 foreign_keys: Vec::new(),
275 indexes: Vec::new(),
276 triggers: Vec::new(),
277 functions: HashMap::new(),
278 });
279 assert_eq!(
280 versioned.format_version(),
281 1,
282 "format_version() for V1 must match 1"
283 );
284 }
285}