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 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;
70use serde::{Deserialize, Serialize};
72
73#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
75pub enum DistributedDbType {
76 CockroachDb,
78 YugabyteDb,
80 PostgreSql,
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct SqlCompatResult {
87 pub sql: String,
89 pub is_compatible: bool,
91 pub reason: Option<String>,
93 pub alternative_sql: Option<String>,
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct TxBehaviorResult {
100 pub isolation_level: String,
102 pub timeout: String,
104 pub retry_behavior: String,
106 pub consistency_with_pg: bool,
108}
109
110#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct BenchmarkComparison {
113 pub pg_baseline: f64,
115 pub distributed_result: f64,
117 pub throughput_ratio: f64,
119 pub latency_ratio: f64,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct DistributedDbCompatReport {
126 pub db_type: DistributedDbType,
128 pub sql_compat_results: Vec<SqlCompatResult>,
130 pub distributed_tx_behavior: Vec<TxBehaviorResult>,
132 pub benchmark_vs_pg: Vec<BenchmarkComparison>,
134}
135
136impl DistributedDbCompatReport {
137 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 pub fn add_sql_compat(&mut self, result: SqlCompatResult) {
149 self.sql_compat_results.push(result);
150 }
151
152 pub fn add_tx_behavior(&mut self, result: TxBehaviorResult) {
154 self.distributed_tx_behavior.push(result);
155 }
156
157 pub fn add_benchmark(&mut self, comparison: BenchmarkComparison) {
159 self.benchmark_vs_pg.push(comparison);
160 }
161
162 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 pub fn to_json(&self) -> Result<String, serde_json::Error> {
177 serde_json::to_string_pretty(self)
178 }
179}
180#[cfg(feature = "db-backend-compat")]
185mod db_backend_compat {
186 use serde::{Deserialize, Serialize};
187
188 #[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 #[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 #[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 #[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 #[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 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 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 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};