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