1mod any;
30pub mod any_driver;
31pub mod enhanced;
32mod error;
33#[cfg(feature = "async-row-stream")]
34pub mod row_stream_impl;
35#[cfg(feature = "dialect-saphana-driver")]
36pub mod saphana_adapter;
37pub mod unified_pool;
38
39pub use any::{
40 mysql_bulk_insert, pg_bulk_insert, sqlite_backup, MySqlPoolHandle, PgExtensions, PgPoolHandle,
41 SqlitePoolHandle, SqlxMySqlConnection, SqlxMySqlConnectionFactory, SqlxPgConnection,
42 SqlxPgConnectionFactory, SqlxSqliteConnection, SqlxSqliteConnectionFactory,
43};
44pub use any_driver::{
45 create_connection, create_connection_by_type, AnyBackend, AnyConnection, AnyPool,
46};
47pub use enhanced::{
48 CacheStats, EnhancedPoolConfig, EnhancedPoolConfigBuilder, PreparedStatementCache,
49 TransactionIsolation,
50};
51pub use error::map_sqlx_error;
52pub use unified_pool::UnifiedPool;
53
54#[cfg(feature = "async-row-stream")]
55pub use row_stream_impl::SqlxRowStream;
56
57pub use sz_orm_core;
58use serde::{Deserialize, Serialize};
60
61#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
63pub enum DistributedDbType {
64 CockroachDb,
66 YugabyteDb,
68 PostgreSql,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct SqlCompatResult {
75 pub sql: String,
77 pub is_compatible: bool,
79 pub reason: Option<String>,
81 pub alternative_sql: Option<String>,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct TxBehaviorResult {
88 pub isolation_level: String,
90 pub timeout: String,
92 pub retry_behavior: String,
94 pub consistency_with_pg: bool,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct BenchmarkComparison {
101 pub pg_baseline: f64,
103 pub distributed_result: f64,
105 pub throughput_ratio: f64,
107 pub latency_ratio: f64,
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct DistributedDbCompatReport {
114 pub db_type: DistributedDbType,
116 pub sql_compat_results: Vec<SqlCompatResult>,
118 pub distributed_tx_behavior: Vec<TxBehaviorResult>,
120 pub benchmark_vs_pg: Vec<BenchmarkComparison>,
122}
123
124impl DistributedDbCompatReport {
125 pub fn new(db_type: DistributedDbType) -> Self {
127 Self {
128 db_type,
129 sql_compat_results: Vec::new(),
130 distributed_tx_behavior: Vec::new(),
131 benchmark_vs_pg: Vec::new(),
132 }
133 }
134
135 pub fn add_sql_compat(&mut self, result: SqlCompatResult) {
137 self.sql_compat_results.push(result);
138 }
139
140 pub fn add_tx_behavior(&mut self, result: TxBehaviorResult) {
142 self.distributed_tx_behavior.push(result);
143 }
144
145 pub fn add_benchmark(&mut self, comparison: BenchmarkComparison) {
147 self.benchmark_vs_pg.push(comparison);
148 }
149
150 pub fn overall_sql_compat_rate(&self) -> f64 {
152 if self.sql_compat_results.is_empty() {
153 return 0.0;
154 }
155 let compatible = self
156 .sql_compat_results
157 .iter()
158 .filter(|r| r.is_compatible)
159 .count();
160 compatible as f64 / self.sql_compat_results.len() as f64
161 }
162
163 pub fn to_json(&self) -> Result<String, serde_json::Error> {
165 serde_json::to_string_pretty(self)
166 }
167}
168#[cfg(feature = "db-backend-compat")]
173mod db_backend_compat {
174 use serde::{Deserialize, Serialize};
175
176 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
178 pub enum BackendDbType {
179 Oracle,
180 Mssql,
181 Postgis,
182 PostgreSql18,
183 }
184
185 impl BackendDbType {
186 pub fn name(&self) -> &str {
187 match self {
188 BackendDbType::Oracle => "Oracle 23ai",
189 BackendDbType::Mssql => "MSSQL",
190 BackendDbType::Postgis => "PostGIS",
191 BackendDbType::PostgreSql18 => "PostgreSQL 18",
192 }
193 }
194 }
195
196 #[derive(Debug, Clone, Serialize, Deserialize)]
198 pub struct BackendSqlCompatResult {
199 pub sql: String,
200 pub compatible: bool,
201 pub incompatible_reason: Option<String>,
202 pub alternative_sql: Option<String>,
203 }
204
205 #[derive(Debug, Clone, Serialize, Deserialize)]
207 pub struct BackendTxBehavior {
208 pub isolation_level: String,
209 pub supports_snapshot: bool,
210 pub timeout_ms: u64,
211 pub retry_supported: bool,
212 }
213
214 #[derive(Debug, Clone, Serialize, Deserialize)]
216 pub struct BackendBenchmark {
217 pub query: String,
218 pub pg_latency_ms: f64,
219 pub backend_latency_ms: f64,
220 pub ratio: f64,
221 }
222
223 #[derive(Debug, Clone, Serialize, Deserialize)]
225 pub struct DbBackendCompatReport {
226 pub db_type: BackendDbType,
227 pub sql_compat_results: Vec<BackendSqlCompatResult>,
228 pub tx_behavior: Vec<BackendTxBehavior>,
229 pub benchmark: Vec<BackendBenchmark>,
230 }
231
232 impl DbBackendCompatReport {
233 pub fn new(db_type: BackendDbType) -> Self {
234 Self {
235 db_type,
236 sql_compat_results: Vec::new(),
237 tx_behavior: Vec::new(),
238 benchmark: Vec::new(),
239 }
240 }
241
242 pub fn add_sql_result(&mut self, result: BackendSqlCompatResult) {
243 self.sql_compat_results.push(result);
244 }
245
246 pub fn add_tx_behavior(&mut self, behavior: BackendTxBehavior) {
247 self.tx_behavior.push(behavior);
248 }
249
250 pub fn add_benchmark(&mut self, bench: BackendBenchmark) {
251 self.benchmark.push(bench);
252 }
253
254 pub fn compat_rate(&self) -> f64 {
255 if self.sql_compat_results.is_empty() {
256 return 0.0;
257 }
258 let compatible = self
259 .sql_compat_results
260 .iter()
261 .filter(|r| r.compatible)
262 .count();
263 compatible as f64 / self.sql_compat_results.len() as f64
264 }
265
266 pub fn to_json(&self) -> Result<String, serde_json::Error> {
267 serde_json::to_string_pretty(self)
268 }
269 }
270
271 pub fn generate_oracle_compat_report() -> DbBackendCompatReport {
273 let mut report = DbBackendCompatReport::new(BackendDbType::Oracle);
274 report.add_sql_result(BackendSqlCompatResult {
275 sql: "SELECT * FROM users WHERE id = ?".to_string(),
276 compatible: true,
277 incompatible_reason: None,
278 alternative_sql: None,
279 });
280 report.add_sql_result(BackendSqlCompatResult {
281 sql: "SELECT * FROM users LIMIT 10".to_string(),
282 compatible: false,
283 incompatible_reason: Some(
284 "Oracle 不支持 LIMIT,需用 ROWNUM 或 FETCH FIRST".to_string(),
285 ),
286 alternative_sql: Some("SELECT * FROM users WHERE ROWNUM <= 10".to_string()),
287 });
288 report.add_sql_result(BackendSqlCompatResult {
289 sql: "SELECT * FROM users OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY".to_string(),
290 compatible: true,
291 incompatible_reason: None,
292 alternative_sql: None,
293 });
294 report.add_tx_behavior(BackendTxBehavior {
295 isolation_level: "SERIALIZABLE".to_string(),
296 supports_snapshot: true,
297 timeout_ms: 30000,
298 retry_supported: true,
299 });
300 report
301 }
302
303 pub fn generate_mssql_compat_report() -> DbBackendCompatReport {
305 let mut report = DbBackendCompatReport::new(BackendDbType::Mssql);
306 report.add_sql_result(BackendSqlCompatResult {
307 sql: "SELECT TOP 10 * FROM users".to_string(),
308 compatible: true,
309 incompatible_reason: None,
310 alternative_sql: None,
311 });
312 report.add_sql_result(BackendSqlCompatResult {
313 sql: "SELECT * FROM users LIMIT 10".to_string(),
314 compatible: false,
315 incompatible_reason: Some("MSSQL 不支持 LIMIT,需用 TOP 或 OFFSET FETCH".to_string()),
316 alternative_sql: Some("SELECT TOP 10 * FROM users".to_string()),
317 });
318 report.add_tx_behavior(BackendTxBehavior {
319 isolation_level: "SNAPSHOT".to_string(),
320 supports_snapshot: true,
321 timeout_ms: 30000,
322 retry_supported: true,
323 });
324 report
325 }
326
327 pub fn generate_postgis_compat_report() -> DbBackendCompatReport {
329 let mut report = DbBackendCompatReport::new(BackendDbType::Postgis);
330 report.add_sql_result(BackendSqlCompatResult {
331 sql: "SELECT ST_AsText(geom) FROM locations".to_string(),
332 compatible: true,
333 incompatible_reason: None,
334 alternative_sql: None,
335 });
336 report.add_sql_result(BackendSqlCompatResult {
337 sql: "SELECT ST_Distance(a.geom, b.geom) FROM locations a, locations b".to_string(),
338 compatible: true,
339 incompatible_reason: None,
340 alternative_sql: None,
341 });
342 report.add_tx_behavior(BackendTxBehavior {
343 isolation_level: "SERIALIZABLE".to_string(),
344 supports_snapshot: true,
345 timeout_ms: 30000,
346 retry_supported: true,
347 });
348 report
349 }
350
351 #[cfg(test)]
352 mod tests {
353 use super::*;
354
355 #[test]
356 fn oracle_compat_report() {
357 let report = generate_oracle_compat_report();
358 assert_eq!(report.db_type, BackendDbType::Oracle);
359 assert!(report.sql_compat_results.len() >= 2);
360 assert!(report.compat_rate() > 0.0);
361 }
362
363 #[test]
364 fn mssql_compat_report() {
365 let report = generate_mssql_compat_report();
366 assert_eq!(report.db_type, BackendDbType::Mssql);
367 assert!(report.sql_compat_results.len() >= 2);
368 }
369
370 #[test]
371 fn postgis_compat_report() {
372 let report = generate_postgis_compat_report();
373 assert_eq!(report.db_type, BackendDbType::Postgis);
374 assert!(report.compat_rate() > 0.0);
375 }
376
377 #[test]
378 fn oracle_limit_incompatible() {
379 let report = generate_oracle_compat_report();
380 let limit_result = report
381 .sql_compat_results
382 .iter()
383 .find(|r| r.sql.contains("LIMIT"))
384 .unwrap();
385 assert!(!limit_result.compatible);
386 assert!(limit_result.alternative_sql.is_some());
387 }
388
389 #[test]
390 fn mssql_limit_incompatible() {
391 let report = generate_mssql_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 postgis_spatial_compatible() {
403 let report = generate_postgis_compat_report();
404 assert!(report.sql_compat_results.iter().all(|r| r.compatible));
405 }
406
407 #[test]
408 fn compat_report_to_json() {
409 let report = generate_oracle_compat_report();
410 let json = report.to_json().unwrap();
411 assert!(json.contains("Oracle"));
412 assert!(json.contains("compatible"));
413 }
414
415 #[test]
416 fn backend_db_type_name() {
417 assert_eq!(BackendDbType::Oracle.name(), "Oracle 23ai");
418 assert_eq!(BackendDbType::Mssql.name(), "MSSQL");
419 assert_eq!(BackendDbType::Postgis.name(), "PostGIS");
420 assert_eq!(BackendDbType::PostgreSql18.name(), "PostgreSQL 18");
421 }
422 }
423}
424
425#[cfg(feature = "db-backend-compat")]
426pub use db_backend_compat::{
427 generate_mssql_compat_report, generate_oracle_compat_report, generate_postgis_compat_report,
428 BackendBenchmark, BackendDbType, BackendSqlCompatResult, BackendTxBehavior,
429 DbBackendCompatReport,
430};