1use std::collections::HashMap;
9use std::sync::Mutex;
10use std::sync::atomic::{AtomicI64, Ordering};
11
12use serde::{Deserialize, Serialize};
13
14use crate::error::LiteError;
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct LiteSequenceDef {
19 pub name: String,
20 pub start_value: i64,
21 pub increment: i64,
22 pub min_value: i64,
23 pub max_value: i64,
24 pub cycle: bool,
25}
26
27struct LiteSeqHandle {
29 counter: AtomicI64,
30 called: std::sync::atomic::AtomicBool,
31 def: LiteSequenceDef,
32}
33
34pub struct LiteSequenceRegistry {
40 sequences: Mutex<HashMap<String, LiteSeqHandle>>,
41}
42
43impl LiteSequenceRegistry {
44 pub fn new() -> Self {
45 Self {
46 sequences: Mutex::new(HashMap::new()),
47 }
48 }
49
50 pub fn register(&self, def: LiteSequenceDef) {
52 let initial = def.start_value - def.increment;
53 let mut map = self.sequences.lock().unwrap_or_else(|p| p.into_inner());
54 map.insert(
55 def.name.clone(),
56 LiteSeqHandle {
57 counter: AtomicI64::new(initial),
58 called: std::sync::atomic::AtomicBool::new(false),
59 def,
60 },
61 );
62 }
63
64 pub fn nextval(&self, name: &str) -> Result<i64, LiteError> {
66 let map = self.sequences.lock().unwrap_or_else(|p| p.into_inner());
67 let handle = map
68 .get(name)
69 .ok_or_else(|| LiteError::Query(format!("sequence \"{name}\" does not exist")))?;
70
71 let inc = handle.def.increment;
72 let prev = handle.counter.fetch_add(inc, Ordering::Relaxed);
73 let new_val = prev + inc;
74 handle.called.store(true, Ordering::Relaxed);
75
76 if inc > 0 && new_val > handle.def.max_value {
77 if handle.def.cycle {
78 handle
79 .counter
80 .store(handle.def.min_value, Ordering::Relaxed);
81 return Ok(handle.def.min_value);
82 }
83 handle.counter.store(prev, Ordering::Relaxed);
84 return Err(LiteError::Query(format!("sequence \"{name}\" exhausted")));
85 }
86 if inc < 0 && new_val < handle.def.min_value {
87 if handle.def.cycle {
88 handle
89 .counter
90 .store(handle.def.max_value, Ordering::Relaxed);
91 return Ok(handle.def.max_value);
92 }
93 handle.counter.store(prev, Ordering::Relaxed);
94 return Err(LiteError::Query(format!("sequence \"{name}\" exhausted")));
95 }
96
97 Ok(new_val)
98 }
99
100 pub fn currval(&self, name: &str) -> Result<i64, LiteError> {
102 let map = self.sequences.lock().unwrap_or_else(|p| p.into_inner());
103 let handle = map
104 .get(name)
105 .ok_or_else(|| LiteError::Query(format!("sequence \"{name}\" does not exist")))?;
106 if !handle.called.load(Ordering::Relaxed) {
107 return Err(LiteError::Query(format!(
108 "currval of \"{name}\" not yet defined"
109 )));
110 }
111 Ok(handle.counter.load(Ordering::Relaxed))
112 }
113
114 pub fn setval(&self, name: &str, value: i64) -> Result<i64, LiteError> {
116 let map = self.sequences.lock().unwrap_or_else(|p| p.into_inner());
117 let handle = map
118 .get(name)
119 .ok_or_else(|| LiteError::Query(format!("sequence \"{name}\" does not exist")))?;
120 if value < handle.def.min_value || value > handle.def.max_value {
121 return Err(LiteError::Query(format!(
122 "setval: {value} outside [{}, {}]",
123 handle.def.min_value, handle.def.max_value
124 )));
125 }
126 handle.counter.store(value, Ordering::Relaxed);
127 handle.called.store(true, Ordering::Relaxed);
128 Ok(value)
129 }
130
131 pub fn restart_collection_sequences(&self, collection: &str) -> Vec<String> {
135 let prefix = format!("{collection}_");
136 let map = self.sequences.lock().unwrap_or_else(|p| p.into_inner());
137 let mut restarted = Vec::new();
138 for (name, handle) in map.iter() {
139 if !(name.starts_with(&prefix) && name.ends_with("_seq")) {
140 continue;
141 }
142 handle.counter.store(
143 handle.def.start_value - handle.def.increment,
144 Ordering::Relaxed,
145 );
146 handle.called.store(false, Ordering::Relaxed);
147 restarted.push(name.clone());
148 }
149 restarted
150 }
151
152 pub fn names(&self) -> Vec<String> {
154 let map = self.sequences.lock().unwrap_or_else(|p| p.into_inner());
155 map.keys().cloned().collect()
156 }
157
158 pub fn remove(&self, name: &str) -> bool {
160 let mut map = self.sequences.lock().unwrap_or_else(|p| p.into_inner());
161 map.remove(name).is_some()
162 }
163}
164
165impl Default for LiteSequenceRegistry {
166 fn default() -> Self {
167 Self::new()
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 fn make_def(name: &str) -> LiteSequenceDef {
176 LiteSequenceDef {
177 name: name.to_string(),
178 start_value: 1,
179 increment: 1,
180 min_value: 1,
181 max_value: i64::MAX,
182 cycle: false,
183 }
184 }
185
186 #[test]
187 fn basic_nextval() {
188 let reg = LiteSequenceRegistry::new();
189 reg.register(make_def("s1"));
190 assert_eq!(reg.nextval("s1").unwrap(), 1);
191 assert_eq!(reg.nextval("s1").unwrap(), 2);
192 assert_eq!(reg.currval("s1").unwrap(), 2);
193 }
194
195 #[test]
196 fn setval_and_nextval() {
197 let reg = LiteSequenceRegistry::new();
198 reg.register(make_def("s1"));
199 reg.setval("s1", 50).unwrap();
200 assert_eq!(reg.nextval("s1").unwrap(), 51);
201 }
202
203 #[test]
204 fn restart_collection_sequences_makes_start_the_next_value() {
205 let reg = LiteSequenceRegistry::new();
206 reg.register(make_def("orders_id_seq"));
207 reg.register(make_def("orders_line_seq"));
208 reg.register(make_def("orders_archive_id_seq"));
209 reg.register(make_def("users_id_seq"));
210 for _ in 0..3 {
211 reg.nextval("orders_id_seq").unwrap();
212 reg.nextval("orders_line_seq").unwrap();
213 reg.nextval("orders_archive_id_seq").unwrap();
214 reg.nextval("users_id_seq").unwrap();
215 }
216 let mut restarted = reg.restart_collection_sequences("orders");
217 restarted.sort();
218 assert_eq!(
219 restarted,
220 vec![
221 "orders_archive_id_seq".to_string(),
222 "orders_id_seq".to_string(),
223 "orders_line_seq".to_string()
224 ]
225 );
226 assert!(
227 reg.currval("orders_id_seq").is_err(),
228 "restart clears currval"
229 );
230 assert_eq!(reg.nextval("orders_id_seq").unwrap(), 1);
231 assert_eq!(reg.nextval("orders_line_seq").unwrap(), 1);
232 assert_eq!(reg.nextval("users_id_seq").unwrap(), 4);
233 }
234
235 #[test]
236 fn not_found() {
237 let reg = LiteSequenceRegistry::new();
238 assert!(reg.nextval("nope").is_err());
239 }
240}