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.sql_compat_results.iter().filter(|r| r.is_compatible).count();
156 compatible as f64 / self.sql_compat_results.len() as f64
157 }
158
159 pub fn to_json(&self) -> Result<String, serde_json::Error> {
161 serde_json::to_string_pretty(self)
162 }
163}
164#[cfg(feature = "db-backend-compat")]
169mod db_backend_compat {
170 use serde::{Deserialize, Serialize};
171
172 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
174 pub enum BackendDbType {
175 Oracle,
176 Mssql,
177 Postgis,
178 PostgreSql18,
179 }
180
181 impl BackendDbType {
182 pub fn name(&self) -> &str {
183 match self {
184 BackendDbType::Oracle => "Oracle 23ai",
185 BackendDbType::Mssql => "MSSQL",
186 BackendDbType::Postgis => "PostGIS",
187 BackendDbType::PostgreSql18 => "PostgreSQL 18",
188 }
189 }
190 }
191
192 #[derive(Debug, Clone, Serialize, Deserialize)]
194 pub struct BackendSqlCompatResult {
195 pub sql: String,
196 pub compatible: bool,
197 pub incompatible_reason: Option<String>,
198 pub alternative_sql: Option<String>,
199 }
200
201 #[derive(Debug, Clone, Serialize, Deserialize)]
203 pub struct BackendTxBehavior {
204 pub isolation_level: String,
205 pub supports_snapshot: bool,
206 pub timeout_ms: u64,
207 pub retry_supported: bool,
208 }
209
210 #[derive(Debug, Clone, Serialize, Deserialize)]
212 pub struct BackendBenchmark {
213 pub query: String,
214 pub pg_latency_ms: f64,
215 pub backend_latency_ms: f64,
216 pub ratio: f64,
217 }
218
219 #[derive(Debug, Clone, Serialize, Deserialize)]
221 pub struct DbBackendCompatReport {
222 pub db_type: BackendDbType,
223 pub sql_compat_results: Vec<BackendSqlCompatResult>,
224 pub tx_behavior: Vec<BackendTxBehavior>,
225 pub benchmark: Vec<BackendBenchmark>,
226 }
227
228 impl DbBackendCompatReport {
229 pub fn new(db_type: BackendDbType) -> Self {
230 Self {
231 db_type,
232 sql_compat_results: Vec::new(),
233 tx_behavior: Vec::new(),
234 benchmark: Vec::new(),
235 }
236 }
237
238 pub fn add_sql_result(&mut self, result: BackendSqlCompatResult) {
239 self.sql_compat_results.push(result);
240 }
241
242 pub fn add_tx_behavior(&mut self, behavior: BackendTxBehavior) {
243 self.tx_behavior.push(behavior);
244 }
245
246 pub fn add_benchmark(&mut self, bench: BackendBenchmark) {
247 self.benchmark.push(bench);
248 }
249
250 pub fn compat_rate(&self) -> f64 {
251 if self.sql_compat_results.is_empty() {
252 return 0.0;
253 }
254 let compatible = self
255 .sql_compat_results
256 .iter()
257 .filter(|r| r.compatible)
258 .count();
259 compatible as f64 / self.sql_compat_results.len() as f64
260 }
261
262 pub fn to_json(&self) -> Result<String, serde_json::Error> {
263 serde_json::to_string_pretty(self)
264 }
265 }
266
267 pub fn generate_oracle_compat_report() -> DbBackendCompatReport {
269 let mut report = DbBackendCompatReport::new(BackendDbType::Oracle);
270 report.add_sql_result(BackendSqlCompatResult {
271 sql: "SELECT * FROM users WHERE id = ?".to_string(),
272 compatible: true,
273 incompatible_reason: None,
274 alternative_sql: None,
275 });
276 report.add_sql_result(BackendSqlCompatResult {
277 sql: "SELECT * FROM users LIMIT 10".to_string(),
278 compatible: false,
279 incompatible_reason: Some("Oracle 不支持 LIMIT,需用 ROWNUM 或 FETCH FIRST".to_string()),
280 alternative_sql: Some("SELECT * FROM users WHERE ROWNUM <= 10".to_string()),
281 });
282 report.add_sql_result(BackendSqlCompatResult {
283 sql: "SELECT * FROM users OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY".to_string(),
284 compatible: true,
285 incompatible_reason: None,
286 alternative_sql: None,
287 });
288 report.add_tx_behavior(BackendTxBehavior {
289 isolation_level: "SERIALIZABLE".to_string(),
290 supports_snapshot: true,
291 timeout_ms: 30000,
292 retry_supported: true,
293 });
294 report
295 }
296
297 pub fn generate_mssql_compat_report() -> DbBackendCompatReport {
299 let mut report = DbBackendCompatReport::new(BackendDbType::Mssql);
300 report.add_sql_result(BackendSqlCompatResult {
301 sql: "SELECT TOP 10 * FROM users".to_string(),
302 compatible: true,
303 incompatible_reason: None,
304 alternative_sql: None,
305 });
306 report.add_sql_result(BackendSqlCompatResult {
307 sql: "SELECT * FROM users LIMIT 10".to_string(),
308 compatible: false,
309 incompatible_reason: Some("MSSQL 不支持 LIMIT,需用 TOP 或 OFFSET FETCH".to_string()),
310 alternative_sql: Some("SELECT TOP 10 * FROM users".to_string()),
311 });
312 report.add_tx_behavior(BackendTxBehavior {
313 isolation_level: "SNAPSHOT".to_string(),
314 supports_snapshot: true,
315 timeout_ms: 30000,
316 retry_supported: true,
317 });
318 report
319 }
320
321 pub fn generate_postgis_compat_report() -> DbBackendCompatReport {
323 let mut report = DbBackendCompatReport::new(BackendDbType::Postgis);
324 report.add_sql_result(BackendSqlCompatResult {
325 sql: "SELECT ST_AsText(geom) FROM locations".to_string(),
326 compatible: true,
327 incompatible_reason: None,
328 alternative_sql: None,
329 });
330 report.add_sql_result(BackendSqlCompatResult {
331 sql: "SELECT ST_Distance(a.geom, b.geom) FROM locations a, locations b".to_string(),
332 compatible: true,
333 incompatible_reason: None,
334 alternative_sql: None,
335 });
336 report.add_tx_behavior(BackendTxBehavior {
337 isolation_level: "SERIALIZABLE".to_string(),
338 supports_snapshot: true,
339 timeout_ms: 30000,
340 retry_supported: true,
341 });
342 report
343 }
344
345 #[cfg(test)]
346 mod tests {
347 use super::*;
348
349 #[test]
350 fn oracle_compat_report() {
351 let report = generate_oracle_compat_report();
352 assert_eq!(report.db_type, BackendDbType::Oracle);
353 assert!(report.sql_compat_results.len() >= 2);
354 assert!(report.compat_rate() > 0.0);
355 }
356
357 #[test]
358 fn mssql_compat_report() {
359 let report = generate_mssql_compat_report();
360 assert_eq!(report.db_type, BackendDbType::Mssql);
361 assert!(report.sql_compat_results.len() >= 2);
362 }
363
364 #[test]
365 fn postgis_compat_report() {
366 let report = generate_postgis_compat_report();
367 assert_eq!(report.db_type, BackendDbType::Postgis);
368 assert!(report.compat_rate() > 0.0);
369 }
370
371 #[test]
372 fn oracle_limit_incompatible() {
373 let report = generate_oracle_compat_report();
374 let limit_result = report
375 .sql_compat_results
376 .iter()
377 .find(|r| r.sql.contains("LIMIT"))
378 .unwrap();
379 assert!(!limit_result.compatible);
380 assert!(limit_result.alternative_sql.is_some());
381 }
382
383 #[test]
384 fn mssql_limit_incompatible() {
385 let report = generate_mssql_compat_report();
386 let limit_result = report
387 .sql_compat_results
388 .iter()
389 .find(|r| r.sql.contains("LIMIT"))
390 .unwrap();
391 assert!(!limit_result.compatible);
392 assert!(limit_result.alternative_sql.is_some());
393 }
394
395 #[test]
396 fn postgis_spatial_compatible() {
397 let report = generate_postgis_compat_report();
398 assert!(report.sql_compat_results.iter().all(|r| r.compatible));
399 }
400
401 #[test]
402 fn compat_report_to_json() {
403 let report = generate_oracle_compat_report();
404 let json = report.to_json().unwrap();
405 assert!(json.contains("Oracle"));
406 assert!(json.contains("compatible"));
407 }
408
409 #[test]
410 fn backend_db_type_name() {
411 assert_eq!(BackendDbType::Oracle.name(), "Oracle 23ai");
412 assert_eq!(BackendDbType::Mssql.name(), "MSSQL");
413 assert_eq!(BackendDbType::Postgis.name(), "PostGIS");
414 assert_eq!(BackendDbType::PostgreSql18.name(), "PostgreSQL 18");
415 }
416 }
417}
418
419#[cfg(feature = "db-backend-compat")]
420pub use db_backend_compat::{
421 BackendBenchmark, BackendDbType, BackendSqlCompatResult, BackendTxBehavior,
422 DbBackendCompatReport, generate_mssql_compat_report, generate_oracle_compat_report,
423 generate_postgis_compat_report,
424};