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