1use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
11#[serde(rename_all = "lowercase")]
12pub enum PoolingMode {
13 #[default]
18 Session,
19
20 Transaction,
26
27 Statement,
33}
34
35impl PoolingMode {
36 pub fn supports_prepared_statements(&self) -> bool {
38 match self {
39 PoolingMode::Session => true,
40 PoolingMode::Transaction => true, PoolingMode::Statement => false,
42 }
43 }
44
45 pub fn description(&self) -> &'static str {
47 match self {
48 PoolingMode::Session => "Hold connection for entire client session",
49 PoolingMode::Transaction => "Return connection after COMMIT/ROLLBACK",
50 PoolingMode::Statement => "Return connection after each statement",
51 }
52 }
53
54 pub fn from_str_lossy(s: &str) -> Self {
56 match s.to_lowercase().as_str() {
57 "session" => PoolingMode::Session,
58 "transaction" | "txn" => PoolingMode::Transaction,
59 "statement" | "stmt" => PoolingMode::Statement,
60 _ => PoolingMode::Session,
61 }
62 }
63}
64
65impl std::fmt::Display for PoolingMode {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 match self {
68 PoolingMode::Session => write!(f, "session"),
69 PoolingMode::Transaction => write!(f, "transaction"),
70 PoolingMode::Statement => write!(f, "statement"),
71 }
72 }
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
79#[serde(rename_all = "lowercase")]
80pub enum PreparedStatementMode {
81 #[default]
85 Disable,
86
87 Track,
92
93 Named,
98}
99
100impl PreparedStatementMode {
101 pub fn description(&self) -> &'static str {
103 match self {
104 PreparedStatementMode::Disable => "Disable prepared statements (safest)",
105 PreparedStatementMode::Track => "Track and recreate on new connections",
106 PreparedStatementMode::Named => "Use protocol-level named statements",
107 }
108 }
109
110 pub fn from_str_lossy(s: &str) -> Self {
112 match s.to_lowercase().as_str() {
113 "disable" | "disabled" | "off" => PreparedStatementMode::Disable,
114 "track" | "tracking" => PreparedStatementMode::Track,
115 "named" | "protocol" => PreparedStatementMode::Named,
116 _ => PreparedStatementMode::Disable,
117 }
118 }
119}
120
121impl std::fmt::Display for PreparedStatementMode {
122 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123 match self {
124 PreparedStatementMode::Disable => write!(f, "disable"),
125 PreparedStatementMode::Track => write!(f, "track"),
126 PreparedStatementMode::Named => write!(f, "named"),
127 }
128 }
129}
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135pub enum TransactionEvent {
136 Begin,
138 Commit,
140 Rollback,
142 Savepoint,
144 ReleaseSavepoint,
146 RollbackToSavepoint,
148 Statement,
150}
151
152impl TransactionEvent {
153 pub fn detect(sql: &str) -> Self {
161 use crate::protocol::{contains_ci, starts_with_ci};
162 let trimmed = sql.trim();
167
168 if starts_with_ci(trimmed, "BEGIN") {
170 return TransactionEvent::Begin;
171 }
172 if starts_with_ci(trimmed, "START TRANSACTION") || starts_with_ci(trimmed, "START ") {
173 if contains_ci(trimmed, "TRANSACTION") {
175 return TransactionEvent::Begin;
176 }
177 }
178 if starts_with_ci(trimmed, "COMMIT") || starts_with_ci(trimmed, "END") {
179 return TransactionEvent::Commit;
181 }
182 if starts_with_ci(trimmed, "ROLLBACK") {
183 if contains_ci(trimmed, " TO ") {
185 return TransactionEvent::RollbackToSavepoint;
186 }
187 return TransactionEvent::Rollback;
188 }
189 if starts_with_ci(trimmed, "SAVEPOINT") {
190 return TransactionEvent::Savepoint;
191 }
192 if starts_with_ci(trimmed, "RELEASE") {
193 return TransactionEvent::ReleaseSavepoint;
194 }
195
196 TransactionEvent::Statement
197 }
198
199 pub fn is_transaction_end(&self) -> bool {
201 matches!(self, TransactionEvent::Commit | TransactionEvent::Rollback)
202 }
203
204 pub fn is_transaction_start(&self) -> bool {
206 matches!(self, TransactionEvent::Begin)
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213
214 #[test]
215 fn test_pooling_mode_default() {
216 assert_eq!(PoolingMode::default(), PoolingMode::Session);
217 }
218
219 #[test]
220 fn test_pooling_mode_display() {
221 assert_eq!(PoolingMode::Session.to_string(), "session");
222 assert_eq!(PoolingMode::Transaction.to_string(), "transaction");
223 assert_eq!(PoolingMode::Statement.to_string(), "statement");
224 }
225
226 #[test]
227 fn test_pooling_mode_from_str() {
228 assert_eq!(PoolingMode::from_str_lossy("SESSION"), PoolingMode::Session);
229 assert_eq!(
230 PoolingMode::from_str_lossy("transaction"),
231 PoolingMode::Transaction
232 );
233 assert_eq!(PoolingMode::from_str_lossy("txn"), PoolingMode::Transaction);
234 assert_eq!(
235 PoolingMode::from_str_lossy("STATEMENT"),
236 PoolingMode::Statement
237 );
238 assert_eq!(PoolingMode::from_str_lossy("stmt"), PoolingMode::Statement);
239 assert_eq!(
240 PoolingMode::from_str_lossy("unknown"),
241 PoolingMode::Session
242 );
243 }
244
245 #[test]
246 fn test_prepared_statement_mode_default() {
247 assert_eq!(
248 PreparedStatementMode::default(),
249 PreparedStatementMode::Disable
250 );
251 }
252
253 #[test]
254 fn test_transaction_event_detect() {
255 assert_eq!(TransactionEvent::detect("BEGIN"), TransactionEvent::Begin);
256 assert_eq!(
257 TransactionEvent::detect("begin work"),
258 TransactionEvent::Begin
259 );
260 assert_eq!(
261 TransactionEvent::detect("START TRANSACTION"),
262 TransactionEvent::Begin
263 );
264 assert_eq!(TransactionEvent::detect("COMMIT"), TransactionEvent::Commit);
265 assert_eq!(TransactionEvent::detect("END"), TransactionEvent::Commit);
266 assert_eq!(
267 TransactionEvent::detect("ROLLBACK"),
268 TransactionEvent::Rollback
269 );
270 assert_eq!(
271 TransactionEvent::detect("ROLLBACK TO SAVEPOINT sp1"),
272 TransactionEvent::RollbackToSavepoint
273 );
274 assert_eq!(
275 TransactionEvent::detect("SAVEPOINT sp1"),
276 TransactionEvent::Savepoint
277 );
278 assert_eq!(
279 TransactionEvent::detect("RELEASE SAVEPOINT sp1"),
280 TransactionEvent::ReleaseSavepoint
281 );
282 assert_eq!(
283 TransactionEvent::detect("SELECT * FROM users"),
284 TransactionEvent::Statement
285 );
286 }
287
288 #[test]
289 fn test_transaction_event_predicates() {
290 assert!(TransactionEvent::Begin.is_transaction_start());
291 assert!(!TransactionEvent::Begin.is_transaction_end());
292
293 assert!(TransactionEvent::Commit.is_transaction_end());
294 assert!(!TransactionEvent::Commit.is_transaction_start());
295
296 assert!(TransactionEvent::Rollback.is_transaction_end());
297 assert!(!TransactionEvent::Statement.is_transaction_end());
298 }
299}