wasi_pg_client/query/
cache.rs1use std::collections::{HashMap, VecDeque};
7
8use crate::query::prepared::PreparedStatement;
9
10#[derive(Debug, Clone)]
21#[non_exhaustive]
22pub struct StatementCache {
23 cache: HashMap<String, PreparedStatement>,
24 capacity: usize,
25 order: VecDeque<String>,
26}
27
28impl StatementCache {
29 pub fn new(capacity: usize) -> Self {
31 Self {
32 cache: HashMap::with_capacity(capacity),
33 capacity,
34 order: VecDeque::with_capacity(capacity),
35 }
36 }
37
38 pub fn len(&self) -> usize {
40 self.cache.len()
41 }
42
43 pub fn is_empty(&self) -> bool {
45 self.cache.is_empty()
46 }
47
48 pub fn get(&mut self, sql: &str) -> Option<&PreparedStatement> {
53 if self.cache.contains_key(sql) {
54 self.order.retain(|s| s != sql);
56 self.order.push_front(sql.to_string());
57 self.cache.get(sql)
58 } else {
59 None
60 }
61 }
62
63 pub fn insert(&mut self, stmt: PreparedStatement) -> Option<PreparedStatement> {
69 let sql = stmt.sql().to_string();
70
71 self.order.retain(|s| s != &sql);
73
74 let evicted = if self.cache.len() >= self.capacity && !self.cache.contains_key(&sql) {
75 self.order
76 .pop_back()
77 .and_then(|old_sql| self.cache.remove(&old_sql))
78 } else {
79 None
80 };
81
82 self.order.push_front(sql.clone());
83 self.cache.insert(sql, stmt);
84 evicted
85 }
86
87 pub fn remove(&mut self, sql: &str) -> Option<PreparedStatement> {
91 self.order.retain(|s| s != sql);
92 self.cache.remove(sql)
93 }
94
95 pub fn clear(&mut self) -> Vec<PreparedStatement> {
100 self.order.clear();
101 self.cache.drain().map(|(_, v)| v).collect()
102 }
103}
104
105#[cfg(test)]
110mod tests {
111 use super::*;
112 use std::sync::Arc;
113
114 fn dummy_stmt(sql: &str, name: &str) -> PreparedStatement {
115 PreparedStatement {
116 name: name.into(),
117 sql: sql.into(),
118 param_types: vec![],
119 columns: Arc::new(vec![]),
120 }
121 }
122
123 #[test]
124 fn test_cache_insert_and_get() {
125 let mut cache = StatementCache::new(2);
126 let stmt = dummy_stmt("SELECT 1", "s1");
127 cache.insert(stmt);
128
129 assert_eq!(cache.len(), 1);
130 assert!(cache.get("SELECT 1").is_some());
131 assert!(cache.get("SELECT 2").is_none());
132 }
133
134 #[test]
135 fn test_cache_lru_eviction() {
136 let mut cache = StatementCache::new(2);
137 cache.insert(dummy_stmt("SELECT 1", "s1"));
138 cache.insert(dummy_stmt("SELECT 2", "s2"));
139
140 let _ = cache.get("SELECT 1");
142
143 let evicted = cache.insert(dummy_stmt("SELECT 3", "s3"));
145 assert!(evicted.is_some());
146 assert_eq!(evicted.unwrap().sql(), "SELECT 2");
147
148 assert!(cache.get("SELECT 1").is_some());
149 assert!(cache.get("SELECT 2").is_none());
150 assert!(cache.get("SELECT 3").is_some());
151 }
152
153 #[test]
154 fn test_cache_remove() {
155 let mut cache = StatementCache::new(2);
156 cache.insert(dummy_stmt("SELECT 1", "s1"));
157
158 let removed = cache.remove("SELECT 1");
159 assert!(removed.is_some());
160 assert_eq!(removed.unwrap().sql(), "SELECT 1");
161 assert!(cache.is_empty());
162 }
163
164 #[test]
165 fn test_cache_clear() {
166 let mut cache = StatementCache::new(2);
167 cache.insert(dummy_stmt("SELECT 1", "s1"));
168 cache.insert(dummy_stmt("SELECT 2", "s2"));
169
170 let cleared = cache.clear();
171 assert_eq!(cleared.len(), 2);
172 assert!(cache.is_empty());
173 }
174}