pgevolve_core/diff/
reloptions.rs1use crate::ir::reloptions::{IndexStorageOptions, TableStorageOptions};
6
7#[must_use]
13pub fn table_delta(
14 target: &TableStorageOptions,
15 source: &TableStorageOptions,
16) -> TableStorageOptions {
17 let mut out = TableStorageOptions::default();
18
19 macro_rules! diff_field {
20 ($field:ident) => {
21 if let Some(src) = &source.$field {
22 if target.$field.as_ref() != Some(src) {
23 out.$field = Some(src.clone());
24 }
25 }
26 };
27 }
28
29 diff_field!(fillfactor);
30 diff_field!(parallel_workers);
31 diff_field!(toast_tuple_target);
32 diff_field!(user_catalog_table);
33 diff_field!(vacuum_truncate);
34
35 for (k, src_v) in &source.extra {
38 if target.extra.get(k) != Some(src_v) {
39 out.extra.insert(k.clone(), src_v.clone());
40 }
41 }
42
43 out
44}
45
46#[must_use]
50pub fn index_delta(
51 target: &IndexStorageOptions,
52 source: &IndexStorageOptions,
53) -> IndexStorageOptions {
54 let mut out = IndexStorageOptions::default();
55
56 macro_rules! diff_field {
57 ($field:ident) => {
58 if let Some(src) = &source.$field {
59 if target.$field.as_ref() != Some(src) {
60 out.$field = Some(src.clone());
61 }
62 }
63 };
64 }
65
66 diff_field!(fillfactor);
67 diff_field!(fastupdate);
68 diff_field!(gin_pending_list_limit);
69 diff_field!(buffering);
70 diff_field!(deduplicate_items);
71 diff_field!(pages_per_range);
72 diff_field!(autosummarize);
73
74 for (k, src_v) in &source.extra {
75 if target.extra.get(k) != Some(src_v) {
76 out.extra.insert(k.clone(), src_v.clone());
77 }
78 }
79
80 out
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn empty_source_yields_empty_delta() {
89 let t = TableStorageOptions {
90 fillfactor: Some(80),
91 ..Default::default()
92 };
93 let s = TableStorageOptions::default();
94 let delta = table_delta(&t, &s);
95 assert!(delta.is_empty(), "lenient: source None means skip");
96 }
97
98 #[test]
99 fn source_only_fillfactor_emits_one_field() {
100 let t = TableStorageOptions::default();
101 let s = TableStorageOptions {
102 fillfactor: Some(80),
103 ..Default::default()
104 };
105 let delta = table_delta(&t, &s);
106 assert_eq!(delta.fillfactor, Some(80));
107 assert!(delta.extra.is_empty());
108 }
109
110 #[test]
111 fn matching_source_and_target_yields_empty_delta() {
112 let t = TableStorageOptions {
113 fillfactor: Some(80),
114 ..Default::default()
115 };
116 let s = TableStorageOptions {
117 fillfactor: Some(80),
118 ..Default::default()
119 };
120 let delta = table_delta(&t, &s);
121 assert!(delta.is_empty());
122 }
123
124 #[test]
125 fn source_extra_key_not_in_target_emits() {
126 let t = TableStorageOptions::default();
127 let mut s = TableStorageOptions::default();
128 s.extra.insert("pg_partman.foo".into(), "value".into());
129 let delta = table_delta(&t, &s);
130 assert_eq!(
131 delta.extra.get("pg_partman.foo").map(String::as_str),
132 Some("value")
133 );
134 }
135
136 #[test]
137 fn target_extra_key_not_in_source_does_not_emit() {
138 let mut t = TableStorageOptions::default();
139 t.extra.insert("pg_partman.foo".into(), "value".into());
140 let s = TableStorageOptions::default();
141 let delta = table_delta(&t, &s);
142 assert!(
143 delta.is_empty(),
144 "lenient: unmanaged extra-bag keys ignored"
145 );
146 }
147
148 #[test]
149 fn source_autovacuum_change_emits_via_extra() {
150 let mut t = TableStorageOptions::default();
152 t.extra.insert("autovacuum_enabled".into(), "true".into());
153 let mut s = TableStorageOptions::default();
154 s.extra.insert("autovacuum_enabled".into(), "false".into());
155 let delta = table_delta(&t, &s);
156 assert_eq!(
157 delta.extra.get("autovacuum_enabled").map(String::as_str),
158 Some("false")
159 );
160 }
161
162 #[test]
163 fn index_delta_fillfactor_change() {
164 let t = IndexStorageOptions {
165 fillfactor: Some(70),
166 ..Default::default()
167 };
168 let s = IndexStorageOptions {
169 fillfactor: Some(80),
170 ..Default::default()
171 };
172 let delta = index_delta(&t, &s);
173 assert_eq!(delta.fillfactor, Some(80));
174 }
175}