1mod 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;
66use serde::{Deserialize, Serialize};
68
69#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
71pub enum DistributedDbType {
72 CockroachDb,
74 YugabyteDb,
76 PostgreSql,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct SqlCompatResult {
83 pub sql: String,
85 pub is_compatible: bool,
87 pub reason: Option<String>,
89 pub alternative_sql: Option<String>,
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct TxBehaviorResult {
96 pub isolation_level: String,
98 pub timeout: String,
100 pub retry_behavior: String,
102 pub consistency_with_pg: bool,
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct BenchmarkComparison {
109 pub pg_baseline: f64,
111 pub distributed_result: f64,
113 pub throughput_ratio: f64,
115 pub latency_ratio: f64,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct DistributedDbCompatReport {
122 pub db_type: DistributedDbType,
124 pub sql_compat_results: Vec<SqlCompatResult>,
126 pub distributed_tx_behavior: Vec<TxBehaviorResult>,
128 pub benchmark_vs_pg: Vec<BenchmarkComparison>,
130}
131
132impl DistributedDbCompatReport {
133 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 pub fn add_sql_compat(&mut self, result: SqlCompatResult) {
145 self.sql_compat_results.push(result);
146 }
147
148 pub fn add_tx_behavior(&mut self, result: TxBehaviorResult) {
150 self.distributed_tx_behavior.push(result);
151 }
152
153 pub fn add_benchmark(&mut self, comparison: BenchmarkComparison) {
155 self.benchmark_vs_pg.push(comparison);
156 }
157
158 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 pub fn to_json(&self) -> Result<String, serde_json::Error> {
173 serde_json::to_string_pretty(self)
174 }
175}
176#[cfg(feature = "db-backend-compat")]
181mod db_backend_compat {
182 use serde::{Deserialize, Serialize};
183
184 #[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 #[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 #[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 #[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 #[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 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 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 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};