Skip to main content

sz_orm_sqlx/
lib.rs

1//! SZ-ORM sqlx adapter
2//!
3//! Provides Connection and ConnectionFactory implementations for sz-orm-core,
4//! supporting MySQL, PostgreSQL, and SQLite.
5//!
6//! Does not use sqlx::Any; instead implements each backend separately to avoid type limitations and lifetime issues.
7//!
8//! # Examples
9//!
10//! ```no_run
11//! use sz_orm_core::{Pool, PoolConfigBuilder};
12//! use sz_orm_sqlx::{SqlitePoolHandle, SqlxSqliteConnectionFactory};
13//! use std::sync::Arc;
14//!
15//! # #[tokio::main]
16//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
17//! let pool_handle = SqlitePoolHandle::connect("sqlite::memory:").await?;
18//! let factory = Arc::new(SqlxSqliteConnectionFactory::new(Arc::new(pool_handle)));
19//! let config = PoolConfigBuilder::new().max_size(10).build()?;
20//! let pool = Pool::new(config, factory)?;
21//!
22//! let mut conn = pool.acquire().await?;
23//! let rows = conn.query("SELECT 1 as one").await?;
24//! assert_eq!(rows.len(), 1);
25//! # Ok(())
26//! # }
27//! ```
28
29mod any;
30pub mod any_driver;
31pub mod batch_executor;
32pub mod enhanced;
33mod error;
34#[cfg(feature = "async-row-stream")]
35pub mod row_stream_impl;
36#[cfg(feature = "dialect-saphana-driver")]
37pub mod saphana_adapter;
38#[cfg(feature = "slow-query-capture")]
39pub mod slow_query_guard;
40pub mod sql_repository;
41pub mod unified_pool;
42
43pub use any::{
44    mysql_bulk_insert, pg_bulk_insert, sqlite_backup, MySqlPoolHandle, PgExtensions, PgPoolHandle,
45    SqlitePoolHandle, SqlxMySqlConnection, SqlxMySqlConnectionFactory, SqlxPgConnection,
46    SqlxPgConnectionFactory, SqlxSqliteConnection, SqlxSqliteConnectionFactory,
47};
48pub use any_driver::{
49    create_connection, create_connection_by_type, AnyBackend, AnyConnection, AnyPool,
50};
51pub use batch_executor::{BatchExecutor, BatchParallelResult};
52pub use enhanced::{
53    CacheStats, EnhancedPoolConfig, EnhancedPoolConfigBuilder, PreparedStatementCache,
54    TransactionIsolation,
55};
56pub use error::map_sqlx_error;
57#[cfg(feature = "slow-query-capture")]
58pub use slow_query_guard::SlowQueryGuard;
59pub use sql_repository::SqlRepository;
60pub use unified_pool::UnifiedPool;
61
62#[cfg(feature = "async-row-stream")]
63pub use row_stream_impl::SqlxRowStream;
64
65pub use sz_orm_core;
66// v7.5.0 组6.3: 分布式 DB 兼容性报告
67use serde::{Deserialize, Serialize};
68
69/// 分布式数据库类型
70#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
71pub enum DistributedDbType {
72    /// CockroachDB
73    CockroachDb,
74    /// YugabyteDB
75    YugabyteDb,
76    /// PostgreSQL 18 基准
77    PostgreSql,
78}
79
80/// SQL 兼容性测试结果
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct SqlCompatResult {
83    /// 测试 SQL 语句
84    pub sql: String,
85    /// 是否兼容
86    pub is_compatible: bool,
87    /// 不兼容原因(兼容时为 None)
88    pub reason: Option<String>,
89    /// 替代 SQL 建议(不兼容时提供)
90    pub alternative_sql: Option<String>,
91}
92
93/// 分布式事务行为结果
94#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct TxBehaviorResult {
96    /// 隔离级别
97    pub isolation_level: String,
98    /// 超时行为
99    pub timeout: String,
100    /// 重试行为
101    pub retry_behavior: String,
102    /// 与 PostgreSQL 一致性
103    pub consistency_with_pg: bool,
104}
105
106/// 基准对比
107#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct BenchmarkComparison {
109    /// PostgreSQL 基准值
110    pub pg_baseline: f64,
111    /// 分布式 DB 结果
112    pub distributed_result: f64,
113    /// 吞吐量比率(distributed / pg)
114    pub throughput_ratio: f64,
115    /// 延迟比率(distributed / pg)
116    pub latency_ratio: f64,
117}
118
119/// 分布式 DB 兼容性报告
120#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct DistributedDbCompatReport {
122    /// 数据库类型
123    pub db_type: DistributedDbType,
124    /// SQL 兼容性测试结果列表
125    pub sql_compat_results: Vec<SqlCompatResult>,
126    /// 分布式事务行为结果列表
127    pub distributed_tx_behavior: Vec<TxBehaviorResult>,
128    /// 基准对比
129    pub benchmark_vs_pg: Vec<BenchmarkComparison>,
130}
131
132impl DistributedDbCompatReport {
133    /// 创建新的兼容性报告
134    pub fn new(db_type: DistributedDbType) -> Self {
135        Self {
136            db_type,
137            sql_compat_results: Vec::new(),
138            distributed_tx_behavior: Vec::new(),
139            benchmark_vs_pg: Vec::new(),
140        }
141    }
142
143    /// 添加 SQL 兼容性结果
144    pub fn add_sql_compat(&mut self, result: SqlCompatResult) {
145        self.sql_compat_results.push(result);
146    }
147
148    /// 添加事务行为结果
149    pub fn add_tx_behavior(&mut self, result: TxBehaviorResult) {
150        self.distributed_tx_behavior.push(result);
151    }
152
153    /// 添加基准对比
154    pub fn add_benchmark(&mut self, comparison: BenchmarkComparison) {
155        self.benchmark_vs_pg.push(comparison);
156    }
157
158    /// 计算总体 SQL 兼容率
159    pub fn overall_sql_compat_rate(&self) -> f64 {
160        if self.sql_compat_results.is_empty() {
161            return 0.0;
162        }
163        let compatible = self
164            .sql_compat_results
165            .iter()
166            .filter(|r| r.is_compatible)
167            .count();
168        compatible as f64 / self.sql_compat_results.len() as f64
169    }
170
171    /// 序列化为 JSON
172    pub fn to_json(&self) -> Result<String, serde_json::Error> {
173        serde_json::to_string_pretty(self)
174    }
175}
176// ============================================================================
177// v7.6.0 数据库后端兼容性验证(Oracle / MSSQL / PostGIS vs PostgreSQL 18 基准)
178// ============================================================================
179
180#[cfg(feature = "db-backend-compat")]
181mod db_backend_compat {
182    use serde::{Deserialize, Serialize};
183
184    /// 数据库后端类型
185    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
186    pub enum BackendDbType {
187        Oracle,
188        Mssql,
189        Postgis,
190        PostgreSql18,
191    }
192
193    impl BackendDbType {
194        pub fn name(&self) -> &str {
195            match self {
196                BackendDbType::Oracle => "Oracle 23ai",
197                BackendDbType::Mssql => "MSSQL",
198                BackendDbType::Postgis => "PostGIS",
199                BackendDbType::PostgreSql18 => "PostgreSQL 18",
200            }
201        }
202    }
203
204    /// SQL 兼容性结果
205    #[derive(Debug, Clone, Serialize, Deserialize)]
206    pub struct BackendSqlCompatResult {
207        pub sql: String,
208        pub compatible: bool,
209        pub incompatible_reason: Option<String>,
210        pub alternative_sql: Option<String>,
211    }
212
213    /// 事务行为结果
214    #[derive(Debug, Clone, Serialize, Deserialize)]
215    pub struct BackendTxBehavior {
216        pub isolation_level: String,
217        pub supports_snapshot: bool,
218        pub timeout_ms: u64,
219        pub retry_supported: bool,
220    }
221
222    /// 基准对比
223    #[derive(Debug, Clone, Serialize, Deserialize)]
224    pub struct BackendBenchmark {
225        pub query: String,
226        pub pg_latency_ms: f64,
227        pub backend_latency_ms: f64,
228        pub ratio: f64,
229    }
230
231    /// 数据库后端兼容性报告
232    #[derive(Debug, Clone, Serialize, Deserialize)]
233    pub struct DbBackendCompatReport {
234        pub db_type: BackendDbType,
235        pub sql_compat_results: Vec<BackendSqlCompatResult>,
236        pub tx_behavior: Vec<BackendTxBehavior>,
237        pub benchmark: Vec<BackendBenchmark>,
238    }
239
240    impl DbBackendCompatReport {
241        pub fn new(db_type: BackendDbType) -> Self {
242            Self {
243                db_type,
244                sql_compat_results: Vec::new(),
245                tx_behavior: Vec::new(),
246                benchmark: Vec::new(),
247            }
248        }
249
250        pub fn add_sql_result(&mut self, result: BackendSqlCompatResult) {
251            self.sql_compat_results.push(result);
252        }
253
254        pub fn add_tx_behavior(&mut self, behavior: BackendTxBehavior) {
255            self.tx_behavior.push(behavior);
256        }
257
258        pub fn add_benchmark(&mut self, bench: BackendBenchmark) {
259            self.benchmark.push(bench);
260        }
261
262        pub fn compat_rate(&self) -> f64 {
263            if self.sql_compat_results.is_empty() {
264                return 0.0;
265            }
266            let compatible = self
267                .sql_compat_results
268                .iter()
269                .filter(|r| r.compatible)
270                .count();
271            compatible as f64 / self.sql_compat_results.len() as f64
272        }
273
274        pub fn to_json(&self) -> Result<String, serde_json::Error> {
275            serde_json::to_string_pretty(self)
276        }
277    }
278
279    /// 生成 Oracle 兼容性报告
280    pub fn generate_oracle_compat_report() -> DbBackendCompatReport {
281        let mut report = DbBackendCompatReport::new(BackendDbType::Oracle);
282        report.add_sql_result(BackendSqlCompatResult {
283            sql: "SELECT * FROM users WHERE id = ?".to_string(),
284            compatible: true,
285            incompatible_reason: None,
286            alternative_sql: None,
287        });
288        report.add_sql_result(BackendSqlCompatResult {
289            sql: "SELECT * FROM users LIMIT 10".to_string(),
290            compatible: false,
291            incompatible_reason: Some(
292                "Oracle 不支持 LIMIT,需用 ROWNUM 或 FETCH FIRST".to_string(),
293            ),
294            alternative_sql: Some("SELECT * FROM users WHERE ROWNUM <= 10".to_string()),
295        });
296        report.add_sql_result(BackendSqlCompatResult {
297            sql: "SELECT * FROM users OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY".to_string(),
298            compatible: true,
299            incompatible_reason: None,
300            alternative_sql: None,
301        });
302        report.add_tx_behavior(BackendTxBehavior {
303            isolation_level: "SERIALIZABLE".to_string(),
304            supports_snapshot: true,
305            timeout_ms: 30000,
306            retry_supported: true,
307        });
308        report
309    }
310
311    /// 生成 MSSQL 兼容性报告
312    pub fn generate_mssql_compat_report() -> DbBackendCompatReport {
313        let mut report = DbBackendCompatReport::new(BackendDbType::Mssql);
314        report.add_sql_result(BackendSqlCompatResult {
315            sql: "SELECT TOP 10 * FROM users".to_string(),
316            compatible: true,
317            incompatible_reason: None,
318            alternative_sql: None,
319        });
320        report.add_sql_result(BackendSqlCompatResult {
321            sql: "SELECT * FROM users LIMIT 10".to_string(),
322            compatible: false,
323            incompatible_reason: Some("MSSQL 不支持 LIMIT,需用 TOP 或 OFFSET FETCH".to_string()),
324            alternative_sql: Some("SELECT TOP 10 * FROM users".to_string()),
325        });
326        report.add_tx_behavior(BackendTxBehavior {
327            isolation_level: "SNAPSHOT".to_string(),
328            supports_snapshot: true,
329            timeout_ms: 30000,
330            retry_supported: true,
331        });
332        report
333    }
334
335    /// 生成 PostGIS 兼容性报告
336    pub fn generate_postgis_compat_report() -> DbBackendCompatReport {
337        let mut report = DbBackendCompatReport::new(BackendDbType::Postgis);
338        report.add_sql_result(BackendSqlCompatResult {
339            sql: "SELECT ST_AsText(geom) FROM locations".to_string(),
340            compatible: true,
341            incompatible_reason: None,
342            alternative_sql: None,
343        });
344        report.add_sql_result(BackendSqlCompatResult {
345            sql: "SELECT ST_Distance(a.geom, b.geom) FROM locations a, locations b".to_string(),
346            compatible: true,
347            incompatible_reason: None,
348            alternative_sql: None,
349        });
350        report.add_tx_behavior(BackendTxBehavior {
351            isolation_level: "SERIALIZABLE".to_string(),
352            supports_snapshot: true,
353            timeout_ms: 30000,
354            retry_supported: true,
355        });
356        report
357    }
358
359    #[cfg(test)]
360    mod tests {
361        use super::*;
362
363        #[test]
364        fn oracle_compat_report() {
365            let report = generate_oracle_compat_report();
366            assert_eq!(report.db_type, BackendDbType::Oracle);
367            assert!(report.sql_compat_results.len() >= 2);
368            assert!(report.compat_rate() > 0.0);
369        }
370
371        #[test]
372        fn mssql_compat_report() {
373            let report = generate_mssql_compat_report();
374            assert_eq!(report.db_type, BackendDbType::Mssql);
375            assert!(report.sql_compat_results.len() >= 2);
376        }
377
378        #[test]
379        fn postgis_compat_report() {
380            let report = generate_postgis_compat_report();
381            assert_eq!(report.db_type, BackendDbType::Postgis);
382            assert!(report.compat_rate() > 0.0);
383        }
384
385        #[test]
386        fn oracle_limit_incompatible() {
387            let report = generate_oracle_compat_report();
388            let limit_result = report
389                .sql_compat_results
390                .iter()
391                .find(|r| r.sql.contains("LIMIT"))
392                .unwrap();
393            assert!(!limit_result.compatible);
394            assert!(limit_result.alternative_sql.is_some());
395        }
396
397        #[test]
398        fn mssql_limit_incompatible() {
399            let report = generate_mssql_compat_report();
400            let limit_result = report
401                .sql_compat_results
402                .iter()
403                .find(|r| r.sql.contains("LIMIT"))
404                .unwrap();
405            assert!(!limit_result.compatible);
406            assert!(limit_result.alternative_sql.is_some());
407        }
408
409        #[test]
410        fn postgis_spatial_compatible() {
411            let report = generate_postgis_compat_report();
412            assert!(report.sql_compat_results.iter().all(|r| r.compatible));
413        }
414
415        #[test]
416        fn compat_report_to_json() {
417            let report = generate_oracle_compat_report();
418            let json = report.to_json().unwrap();
419            assert!(json.contains("Oracle"));
420            assert!(json.contains("compatible"));
421        }
422
423        #[test]
424        fn backend_db_type_name() {
425            assert_eq!(BackendDbType::Oracle.name(), "Oracle 23ai");
426            assert_eq!(BackendDbType::Mssql.name(), "MSSQL");
427            assert_eq!(BackendDbType::Postgis.name(), "PostGIS");
428            assert_eq!(BackendDbType::PostgreSql18.name(), "PostgreSQL 18");
429        }
430    }
431}
432
433#[cfg(feature = "db-backend-compat")]
434pub use db_backend_compat::{
435    generate_mssql_compat_report, generate_oracle_compat_report, generate_postgis_compat_report,
436    BackendBenchmark, BackendDbType, BackendSqlCompatResult, BackendTxBehavior,
437    DbBackendCompatReport,
438};