1use std::collections::HashMap;
10use std::sync::atomic::{AtomicU64, Ordering};
11use parking_lot::Mutex;
12use std::time::Duration;
13
14use crate::any_driver::AnyBackend;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
24pub enum TransactionIsolation {
25 ReadUncommitted,
27 #[default]
29 ReadCommitted,
30 RepeatableRead,
32 Serializable,
34}
35
36impl TransactionIsolation {
37 pub fn name(&self) -> &'static str {
39 match self {
40 TransactionIsolation::ReadUncommitted => "READ UNCOMMITTED",
41 TransactionIsolation::ReadCommitted => "READ COMMITTED",
42 TransactionIsolation::RepeatableRead => "REPEATABLE READ",
43 TransactionIsolation::Serializable => "SERIALIZABLE",
44 }
45 }
46
47 pub fn description(&self) -> &'static str {
49 match self {
50 TransactionIsolation::ReadUncommitted => "读未提交",
51 TransactionIsolation::ReadCommitted => "读已提交",
52 TransactionIsolation::RepeatableRead => "可重复读",
53 TransactionIsolation::Serializable => "串行化",
54 }
55 }
56
57 pub fn strictness(&self) -> u8 {
59 match self {
60 TransactionIsolation::ReadUncommitted => 0,
61 TransactionIsolation::ReadCommitted => 1,
62 TransactionIsolation::RepeatableRead => 2,
63 TransactionIsolation::Serializable => 3,
64 }
65 }
66
67 pub fn set_session_sql(&self, backend: AnyBackend) -> String {
74 match backend {
75 AnyBackend::MySql => {
76 format!("SET SESSION TRANSACTION ISOLATION LEVEL {}", self.name())
77 }
78 AnyBackend::Postgres => {
79 format!(
80 "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL {}",
81 self.name()
82 )
83 }
84 AnyBackend::Sqlite => {
85 String::new()
87 }
88 }
89 }
90
91 pub fn set_transaction_sql(&self, backend: AnyBackend) -> String {
97 match backend {
98 AnyBackend::MySql => format!("SET TRANSACTION ISOLATION LEVEL {}", self.name()),
99 AnyBackend::Postgres => format!("SET TRANSACTION ISOLATION LEVEL {}", self.name()),
100 AnyBackend::Sqlite => String::new(),
101 }
102 }
103
104 pub fn query_sql(&self, backend: AnyBackend) -> String {
110 match backend {
111 AnyBackend::MySql => "SELECT @@transaction_isolation".to_string(),
112 AnyBackend::Postgres => "SHOW transaction_isolation".to_string(),
113 AnyBackend::Sqlite => String::new(),
114 }
115 }
116
117 #[allow(clippy::should_implement_trait)]
119 pub fn from_str(s: &str) -> Option<Self> {
120 let upper = s.to_uppercase().replace('_', " ");
121 match upper.as_str() {
122 "READ UNCOMMITTED" => Some(TransactionIsolation::ReadUncommitted),
123 "READ COMMITTED" => Some(TransactionIsolation::ReadCommitted),
124 "REPEATABLE READ" => Some(TransactionIsolation::RepeatableRead),
125 "SERIALIZABLE" => Some(TransactionIsolation::Serializable),
126 _ => None,
127 }
128 }
129}
130
131impl std::fmt::Display for TransactionIsolation {
132 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133 write!(f, "{}", self.name())
134 }
135}
136
137#[derive(Debug, Clone)]
145pub struct EnhancedPoolConfig {
146 pub max_connections: u32,
148 pub min_idle: Option<u32>,
150 pub acquire_timeout: Duration,
152 pub idle_timeout: Option<Duration>,
154 pub max_lifetime: Option<Duration>,
156 pub test_on_acquire: bool,
158 pub test_query: String,
160 pub pool_name: Option<String>,
162}
163
164impl Default for EnhancedPoolConfig {
165 fn default() -> Self {
166 Self {
167 max_connections: 10,
168 min_idle: None,
169 acquire_timeout: Duration::from_secs(30),
170 idle_timeout: Some(Duration::from_secs(600)),
171 max_lifetime: Some(Duration::from_secs(1800)),
172 test_on_acquire: false,
173 test_query: "SELECT 1".to_string(),
174 pool_name: None,
175 }
176 }
177}
178
179impl EnhancedPoolConfig {
180 pub fn builder() -> EnhancedPoolConfigBuilder {
182 EnhancedPoolConfigBuilder::default()
183 }
184
185 pub fn validate(&self) -> Result<(), String> {
187 if self.max_connections == 0 {
188 return Err("max_connections 不能为 0".to_string());
189 }
190 if let Some(min) = self.min_idle {
191 if min > self.max_connections {
192 return Err(format!(
193 "min_idle ({}) 不能大于 max_connections ({})",
194 min, self.max_connections
195 ));
196 }
197 }
198 if self.acquire_timeout.is_zero() {
199 return Err("acquire_timeout 不能为 0".to_string());
200 }
201 if self.test_query.is_empty() {
202 return Err("test_query 不能为空".to_string());
203 }
204 Ok(())
205 }
206
207 pub fn summary(&self) -> String {
209 format!(
210 "PoolConfig{{max={}, min_idle={:?}, timeout={}ms, test_on_acquire={}, name={:?}}}",
211 self.max_connections,
212 self.min_idle,
213 self.acquire_timeout.as_millis(),
214 self.test_on_acquire,
215 self.pool_name
216 )
217 }
218}
219
220#[derive(Debug, Clone, Default)]
222pub struct EnhancedPoolConfigBuilder {
223 config: EnhancedPoolConfig,
224}
225
226impl EnhancedPoolConfigBuilder {
227 pub fn max_connections(mut self, n: u32) -> Self {
229 self.config.max_connections = n;
230 self
231 }
232
233 pub fn min_idle(mut self, n: u32) -> Self {
235 self.config.min_idle = Some(n);
236 self
237 }
238
239 pub fn acquire_timeout_secs(mut self, secs: u64) -> Self {
241 self.config.acquire_timeout = Duration::from_secs(secs);
242 self
243 }
244
245 pub fn acquire_timeout_millis(mut self, millis: u64) -> Self {
247 self.config.acquire_timeout = Duration::from_millis(millis);
248 self
249 }
250
251 pub fn idle_timeout_secs(mut self, secs: u64) -> Self {
253 self.config.idle_timeout = Some(Duration::from_secs(secs));
254 self
255 }
256
257 pub fn max_lifetime_secs(mut self, secs: u64) -> Self {
259 self.config.max_lifetime = Some(Duration::from_secs(secs));
260 self
261 }
262
263 pub fn test_on_acquire(mut self) -> Self {
265 self.config.test_on_acquire = true;
266 self
267 }
268
269 pub fn test_query(mut self, sql: &str) -> Self {
271 self.config.test_query = sql.to_string();
272 self
273 }
274
275 pub fn name(mut self, name: &str) -> Self {
277 self.config.pool_name = Some(name.to_string());
278 self
279 }
280
281 pub fn build(self) -> Result<EnhancedPoolConfig, String> {
283 self.config.validate()?;
284 Ok(self.config)
285 }
286}
287
288#[derive(Debug, Clone)]
294#[allow(dead_code)]
295struct CacheEntry {
296 statement_id: String,
298 created_seq: u64,
300 last_access_seq: u64,
302 hit_count: u64,
304}
305
306#[derive(Debug, Clone, Default)]
308pub struct CacheStats {
309 pub hits: u64,
311 pub misses: u64,
313 pub evictions: u64,
315 pub size: usize,
317 pub capacity: usize,
319}
320
321impl CacheStats {
322 pub fn hit_rate(&self) -> f64 {
324 let total = self.hits + self.misses;
325 if total == 0 {
326 return 0.0;
327 }
328 self.hits as f64 / total as f64
329 }
330
331 pub fn summary(&self) -> String {
333 format!(
334 "CacheStats{{hits={}, misses={}, evictions={}, size={}, capacity={}, hit_rate={:.2}%, capacity_utilization={:.2}%}}",
335 self.hits,
336 self.misses,
337 self.evictions,
338 self.size,
339 self.capacity,
340 self.hit_rate() * 100.0,
341 self.capacity_utilization() * 100.0,
342 )
343 }
344
345 pub fn capacity_utilization(&self) -> f64 {
347 if self.capacity == 0 {
348 return 0.0;
349 }
350 self.size as f64 / self.capacity as f64
351 }
352
353 pub fn total_accesses(&self) -> u64 {
355 self.hits + self.misses
356 }
357}
358
359pub struct PreparedStatementCache {
369 entries: Mutex<HashMap<u64, CacheEntry>>,
371 capacity: usize,
373 stats: Mutex<CacheStats>,
375 access_seq: AtomicU64,
377}
378
379impl PreparedStatementCache {
380 pub fn new(capacity: usize) -> Self {
386 let capacity = capacity.max(1);
387 Self {
388 entries: Mutex::new(HashMap::with_capacity(capacity)),
389 capacity,
390 stats: Mutex::new(CacheStats {
391 hits: 0,
392 misses: 0,
393 evictions: 0,
394 size: 0,
395 capacity,
396 }),
397 access_seq: AtomicU64::new(0),
398 }
399 }
400
401 fn hash_sql(sql: &str) -> u64 {
403 const FNV_OFFSET: u64 = 0xcbf29ce484222325;
405 const FNV_PRIME: u64 = 0x100000001b3;
406 let mut hash = FNV_OFFSET;
407 for byte in sql.as_bytes() {
408 hash ^= *byte as u64;
409 hash = hash.wrapping_mul(FNV_PRIME);
410 }
411 hash
412 }
413
414 fn next_seq(&self) -> u64 {
416 self.access_seq.fetch_add(1, Ordering::Relaxed)
417 }
418
419 pub fn get(&self, sql: &str) -> Option<String> {
423 let hash = Self::hash_sql(sql);
424 let seq = self.next_seq();
425
426 let mut entries = self.entries.lock();
427 let mut stats = self.stats.lock();
428
429 if let Some(entry) = entries.get_mut(&hash) {
430 entry.last_access_seq = seq;
431 entry.hit_count += 1;
432 stats.hits += 1;
433 Some(entry.statement_id.clone())
434 } else {
435 stats.misses += 1;
436 None
437 }
438 }
439
440 pub fn put(&self, sql: &str, statement_id: &str) {
444 let hash = Self::hash_sql(sql);
445 let seq = self.next_seq();
446
447 let mut entries = self.entries.lock();
448 let mut stats = self.stats.lock();
449
450 if let Some(entry) = entries.get_mut(&hash) {
452 entry.statement_id = statement_id.to_string();
453 entry.last_access_seq = seq;
454 return;
455 }
456
457 if entries.len() >= self.capacity {
459 if let Some(&evict_hash) = entries
461 .iter()
462 .min_by_key(|(_, entry)| entry.last_access_seq)
463 .map(|(k, _)| k)
464 {
465 entries.remove(&evict_hash);
466 stats.evictions += 1;
467 }
468 }
469
470 entries.insert(
471 hash,
472 CacheEntry {
473 statement_id: statement_id.to_string(),
474 created_seq: seq,
475 last_access_seq: seq,
476 hit_count: 0,
477 },
478 );
479 stats.size = entries.len();
480 }
481
482 pub fn remove(&self, sql: &str) -> bool {
484 let hash = Self::hash_sql(sql);
485 let mut entries = self.entries.lock();
486 let mut stats = self.stats.lock();
487 let removed = entries.remove(&hash).is_some();
488 if removed {
489 stats.size = entries.len();
490 }
491 removed
492 }
493
494 pub fn clear(&self) {
496 let mut entries = self.entries.lock();
497 let mut stats = self.stats.lock();
498 entries.clear();
499 stats.size = 0;
500 }
501
502 pub fn stats(&self) -> CacheStats {
504 let stats = self.stats.lock();
505 stats.clone()
506 }
507
508 pub fn capacity(&self) -> usize {
510 self.capacity
511 }
512
513 pub fn len(&self) -> usize {
515 self.entries.lock().len()
516 }
517
518 pub fn is_empty(&self) -> bool {
520 self.len() == 0
521 }
522
523 pub fn reset_stats(&self) {
525 let mut stats = self.stats.lock();
526 stats.hits = 0;
527 stats.misses = 0;
528 stats.evictions = 0;
529 }
530}
531
532impl Default for PreparedStatementCache {
533 fn default() -> Self {
534 Self::new(256)
535 }
536}
537
538impl std::fmt::Debug for PreparedStatementCache {
539 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
540 let stats = self.stats();
541 f.debug_struct("PreparedStatementCache")
542 .field("capacity", &self.capacity)
543 .field("size", &stats.size)
544 .field("hits", &stats.hits)
545 .field("misses", &stats.misses)
546 .field("evictions", &stats.evictions)
547 .finish()
548 }
549}
550
551#[cfg(test)]
556mod tests {
557 use super::*;
558
559 #[test]
562 fn test_isolation_level_names() {
563 assert_eq!(
564 TransactionIsolation::ReadUncommitted.name(),
565 "READ UNCOMMITTED"
566 );
567 assert_eq!(TransactionIsolation::ReadCommitted.name(), "READ COMMITTED");
568 assert_eq!(
569 TransactionIsolation::RepeatableRead.name(),
570 "REPEATABLE READ"
571 );
572 assert_eq!(TransactionIsolation::Serializable.name(), "SERIALIZABLE");
573 }
574
575 #[test]
576 fn test_isolation_level_descriptions() {
577 assert_eq!(
578 TransactionIsolation::ReadUncommitted.description(),
579 "读未提交"
580 );
581 assert_eq!(
582 TransactionIsolation::ReadCommitted.description(),
583 "读已提交"
584 );
585 assert_eq!(
586 TransactionIsolation::RepeatableRead.description(),
587 "可重复读"
588 );
589 assert_eq!(TransactionIsolation::Serializable.description(), "串行化");
590 }
591
592 #[test]
593 fn test_isolation_level_strictness_order() {
594 assert!(
595 TransactionIsolation::ReadUncommitted.strictness()
596 < TransactionIsolation::ReadCommitted.strictness()
597 );
598 assert!(
599 TransactionIsolation::ReadCommitted.strictness()
600 < TransactionIsolation::RepeatableRead.strictness()
601 );
602 assert!(
603 TransactionIsolation::RepeatableRead.strictness()
604 < TransactionIsolation::Serializable.strictness()
605 );
606 }
607
608 #[test]
609 fn test_isolation_level_from_str() {
610 assert_eq!(
611 TransactionIsolation::from_str("READ COMMITTED"),
612 Some(TransactionIsolation::ReadCommitted)
613 );
614 assert_eq!(
615 TransactionIsolation::from_str("read committed"),
616 Some(TransactionIsolation::ReadCommitted)
617 );
618 assert_eq!(
619 TransactionIsolation::from_str("READ_COMMITTED"),
620 Some(TransactionIsolation::ReadCommitted)
621 );
622 assert_eq!(
623 TransactionIsolation::from_str("SERIALIZABLE"),
624 Some(TransactionIsolation::Serializable)
625 );
626 assert_eq!(TransactionIsolation::from_str("UNKNOWN"), None);
627 }
628
629 #[test]
630 fn test_isolation_level_set_session_sql_mysql() {
631 let sql = TransactionIsolation::ReadCommitted.set_session_sql(AnyBackend::MySql);
632 assert!(sql.contains("SET SESSION TRANSACTION ISOLATION LEVEL"));
633 assert!(sql.contains("READ COMMITTED"));
634 }
635
636 #[test]
637 fn test_isolation_level_set_session_sql_postgres() {
638 let sql = TransactionIsolation::Serializable.set_session_sql(AnyBackend::Postgres);
639 assert!(sql.contains("SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL"));
640 assert!(sql.contains("SERIALIZABLE"));
641 }
642
643 #[test]
644 fn test_isolation_level_set_session_sql_sqlite_empty() {
645 let sql = TransactionIsolation::ReadCommitted.set_session_sql(AnyBackend::Sqlite);
646 assert!(sql.is_empty(), "SQLite 不支持设置隔离级别");
647 }
648
649 #[test]
650 fn test_isolation_level_set_transaction_sql() {
651 let mysql_sql = TransactionIsolation::RepeatableRead.set_transaction_sql(AnyBackend::MySql);
652 assert!(mysql_sql.contains("SET TRANSACTION ISOLATION LEVEL"));
653 assert!(mysql_sql.contains("REPEATABLE READ"));
654
655 let pg_sql = TransactionIsolation::RepeatableRead.set_transaction_sql(AnyBackend::Postgres);
656 assert!(pg_sql.contains("SET TRANSACTION ISOLATION LEVEL"));
657
658 let sqlite_sql =
659 TransactionIsolation::RepeatableRead.set_transaction_sql(AnyBackend::Sqlite);
660 assert!(sqlite_sql.is_empty());
661 }
662
663 #[test]
664 fn test_isolation_level_query_sql() {
665 assert_eq!(
666 TransactionIsolation::ReadCommitted.query_sql(AnyBackend::MySql),
667 "SELECT @@transaction_isolation"
668 );
669 assert_eq!(
670 TransactionIsolation::ReadCommitted.query_sql(AnyBackend::Postgres),
671 "SHOW transaction_isolation"
672 );
673 assert_eq!(
674 TransactionIsolation::ReadCommitted.query_sql(AnyBackend::Sqlite),
675 ""
676 );
677 }
678
679 #[test]
680 fn test_isolation_level_display() {
681 let level = TransactionIsolation::Serializable;
682 let s = format!("{}", level);
683 assert_eq!(s, "SERIALIZABLE");
684 }
685
686 #[test]
687 fn test_isolation_level_default() {
688 let level = TransactionIsolation::default();
689 assert_eq!(level, TransactionIsolation::ReadCommitted);
690 }
691
692 #[test]
693 fn test_isolation_level_equality() {
694 assert_eq!(
695 TransactionIsolation::ReadCommitted,
696 TransactionIsolation::ReadCommitted
697 );
698 assert_ne!(
699 TransactionIsolation::ReadCommitted,
700 TransactionIsolation::Serializable
701 );
702 }
703
704 #[test]
707 fn test_pool_config_default() {
708 let config = EnhancedPoolConfig::default();
709 assert_eq!(config.max_connections, 10);
710 assert!(config.min_idle.is_none());
711 assert_eq!(config.acquire_timeout, Duration::from_secs(30));
712 assert_eq!(config.test_query, "SELECT 1");
713 assert!(!config.test_on_acquire);
714 }
715
716 #[test]
717 fn test_pool_config_builder_basic() {
718 let config = EnhancedPoolConfig::builder()
719 .max_connections(20)
720 .min_idle(5)
721 .acquire_timeout_secs(60)
722 .build()
723 .unwrap();
724 assert_eq!(config.max_connections, 20);
725 assert_eq!(config.min_idle, Some(5));
726 assert_eq!(config.acquire_timeout, Duration::from_secs(60));
727 }
728
729 #[test]
730 fn test_pool_config_builder_test_on_acquire() {
731 let config = EnhancedPoolConfig::builder()
732 .test_on_acquire()
733 .test_query("SELECT 1 FROM dual")
734 .build()
735 .unwrap();
736 assert!(config.test_on_acquire);
737 assert_eq!(config.test_query, "SELECT 1 FROM dual");
738 }
739
740 #[test]
741 fn test_pool_config_builder_with_name() {
742 let config = EnhancedPoolConfig::builder()
743 .name("primary-pool")
744 .build()
745 .unwrap();
746 assert_eq!(config.pool_name, Some("primary-pool".to_string()));
747 }
748
749 #[test]
750 fn test_pool_config_validate_max_connections_zero() {
751 let config = EnhancedPoolConfig {
752 max_connections: 0,
753 ..Default::default()
754 };
755 assert!(config.validate().is_err());
756 }
757
758 #[test]
759 fn test_pool_config_validate_min_idle_exceeds_max() {
760 let config = EnhancedPoolConfig {
761 max_connections: 5,
762 min_idle: Some(10),
763 ..Default::default()
764 };
765 assert!(config.validate().is_err());
766 }
767
768 #[test]
769 fn test_pool_config_validate_timeout_zero() {
770 let config = EnhancedPoolConfig {
771 acquire_timeout: Duration::from_secs(0),
772 ..Default::default()
773 };
774 assert!(config.validate().is_err());
775 }
776
777 #[test]
778 fn test_pool_config_validate_empty_test_query() {
779 let config = EnhancedPoolConfig {
780 test_query: "".to_string(),
781 ..Default::default()
782 };
783 assert!(config.validate().is_err());
784 }
785
786 #[test]
787 fn test_pool_config_validate_valid() {
788 let config = EnhancedPoolConfig::default();
789 assert!(config.validate().is_ok());
790 }
791
792 #[test]
793 fn test_pool_config_summary() {
794 let config = EnhancedPoolConfig::builder()
795 .max_connections(15)
796 .name("test-pool")
797 .build()
798 .unwrap();
799 let summary = config.summary();
800 assert!(summary.contains("max=15"));
801 assert!(summary.contains("name=Some(\"test-pool\")"));
802 }
803
804 #[test]
805 fn test_pool_config_builder_millis_timeout() {
806 let config = EnhancedPoolConfig::builder()
807 .acquire_timeout_millis(500)
808 .build()
809 .unwrap();
810 assert_eq!(config.acquire_timeout, Duration::from_millis(500));
811 }
812
813 #[test]
814 fn test_pool_config_builder_idle_and_lifetime() {
815 let config = EnhancedPoolConfig::builder()
816 .idle_timeout_secs(300)
817 .max_lifetime_secs(900)
818 .build()
819 .unwrap();
820 assert_eq!(config.idle_timeout, Some(Duration::from_secs(300)));
821 assert_eq!(config.max_lifetime, Some(Duration::from_secs(900)));
822 }
823
824 #[test]
827 fn test_cache_basic_put_and_get() {
828 let cache = PreparedStatementCache::new(10);
829 cache.put("SELECT * FROM users WHERE id = ?", "stmt_1");
830
831 let result = cache.get("SELECT * FROM users WHERE id = ?");
832 assert_eq!(result, Some("stmt_1".to_string()));
833 }
834
835 #[test]
836 fn test_cache_miss() {
837 let cache = PreparedStatementCache::new(10);
838 let result = cache.get("SELECT * FROM nonexist");
839 assert!(result.is_none());
840
841 let stats = cache.stats();
842 assert_eq!(stats.misses, 1);
843 assert_eq!(stats.hits, 0);
844 }
845
846 #[test]
847 fn test_cache_hit_increments_counter() {
848 let cache = PreparedStatementCache::new(10);
849 cache.put("SELECT 1", "stmt_1");
850
851 cache.get("SELECT 1");
852 cache.get("SELECT 1");
853 cache.get("SELECT 1");
854
855 let stats = cache.stats();
856 assert_eq!(stats.hits, 3);
857 }
858
859 #[test]
860 fn test_cache_remove() {
861 let cache = PreparedStatementCache::new(10);
862 cache.put("SELECT 1", "stmt_1");
863 assert!(cache.remove("SELECT 1"));
864 assert!(cache.get("SELECT 1").is_none());
865 }
866
867 #[test]
868 fn test_cache_remove_nonexistent() {
869 let cache = PreparedStatementCache::new(10);
870 assert!(!cache.remove("SELECT 1"));
871 }
872
873 #[test]
874 fn test_cache_clear() {
875 let cache = PreparedStatementCache::new(10);
876 cache.put("SELECT 1", "stmt_1");
877 cache.put("SELECT 2", "stmt_2");
878 cache.clear();
879 assert_eq!(cache.len(), 0);
880 assert!(cache.is_empty());
881 }
882
883 #[test]
884 fn test_cache_lru_eviction() {
885 let cache = PreparedStatementCache::new(2);
886 cache.put("sql_1", "stmt_1");
887 cache.put("sql_2", "stmt_2");
888
889 cache.get("sql_1");
891
892 cache.put("sql_3", "stmt_3");
894
895 assert!(cache.get("sql_1").is_some(), "sql_1 应被保留(最近使用)");
896 assert!(cache.get("sql_2").is_none(), "sql_2 应被 LRU 驱逐");
897 assert!(cache.get("sql_3").is_some(), "sql_3 应存在");
898
899 let stats = cache.stats();
900 assert!(stats.evictions >= 1, "应至少有 1 次驱逐");
901 }
902
903 #[test]
904 fn test_cache_update_existing() {
905 let cache = PreparedStatementCache::new(10);
906 cache.put("SELECT 1", "stmt_1");
907 cache.put("SELECT 1", "stmt_2"); let result = cache.get("SELECT 1");
910 assert_eq!(result, Some("stmt_2".to_string()));
911
912 assert_eq!(cache.len(), 1);
914 }
915
916 #[test]
917 fn test_cache_stats_hit_rate() {
918 let cache = PreparedStatementCache::new(10);
919 cache.put("SELECT 1", "stmt_1");
920
921 cache.get("SELECT 1");
923 cache.get("SELECT 1");
924 cache.get("SELECT 1");
925 cache.get("SELECT 2");
927 cache.get("SELECT 3");
928
929 let stats = cache.stats();
930 assert_eq!(stats.hits, 3);
931 assert_eq!(stats.misses, 2);
932 assert_eq!(stats.total_accesses(), 5);
933 let expected_rate = 3.0 / 5.0;
934 assert!((stats.hit_rate() - expected_rate).abs() < 0.001);
935 }
936
937 #[test]
938 fn test_cache_stats_summary() {
939 let cache = PreparedStatementCache::new(100);
940 cache.put("SELECT 1", "stmt_1");
941 cache.get("SELECT 1");
942
943 let stats = cache.stats();
944 let summary = stats.summary();
945 assert!(summary.contains("hits=1"));
946 assert!(summary.contains("capacity=100"));
947 assert!(summary.contains("hit_rate="));
948 }
949
950 #[test]
951 fn test_cache_capacity_utilization() {
952 let cache = PreparedStatementCache::new(10);
953 cache.put("sql_1", "stmt_1");
954 cache.put("sql_2", "stmt_2");
955
956 let stats = cache.stats();
957 assert_eq!(stats.size, 2);
958 assert_eq!(stats.capacity, 10);
959 assert!((stats.capacity_utilization() - 0.2).abs() < 0.001);
960 }
961
962 #[test]
963 fn test_cache_reset_stats() {
964 let cache = PreparedStatementCache::new(10);
965 cache.put("SELECT 1", "stmt_1");
966 cache.get("SELECT 1");
967 cache.get("SELECT 2");
968
969 cache.reset_stats();
970 let stats = cache.stats();
971 assert_eq!(stats.hits, 0);
972 assert_eq!(stats.misses, 0);
973 assert_eq!(stats.evictions, 0);
974 assert_eq!(stats.size, 1);
976 }
977
978 #[test]
979 fn test_cache_default_capacity() {
980 let cache = PreparedStatementCache::default();
981 assert_eq!(cache.capacity(), 256);
982 }
983
984 #[test]
985 fn test_cache_min_capacity_1() {
986 let cache = PreparedStatementCache::new(0);
987 assert_eq!(cache.capacity(), 1, "容量为 0 时应自动设为 1");
988 }
989
990 #[test]
991 fn test_cache_debug_format() {
992 let cache = PreparedStatementCache::new(10);
993 cache.put("SELECT 1", "stmt_1");
994 let debug_str = format!("{:?}", cache);
995 assert!(debug_str.contains("PreparedStatementCache"));
996 assert!(debug_str.contains("capacity: 10"));
998 assert!(debug_str.contains("size: 1"));
999 }
1000
1001 #[test]
1002 fn test_cache_concurrent_access() {
1003 use std::sync::Arc;
1004 use std::thread;
1005
1006 let cache = Arc::new(PreparedStatementCache::new(100));
1007 let mut handles = Vec::new();
1008
1009 for i in 0..4 {
1010 let c = cache.clone();
1011 handles.push(thread::spawn(move || {
1012 for j in 0..10 {
1013 let sql = format!("SELECT {}", i * 10 + j);
1014 c.put(&sql, &format!("stmt_{}", i * 10 + j));
1015 c.get(&sql);
1016 }
1017 }));
1018 }
1019
1020 for h in handles {
1021 h.join().unwrap();
1022 }
1023
1024 assert_eq!(cache.len(), 40);
1026 let stats = cache.stats();
1027 assert!(stats.hits >= 40, "每个 put 后立即 get 应产生 40 次命中");
1028 }
1029
1030 #[test]
1031 fn test_cache_same_sql_different_whitespace_same_hash() {
1032 let cache = PreparedStatementCache::new(10);
1035 cache.put("SELECT 1", "stmt_1");
1036 cache.put("SELECT 1", "stmt_2"); assert_eq!(cache.len(), 2, "不同空格的 SQL 应为不同条目");
1038 }
1039}