Skip to main content

sz_orm_sqlx/
enhanced.rs

1//! 增强功能模块:事务隔离级别、连接池配置增强、预备语句缓存
2//!
3//! 本模块为 sz-orm-sqlx 提供三项深度增强能力:
4//!
5//! 1. **事务隔离级别**:支持四种标准隔离级别的设置与查询
6//! 2. **连接池配置增强**:提供更丰富的连接池配置选项与构建器模式
7//! 3. **预备语句缓存**:LRU 策略的预备语句缓存,减少 SQL 解析开销
8
9use 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// ============================================================================
17// 事务隔离级别
18// ============================================================================
19
20/// SQL 事务隔离级别。
21///
22/// 对应 SQL 标准的四种隔离级别,不同后端的 SQL 语法略有差异。
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
24pub enum TransactionIsolation {
25    /// 读未提交(最低隔离级别,允许脏读)
26    ReadUncommitted,
27    /// 读已提交(禁止脏读,允许不可重复读)
28    #[default]
29    ReadCommitted,
30    /// 可重复读(禁止不可重复读,允许幻读)
31    RepeatableRead,
32    /// 串行化(最高隔离级别,完全隔离)
33    Serializable,
34}
35
36impl TransactionIsolation {
37    /// 返回隔离级别的标准名称。
38    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    /// 返回隔离级别的中文描述。
48    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    /// 返回隔离级别的严格程度排序值(0=最低,3=最高)。
58    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    /// 生成设置当前会话隔离级别的 SQL 语句。
68    ///
69    /// 不同后端语法略有差异:
70    /// - MySQL: `SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED`
71    /// - PostgreSQL: `SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ COMMITTED`
72    /// - SQLite: 不支持(SQLite 始终使用 SERIALIZABLE),返回空字符串
73    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                // SQLite 隐式使用 SERIALIZABLE,不支持设置
86                String::new()
87            }
88            AnyBackend::Oracle | AnyBackend::Mssql => {
89                format!("SET TRANSACTION ISOLATION LEVEL {}", self.name())
90            }
91        }
92    }
93
94    /// 生成设置下一事务隔离级别的 SQL 语句。
95    ///
96    /// - MySQL: `SET TRANSACTION ISOLATION LEVEL READ COMMITTED`
97    /// - PostgreSQL: `SET TRANSACTION ISOLATION LEVEL READ COMMITTED`
98    /// - SQLite: 不支持,返回空字符串
99    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    /// 生成查询当前隔离级别的 SQL 语句。
111    ///
112    /// - MySQL: `SELECT @@transaction_isolation`
113    /// - PostgreSQL: `SHOW transaction_isolation`
114    /// - SQLite: 不支持,返回空字符串
115    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    /// 从字符串解析隔离级别(不区分大小写)。
125    #[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// ============================================================================
145// 连接池配置增强
146// ============================================================================
147
148/// 增强的连接池配置。
149///
150/// 提供 比 sqlx::PoolOptions 更丰富的配置项,包括健康检查、测试查询等。
151#[derive(Debug, Clone)]
152pub struct EnhancedPoolConfig {
153    /// 最大连接数(默认 10)
154    pub max_connections: u32,
155    /// 最小空闲连接数(默认 0)
156    pub min_idle: Option<u32>,
157    /// 获取连接超时时间(默认 30 秒)
158    pub acquire_timeout: Duration,
159    /// 空闲连接超时时间(默认 600 秒)
160    pub idle_timeout: Option<Duration>,
161    /// 连接最大生存时间(默认 1800 秒)
162    pub max_lifetime: Option<Duration>,
163    /// 获取连接时是否执行测试查询(默认 false)
164    pub test_on_acquire: bool,
165    /// 测试查询 SQL(默认 "SELECT 1")
166    pub test_query: String,
167    /// 连接池名称(用于日志和监控)
168    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    /// 创建新的配置构建器。
188    pub fn builder() -> EnhancedPoolConfigBuilder {
189        EnhancedPoolConfigBuilder::default()
190    }
191
192    /// 校验配置合法性。
193    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    /// 返回配置摘要信息。
215    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/// 增强连接池配置构建器。
228#[derive(Debug, Clone, Default)]
229pub struct EnhancedPoolConfigBuilder {
230    config: EnhancedPoolConfig,
231}
232
233impl EnhancedPoolConfigBuilder {
234    /// 设置最大连接数。
235    pub fn max_connections(mut self, n: u32) -> Self {
236        self.config.max_connections = n;
237        self
238    }
239
240    /// 设置最小空闲连接数。
241    pub fn min_idle(mut self, n: u32) -> Self {
242        self.config.min_idle = Some(n);
243        self
244    }
245
246    /// 设置获取连接超时时间(秒)。
247    pub fn acquire_timeout_secs(mut self, secs: u64) -> Self {
248        self.config.acquire_timeout = Duration::from_secs(secs);
249        self
250    }
251
252    /// 设置获取连接超时时间(毫秒)。
253    pub fn acquire_timeout_millis(mut self, millis: u64) -> Self {
254        self.config.acquire_timeout = Duration::from_millis(millis);
255        self
256    }
257
258    /// 设置空闲连接超时时间(秒)。
259    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    /// 设置连接最大生存时间(秒)。
265    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    /// 启用获取连接时的测试查询。
271    pub fn test_on_acquire(mut self) -> Self {
272        self.config.test_on_acquire = true;
273        self
274    }
275
276    /// 设置测试查询 SQL。
277    pub fn test_query(mut self, sql: &str) -> Self {
278        self.config.test_query = sql.to_string();
279        self
280    }
281
282    /// 设置连接池名称。
283    pub fn name(mut self, name: &str) -> Self {
284        self.config.pool_name = Some(name.to_string());
285        self
286    }
287
288    /// 构建配置,执行校验。
289    pub fn build(self) -> Result<EnhancedPoolConfig, String> {
290        self.config.validate()?;
291        Ok(self.config)
292    }
293}
294
295// ============================================================================
296// 预备语句缓存
297// ============================================================================
298
299/// 预备语句缓存条目。
300#[derive(Debug, Clone)]
301#[allow(dead_code)]
302struct CacheEntry {
303    /// 预备语句 ID 或名称
304    statement_id: String,
305    /// 创建时的访问序号
306    created_seq: u64,
307    /// 最后访问序号(用于 LRU 排序,单调递增)
308    last_access_seq: u64,
309    /// 命中次数
310    hit_count: u64,
311}
312
313/// 预备语句缓存统计信息。
314#[derive(Debug, Clone, Default)]
315pub struct CacheStats {
316    /// 缓存命中次数
317    pub hits: u64,
318    /// 缓存未命中次数
319    pub misses: u64,
320    /// 缓存驱逐次数
321    pub evictions: u64,
322    /// 当前缓存条目数
323    pub size: usize,
324    /// 最大缓存容量
325    pub capacity: usize,
326}
327
328impl CacheStats {
329    /// 计算缓存命中率(0.0 ~ 1.0)。
330    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    /// 返回统计信息摘要字符串。
339    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    /// 计算容量利用率(0.0 ~ 1.0)。
353    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    /// 计算总访问次数。
361    pub fn total_accesses(&self) -> u64 {
362        self.hits + self.misses
363    }
364}
365
366/// 预备语句缓存(LRU 策略)。
367///
368/// 缓存 SQL 语句到预备语句 ID 的映射,避免重复解析和编译 SQL。
369/// 使用 LRU(Least Recently Used)策略在容量满时驱逐最久未使用的条目。
370///
371/// # 线程安全
372///
373/// 内部使用 `Mutex` 保护,可安全跨线程共享。
374/// LRU 排序基于单调递增的原子计数器,不受系统时钟精度影响。
375pub struct PreparedStatementCache {
376    /// 缓存映射:SQL 哈希 → 缓存条目
377    entries: Mutex<HashMap<u64, CacheEntry>>,
378    /// 最大缓存容量
379    capacity: usize,
380    /// 统计信息
381    stats: Mutex<CacheStats>,
382    /// 单调递增的访问序号(用于 LRU 排序,避免时钟精度问题)
383    access_seq: AtomicU64,
384}
385
386impl PreparedStatementCache {
387    /// 创建新的预备语句缓存。
388    ///
389    /// # 参数
390    ///
391    /// - `capacity`: 最大缓存条目数(建议 100-1000)
392    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    /// 计算 SQL 语句的哈希值(使用 FNV-1a 算法,无需额外依赖)。
409    fn hash_sql(sql: &str) -> u64 {
410        // FNV-1a 64-bit
411        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    /// 获取下一个单调递增的访问序号。
422    fn next_seq(&self) -> u64 {
423        self.access_seq.fetch_add(1, Ordering::Relaxed)
424    }
425
426    /// 查询缓存中是否存在指定 SQL 的预备语句。
427    ///
428    /// 如果命中,更新最后访问序号并增加命中计数。
429    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    /// 向缓存中插入预备语句。
448    ///
449    /// 如果缓存已满,驱逐最久未使用的条目(LRU)。
450    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        // 如果已存在,更新
458        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        // 检查是否需要 LRU 驱逐
465        if entries.len() >= self.capacity {
466            // 找到 last_access_seq 最小的条目(最久未使用)
467            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    /// 从缓存中移除指定 SQL 的预备语句。
490    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    /// 清空缓存。
502    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    /// 获取缓存统计信息。
510    pub fn stats(&self) -> CacheStats {
511        let stats = self.stats.lock();
512        stats.clone()
513    }
514
515    /// 获取缓存容量。
516    pub fn capacity(&self) -> usize {
517        self.capacity
518    }
519
520    /// 获取当前缓存条目数。
521    pub fn len(&self) -> usize {
522        self.entries.lock().len()
523    }
524
525    /// 缓存是否为空。
526    pub fn is_empty(&self) -> bool {
527        self.len() == 0
528    }
529
530    /// 重置统计信息(不清空缓存条目)。
531    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// ============================================================================
559// 测试模块
560// ============================================================================
561
562#[cfg(test)]
563mod tests {
564    use super::*;
565
566    // ---- TransactionIsolation 测试 ----
567
568    #[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    // ---- EnhancedPoolConfig 测试 ----
712
713    #[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    // ---- PreparedStatementCache 测试 ----
832
833    #[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        // 访问 sql_1 使其成为最近使用
897        cache.get("sql_1");
898
899        // 插入 sql_3,应驱逐最久未使用的 sql_2
900        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"); // 更新
915
916        let result = cache.get("SELECT 1");
917        assert_eq!(result, Some("stmt_2".to_string()));
918
919        // 更新不应增加条目数
920        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        // 3 次命中
929        cache.get("SELECT 1");
930        cache.get("SELECT 1");
931        cache.get("SELECT 1");
932        // 2 次未命中
933        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        // 条目不清空
982        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        // debug_struct 使用 `: ` 作为键值分隔符
1004        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        // 所有线程完成后,缓存应包含 40 个条目
1032        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        // 注意:当前实现基于字节级哈希,不同空格的 SQL 会被视为不同条目
1040        // 此测试验证行为符合预期(字节级哈希)
1041        let cache = PreparedStatementCache::new(10);
1042        cache.put("SELECT 1", "stmt_1");
1043        cache.put("SELECT  1", "stmt_2"); // 两个空格
1044        assert_eq!(cache.len(), 2, "不同空格的 SQL 应为不同条目");
1045    }
1046}