pgevolve_core/ir/
reloptions.rs1use std::collections::BTreeMap;
10use std::str::FromStr;
11
12use serde::{Deserialize, Serialize};
13
14#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize)]
17pub struct TableStorageOptions {
18 pub fillfactor: Option<u32>,
20 pub parallel_workers: Option<u32>,
22 pub toast_tuple_target: Option<u32>,
24 pub user_catalog_table: Option<bool>,
26 pub vacuum_truncate: Option<bool>,
28 pub extra: BTreeMap<String, String>,
32}
33
34impl TableStorageOptions {
35 #[must_use]
37 pub fn is_empty(&self) -> bool {
38 self.fillfactor.is_none()
39 && self.parallel_workers.is_none()
40 && self.toast_tuple_target.is_none()
41 && self.user_catalog_table.is_none()
42 && self.vacuum_truncate.is_none()
43 && self.extra.is_empty()
44 }
45}
46
47pub type MaterializedViewStorageOptions = TableStorageOptions;
49
50#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize)]
53pub struct IndexStorageOptions {
54 pub fillfactor: Option<u32>,
56 pub fastupdate: Option<bool>,
58 pub gin_pending_list_limit: Option<u64>,
60 pub buffering: Option<BufferingMode>,
62 pub deduplicate_items: Option<bool>,
64 pub pages_per_range: Option<u32>,
66 pub autosummarize: Option<bool>,
68 pub extra: BTreeMap<String, String>,
70}
71
72impl IndexStorageOptions {
73 #[must_use]
75 pub fn is_empty(&self) -> bool {
76 self.fillfactor.is_none()
77 && self.fastupdate.is_none()
78 && self.gin_pending_list_limit.is_none()
79 && self.buffering.is_none()
80 && self.deduplicate_items.is_none()
81 && self.pages_per_range.is_none()
82 && self.autosummarize.is_none()
83 && self.extra.is_empty()
84 }
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize)]
89#[serde(rename_all = "snake_case")]
90pub enum BufferingMode {
91 On,
93 Off,
95 Auto,
97}
98
99impl BufferingMode {
100 #[must_use]
102 pub const fn sql_keyword(self) -> &'static str {
103 match self {
104 Self::On => "on",
105 Self::Off => "off",
106 Self::Auto => "auto",
107 }
108 }
109}
110
111impl FromStr for BufferingMode {
112 type Err = ();
113
114 fn from_str(s: &str) -> Result<Self, Self::Err> {
116 match s.to_ascii_lowercase().as_str() {
117 "on" => Ok(Self::On),
118 "off" => Ok(Self::Off),
119 "auto" => Ok(Self::Auto),
120 _ => Err(()),
121 }
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn default_storage_is_empty() {
131 assert!(TableStorageOptions::default().is_empty());
132 assert!(IndexStorageOptions::default().is_empty());
133 }
134
135 #[test]
136 fn autovacuum_extra_key_makes_non_empty() {
137 let mut s = TableStorageOptions::default();
138 s.extra.insert("autovacuum_enabled".into(), "false".into());
139 assert!(!s.is_empty());
140 }
141
142 #[test]
143 fn non_empty_storage_detected() {
144 let s = TableStorageOptions {
145 fillfactor: Some(80),
146 ..Default::default()
147 };
148 assert!(!s.is_empty());
149 }
150
151 #[test]
152 fn buffering_mode_roundtrips() {
153 for m in [BufferingMode::On, BufferingMode::Off, BufferingMode::Auto] {
154 assert_eq!(m.sql_keyword().parse::<BufferingMode>(), Ok(m));
155 }
156 assert!("bogus".parse::<BufferingMode>().is_err());
157 }
158
159 #[test]
160 fn extra_is_sorted_via_btreemap() {
161 let mut s = TableStorageOptions::default();
162 s.extra.insert("zebra".into(), "1".into());
163 s.extra.insert("alpha".into(), "2".into());
164 let keys: Vec<_> = s.extra.keys().cloned().collect();
165 assert_eq!(keys, vec!["alpha", "zebra"]);
166 }
167}