1use std::future::Future;
32use std::pin::Pin;
33use std::sync::Arc;
34use sz_orm_core::{
35 Connection, ConnectionFactory, DbError, Dialect, MySqlDialect, OracleDialect,
36 PostgreSqlDialect, QueryRows, QueryStreamItem, QueryValues, SqlServerDialect, SqliteDialect,
37 Value,
38};
39
40use sz_orm_core::DbType;
41
42use crate::any::{
43 MySqlPoolHandle, PgPoolHandle, SqlitePoolHandle, SqlxMySqlConnectionFactory,
44 SqlxPgConnectionFactory, SqlxSqliteConnectionFactory,
45};
46
47#[cfg(feature = "oracle")]
48use sz_orm_oracle::{OracleConnectionFactory, OraclePoolHandle};
49
50#[cfg(feature = "mssql")]
51use sz_orm_mssql::{MssqlConnectionFactory, MssqlPoolHandle};
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59#[non_exhaustive]
60pub enum AnyBackend {
61 MySql,
63 Postgres,
65 Sqlite,
67 Oracle,
69 Mssql,
71}
72
73impl AnyBackend {
74 pub fn from_dsn(dsn: &str) -> Result<Self, DbError> {
84 if dsn.starts_with("mysql://") || dsn.starts_with("mariadb://") {
85 Ok(AnyBackend::MySql)
86 } else if dsn.starts_with("postgres://") || dsn.starts_with("postgresql://") {
87 Ok(AnyBackend::Postgres)
88 } else if dsn.starts_with("sqlite://") || dsn.starts_with("sqlite:") {
89 Ok(AnyBackend::Sqlite)
90 } else if dsn.starts_with("oracle://") {
91 Ok(AnyBackend::Oracle)
92 } else if dsn.starts_with("mssql://") || dsn.starts_with("sqlserver://") {
93 Ok(AnyBackend::Mssql)
94 } else {
95 Err(DbError::ConnectionRefused(format!(
96 "未知的 DSN scheme: {}(支持 mysql/postgres/sqlite/oracle/mssql)",
97 dsn
98 )))
99 }
100 }
101
102 pub fn from_db_type(db_type: DbType) -> Option<Self> {
116 match db_type {
117 DbType::MySQL | DbType::MariaDB | DbType::TiDB | DbType::OceanBase => {
118 Some(AnyBackend::MySql)
119 }
120 DbType::PostgreSQL | DbType::Kingbase | DbType::PolarDB | DbType::GaussDB => {
121 Some(AnyBackend::Postgres)
122 }
123 DbType::Sqlite => Some(AnyBackend::Sqlite),
124 DbType::Oracle | DbType::Dameng => Some(AnyBackend::Oracle),
125 DbType::SqlServer | DbType::Sybase | DbType::GBase => Some(AnyBackend::Mssql),
126 _ => None,
127 }
128 }
129
130 pub fn name(&self) -> &'static str {
132 match self {
133 AnyBackend::MySql => "mysql",
134 AnyBackend::Postgres => "postgres",
135 AnyBackend::Sqlite => "sqlite",
136 AnyBackend::Oracle => "oracle",
137 AnyBackend::Mssql => "mssql",
138 }
139 }
140
141 pub fn dialect(&self) -> Box<dyn Dialect> {
149 match self {
150 AnyBackend::MySql => Box::new(MySqlDialect),
151 AnyBackend::Postgres => Box::new(PostgreSqlDialect),
152 AnyBackend::Sqlite => Box::new(SqliteDialect),
153 AnyBackend::Oracle => Box::new(OracleDialect),
154 AnyBackend::Mssql => Box::new(SqlServerDialect),
155 }
156 }
157}
158
159pub struct AnyPool {
161 backend: AnyBackend,
162 factory: Arc<dyn ConnectionFactory>,
163}
164
165impl AnyPool {
166 pub async fn connect(dsn: &str) -> Result<Self, DbError> {
174 let backend = AnyBackend::from_dsn(dsn)?;
175 let factory: Arc<dyn ConnectionFactory> = match backend {
176 AnyBackend::MySql => {
177 let handle = Arc::new(MySqlPoolHandle::connect(dsn).await?);
178 Arc::new(SqlxMySqlConnectionFactory::new(handle))
179 }
180 AnyBackend::Postgres => {
181 let handle = Arc::new(PgPoolHandle::connect(dsn).await?);
182 Arc::new(SqlxPgConnectionFactory::new(handle))
183 }
184 AnyBackend::Sqlite => {
185 let handle = Arc::new(SqlitePoolHandle::connect(dsn).await?);
186 Arc::new(SqlxSqliteConnectionFactory::new(handle))
187 }
188 AnyBackend::Oracle => {
189 #[cfg(feature = "oracle")]
190 {
191 let (username, password, connect_string) = parse_oracle_dsn(dsn)?;
192 let handle = Arc::new(OraclePoolHandle::connect(
193 &username,
194 &password,
195 &connect_string,
196 )?);
197 Arc::new(OracleConnectionFactory::new(handle))
198 }
199 #[cfg(not(feature = "oracle"))]
200 {
201 return Err(DbError::ConnectionRefused(
202 "Oracle 后端未启用,请在 Cargo.toml 中添加 features = [\"oracle\"]"
203 .to_string(),
204 ));
205 }
206 }
207 AnyBackend::Mssql => {
208 #[cfg(feature = "mssql")]
209 {
210 let ado_string = parse_mssql_dsn(dsn)?;
211 let handle = Arc::new(MssqlPoolHandle::connect(&ado_string).await?);
212 Arc::new(MssqlConnectionFactory::new(handle))
213 }
214 #[cfg(not(feature = "mssql"))]
215 {
216 return Err(DbError::ConnectionRefused(
217 "MSSQL 后端未启用,请在 Cargo.toml 中添加 features = [\"mssql\"]"
218 .to_string(),
219 ));
220 }
221 }
222 };
223 Ok(Self { backend, factory })
224 }
225
226 pub fn from_factory(backend: AnyBackend, factory: Arc<dyn ConnectionFactory>) -> Self {
228 Self { backend, factory }
229 }
230
231 pub fn backend(&self) -> AnyBackend {
233 self.backend
234 }
235
236 pub fn dialect(&self) -> Box<dyn Dialect> {
240 self.backend.dialect()
241 }
242
243 pub async fn create(&self) -> Result<AnyConnection, DbError> {
245 let conn = self.factory.create().await?;
246 Ok(AnyConnection {
247 backend: self.backend,
248 inner: conn,
249 })
250 }
251}
252
253pub async fn create_connection(dsn: &str) -> Result<Box<dyn Connection>, DbError> {
284 let pool = AnyPool::connect(dsn).await?;
285 let conn = pool.create().await?;
286 Ok(Box::new(conn))
287}
288
289pub async fn create_connection_by_type(
308 db_type: DbType,
309 dsn: &str,
310) -> Result<Box<dyn Connection>, DbError> {
311 let backend = AnyBackend::from_db_type(db_type).ok_or_else(|| {
312 DbError::ConnectionRefused(format!(
313 "DbType {:?} 不在 sz-orm-sqlx 支持范围内(支持 MySQL/PostgreSQL/SQLite/Oracle/MSSQL 及兼容方言)",
314 db_type
315 ))
316 })?;
317 let normalized = normalize_dsn_scheme(backend, dsn);
318 create_connection(&normalized).await
319}
320
321fn normalize_dsn_scheme(backend: AnyBackend, dsn: &str) -> String {
326 let expected_scheme = match backend {
327 AnyBackend::MySql => "mysql://",
328 AnyBackend::Postgres => "postgres://",
329 AnyBackend::Sqlite => "sqlite:",
330 AnyBackend::Oracle => "oracle://",
331 AnyBackend::Mssql => "mssql://",
332 };
333 let known_schemes = [
334 "mysql://",
335 "mariadb://",
336 "postgres://",
337 "postgresql://",
338 "sqlite://",
339 "sqlite:",
340 "oracle://",
341 "mssql://",
342 "sqlserver://",
343 ];
344 for scheme in &known_schemes {
345 if let Some(rest) = dsn.strip_prefix(scheme) {
346 if backend == AnyBackend::from_dsn(dsn).unwrap_or(AnyBackend::MySql) {
347 return dsn.to_string();
348 }
349 return format!("{}{}", expected_scheme, rest);
350 }
351 }
352 dsn.to_string()
353}
354
355#[allow(dead_code)]
361pub(crate) fn parse_oracle_dsn(dsn: &str) -> Result<(String, String, String), DbError> {
362 let rest = dsn
363 .strip_prefix("oracle://")
364 .ok_or_else(|| DbError::ConnectionRefused(format!("无效的 Oracle DSN: {}", dsn)))?;
365 parse_user_pass_host(rest, "Oracle")
366}
367
368#[allow(dead_code)]
374pub(crate) fn parse_mssql_dsn(dsn: &str) -> Result<String, DbError> {
375 let rest = dsn
376 .strip_prefix("mssql://")
377 .or_else(|| dsn.strip_prefix("sqlserver://"))
378 .ok_or_else(|| DbError::ConnectionRefused(format!("无效的 MSSQL DSN: {}", dsn)))?;
379 let (username, password, host_port_db) = parse_user_pass_host(rest, "MSSQL")?;
380 let (host_port, database) = host_port_db
381 .split_once('/')
382 .ok_or_else(|| DbError::ConnectionRefused(format!("MSSQL DSN 缺少 database: {}", dsn)))?;
383 let (host, port) = host_port.split_once(':').unwrap_or((host_port, "1433"));
384 Ok(format!(
385 "Server={},{};Database={};User Id={};Password={};",
386 host, port, database, username, password
387 ))
388}
389
390#[allow(dead_code)]
392fn parse_user_pass_host(rest: &str, backend: &str) -> Result<(String, String, String), DbError> {
393 let (userinfo, hostinfo) = rest
394 .split_once('@')
395 .ok_or_else(|| DbError::ConnectionRefused(format!("{} DSN 缺少 @: {}", backend, rest)))?;
396 let (username, password) = userinfo.split_once(':').ok_or_else(|| {
397 DbError::ConnectionRefused(format!("{} DSN 缺少 password: {}", backend, rest))
398 })?;
399 Ok((
400 username.to_string(),
401 password.to_string(),
402 hostinfo.to_string(),
403 ))
404}
405
406pub struct AnyConnection {
408 backend: AnyBackend,
409 inner: Box<dyn Connection>,
410}
411
412impl AnyConnection {
413 pub fn backend(&self) -> AnyBackend {
415 self.backend
416 }
417}
418
419impl Connection for AnyConnection {
420 fn execute<'a>(
421 &'a mut self,
422 sql: &'a str,
423 ) -> Pin<Box<dyn Future<Output = Result<u64, DbError>> + Send + 'a>> {
424 self.inner.execute(sql)
425 }
426
427 fn query<'a>(
428 &'a mut self,
429 sql: &'a str,
430 ) -> Pin<Box<dyn Future<Output = Result<QueryRows, DbError>> + Send + 'a>> {
431 self.inner.query(sql)
432 }
433
434 fn begin_transaction<'a>(
435 &'a mut self,
436 ) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
437 self.inner.begin_transaction()
438 }
439
440 fn commit<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
441 self.inner.commit()
442 }
443
444 fn rollback<'a>(
445 &'a mut self,
446 ) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
447 self.inner.rollback()
448 }
449
450 fn in_transaction(&self) -> bool {
451 self.inner.in_transaction()
452 }
453
454 fn is_connected(&self) -> bool {
455 self.inner.is_connected()
456 }
457
458 fn ping<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = bool> + Send + 'a>> {
459 self.inner.ping()
460 }
461
462 fn close<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
463 self.inner.close()
464 }
465
466 fn execute_with_params<'a>(
467 &'a mut self,
468 sql: &'a str,
469 params: &'a [Value],
470 ) -> Pin<Box<dyn Future<Output = Result<u64, DbError>> + Send + 'a>> {
471 self.inner.execute_with_params(sql, params)
472 }
473
474 fn query_with_params<'a>(
475 &'a mut self,
476 sql: &'a str,
477 params: &'a [Value],
478 ) -> Pin<Box<dyn Future<Output = Result<QueryRows, DbError>> + Send + 'a>> {
479 self.inner.query_with_params(sql, params)
480 }
481
482 fn query_values<'a>(
483 &'a mut self,
484 sql: &'a str,
485 ) -> Pin<Box<dyn Future<Output = Result<QueryValues, DbError>> + Send + 'a>> {
486 self.inner.query_values(sql)
487 }
488
489 fn query_values_with_params<'a>(
490 &'a mut self,
491 sql: &'a str,
492 params: &'a [Value],
493 ) -> Pin<Box<dyn Future<Output = Result<QueryValues, DbError>> + Send + 'a>> {
494 self.inner.query_values_with_params(sql, params)
495 }
496
497 fn query_stream<'a>(
498 &'a mut self,
499 sql: &'a str,
500 ) -> Pin<Box<dyn futures::Stream<Item = QueryStreamItem> + Send + 'a>> {
501 self.inner.query_stream(sql)
502 }
503
504 fn query_stream_cursor<'a>(
505 &'a mut self,
506 sql: &'a str,
507 batch_size: usize,
508 ) -> Pin<Box<dyn futures::Stream<Item = QueryStreamItem> + Send + 'a>> {
509 self.inner.query_stream_cursor(sql, batch_size)
510 }
511
512 fn execute_batch<'a>(
513 &'a mut self,
514 sqls: &'a [String],
515 ) -> Pin<Box<dyn Future<Output = Result<u64, DbError>> + Send + 'a>> {
516 self.inner.execute_batch(sqls)
517 }
518}
519
520#[cfg(test)]
521mod tests {
522 use super::*;
523
524 #[test]
525 fn test_any_backend_from_dsn_mysql() {
526 assert_eq!(
527 AnyBackend::from_dsn("mysql://root:pass@127.0.0.1/db").unwrap(),
528 AnyBackend::MySql
529 );
530 assert_eq!(
531 AnyBackend::from_dsn("mariadb://root:pass@127.0.0.1/db").unwrap(),
532 AnyBackend::MySql
533 );
534 }
535
536 #[test]
537 fn test_any_backend_from_dsn_postgres() {
538 assert_eq!(
539 AnyBackend::from_dsn("postgres://user:pass@127.0.0.1/db").unwrap(),
540 AnyBackend::Postgres
541 );
542 assert_eq!(
543 AnyBackend::from_dsn("postgresql://user:pass@127.0.0.1/db").unwrap(),
544 AnyBackend::Postgres
545 );
546 }
547
548 #[test]
549 fn test_any_backend_from_dsn_sqlite() {
550 assert_eq!(
551 AnyBackend::from_dsn("sqlite::memory:").unwrap(),
552 AnyBackend::Sqlite
553 );
554 assert_eq!(
555 AnyBackend::from_dsn("sqlite://./test.db").unwrap(),
556 AnyBackend::Sqlite
557 );
558 }
559
560 #[test]
561 fn test_any_backend_from_dsn_oracle() {
562 assert_eq!(
563 AnyBackend::from_dsn("oracle://sys:test123@127.0.0.1:1521/freepdb1").unwrap(),
564 AnyBackend::Oracle
565 );
566 }
567
568 #[test]
569 fn test_any_backend_from_dsn_mssql() {
570 assert_eq!(
571 AnyBackend::from_dsn("mssql://sa:test123@127.0.0.1:1433/testdb").unwrap(),
572 AnyBackend::Mssql
573 );
574 }
575
576 #[test]
577 fn test_any_backend_from_dsn_sqlserver() {
578 assert_eq!(
579 AnyBackend::from_dsn("sqlserver://sa:test123@127.0.0.1:1433/testdb").unwrap(),
580 AnyBackend::Mssql
581 );
582 }
583
584 #[test]
585 fn test_any_backend_from_dsn_unknown_v22() {
586 let result = AnyBackend::from_dsn("redis://127.0.0.1");
587 assert!(result.is_err());
588 if let Err(e) = result {
589 let msg = format!("{}", e);
590 assert!(msg.contains("mysql"));
591 assert!(msg.contains("postgres"));
592 assert!(msg.contains("sqlite"));
593 assert!(msg.contains("oracle"));
594 assert!(msg.contains("mssql"));
595 }
596 }
597
598 #[test]
599 fn test_any_backend_name_v22() {
600 assert_eq!(AnyBackend::MySql.name(), "mysql");
601 assert_eq!(AnyBackend::Postgres.name(), "postgres");
602 assert_eq!(AnyBackend::Sqlite.name(), "sqlite");
603 assert_eq!(AnyBackend::Oracle.name(), "oracle");
604 assert_eq!(AnyBackend::Mssql.name(), "mssql");
605 }
606
607 #[test]
608 fn test_any_backend_equality() {
609 assert_eq!(AnyBackend::MySql, AnyBackend::MySql);
610 assert_ne!(AnyBackend::MySql, AnyBackend::Postgres);
611 assert_ne!(AnyBackend::Postgres, AnyBackend::Sqlite);
612 assert_ne!(AnyBackend::Oracle, AnyBackend::Mssql);
613 assert_ne!(AnyBackend::Oracle, AnyBackend::MySql);
614 }
615
616 #[test]
617 fn test_parse_oracle_dsn() {
618 let (user, pass, cs) =
619 parse_oracle_dsn("oracle://sys:test123@127.0.0.1:1521/freepdb1").unwrap();
620 assert_eq!(user, "sys");
621 assert_eq!(pass, "test123");
622 assert_eq!(cs, "127.0.0.1:1521/freepdb1");
623 }
624
625 #[test]
626 fn test_parse_mssql_dsn() {
627 let ado = parse_mssql_dsn("mssql://sa:test123@127.0.0.1:1433/testdb").unwrap();
628 assert!(ado.contains("Server=127.0.0.1,1433"));
629 assert!(ado.contains("Database=testdb"));
630 assert!(ado.contains("User Id=sa"));
631 assert!(ado.contains("Password=test123"));
632 }
633
634 #[test]
635 fn test_parse_mssql_dsn_sqlserver_scheme() {
636 let ado = parse_mssql_dsn("sqlserver://sa:pass@localhost/testdb").unwrap();
637 assert!(ado.contains("Server=localhost,1433"));
638 assert!(ado.contains("Database=testdb"));
639 }
640
641 #[tokio::test]
644 async fn test_any_pool_sqlite_connect_and_query() {
645 let pool = AnyPool::connect("sqlite::memory:").await.unwrap();
646 assert_eq!(pool.backend(), AnyBackend::Sqlite);
647
648 let mut conn = pool.create().await.unwrap();
649 assert_eq!(conn.backend(), AnyBackend::Sqlite);
650
651 conn.execute("CREATE TABLE test_any (id INTEGER PRIMARY KEY, name TEXT)")
653 .await
654 .unwrap();
655 conn.execute("INSERT INTO test_any (name) VALUES ('Alice')")
656 .await
657 .unwrap();
658 conn.execute("INSERT INTO test_any (name) VALUES ('Bob')")
659 .await
660 .unwrap();
661
662 let rows = conn
664 .query("SELECT * FROM test_any ORDER BY id")
665 .await
666 .unwrap();
667 assert_eq!(rows.len(), 2);
668 assert_eq!(rows[0].get("name").and_then(|v| v.as_str()), Some("Alice"));
669 assert_eq!(rows[1].get("name").and_then(|v| v.as_str()), Some("Bob"));
670 }
671
672 #[tokio::test]
673 async fn test_any_pool_sqlite_transaction_commit() {
674 let pool = AnyPool::connect("sqlite::memory:").await.unwrap();
675 let mut conn = pool.create().await.unwrap();
676
677 conn.execute("CREATE TABLE tx_test (id INTEGER PRIMARY KEY, val INTEGER)")
678 .await
679 .unwrap();
680
681 conn.begin_transaction().await.unwrap();
683 conn.execute("INSERT INTO tx_test (val) VALUES (1)")
684 .await
685 .unwrap();
686 conn.execute("INSERT INTO tx_test (val) VALUES (2)")
687 .await
688 .unwrap();
689 conn.commit().await.unwrap();
690
691 let rows = conn.query("SELECT * FROM tx_test").await.unwrap();
692 assert_eq!(rows.len(), 2);
693 }
694
695 #[tokio::test]
696 async fn test_any_pool_sqlite_transaction_rollback() {
697 let pool = AnyPool::connect("sqlite::memory:").await.unwrap();
698 let mut conn = pool.create().await.unwrap();
699
700 conn.execute("CREATE TABLE tx_rb (id INTEGER PRIMARY KEY, val INTEGER)")
701 .await
702 .unwrap();
703
704 conn.begin_transaction().await.unwrap();
705 conn.execute("INSERT INTO tx_rb (val) VALUES (1)")
706 .await
707 .unwrap();
708 conn.rollback().await.unwrap();
709
710 let rows = conn.query("SELECT * FROM tx_rb").await.unwrap();
711 assert_eq!(rows.len(), 0);
712 }
713
714 #[tokio::test]
715 async fn test_any_connection_in_transaction_state_tracking() {
716 let pool = AnyPool::connect("sqlite::memory:").await.unwrap();
717 let mut conn = pool.create().await.unwrap();
718
719 assert!(!conn.in_transaction(), "新建连接不应处于事务中");
720
721 conn.begin_transaction().await.unwrap();
722 assert!(conn.in_transaction(), "begin 后应处于事务中");
723
724 conn.execute("CREATE TABLE tx_track (id INTEGER PRIMARY KEY)")
725 .await
726 .unwrap();
727 assert!(conn.in_transaction(), "事务内执行 SQL 后仍应处于事务中");
728
729 conn.commit().await.unwrap();
730 assert!(!conn.in_transaction(), "commit 后不应处于事务中");
731
732 conn.begin_transaction().await.unwrap();
733 assert!(conn.in_transaction());
734 conn.rollback().await.unwrap();
735 assert!(!conn.in_transaction(), "rollback 后不应处于事务中");
736 }
737
738 #[tokio::test]
739 async fn test_any_connection_in_transaction_leak_detection() {
740 let pool = AnyPool::connect("sqlite::memory:").await.unwrap();
741 let mut conn = pool.create().await.unwrap();
742
743 conn.execute("CREATE TABLE tx_leak (id INTEGER PRIMARY KEY)")
744 .await
745 .unwrap();
746
747 conn.begin_transaction().await.unwrap();
748 conn.execute("INSERT INTO tx_leak (id) VALUES (1)")
749 .await
750 .unwrap();
751
752 assert!(
753 conn.in_transaction(),
754 "未提交事务应被检测到(泄漏防护前提)"
755 );
756
757 conn.rollback().await.unwrap();
758 assert!(!conn.in_transaction());
759 }
760
761 #[tokio::test]
762 async fn test_any_pool_sqlite_ping() {
763 let pool = AnyPool::connect("sqlite::memory:").await.unwrap();
764 let mut conn = pool.create().await.unwrap();
765 let ok = conn.ping().await;
766 assert!(ok);
767 assert!(conn.is_connected());
768 }
769
770 #[tokio::test]
771 async fn test_any_pool_invalid_dsn() {
772 let result = AnyPool::connect("invalid://dsn").await;
773 assert!(result.is_err());
774 }
775
776 #[tokio::test]
777 async fn test_any_pool_sqlite_count_query() {
778 let pool = AnyPool::connect("sqlite::memory:").await.unwrap();
779 let mut conn = pool.create().await.unwrap();
780
781 conn.execute("CREATE TABLE cnt (id INTEGER PRIMARY KEY)")
782 .await
783 .unwrap();
784 for i in 1..=5 {
785 conn.execute(&format!("INSERT INTO cnt (id) VALUES ({})", i))
786 .await
787 .unwrap();
788 }
789
790 let rows = conn.query("SELECT * FROM cnt").await.unwrap();
792 assert_eq!(rows.len(), 5);
793 }
794
795 #[test]
798 fn test_any_backend_dialect_mapping() {
799 use sz_orm_core::DbType;
800
801 let mysql_d = AnyBackend::MySql.dialect();
802 assert_eq!(mysql_d.db_type(), DbType::MySQL);
803
804 let pg_d = AnyBackend::Postgres.dialect();
805 assert_eq!(pg_d.db_type(), DbType::PostgreSQL);
806
807 let sqlite_d = AnyBackend::Sqlite.dialect();
808 assert_eq!(sqlite_d.db_type(), DbType::Sqlite);
809
810 let oracle_d = AnyBackend::Oracle.dialect();
811 assert_eq!(oracle_d.db_type(), DbType::Oracle);
812
813 let mssql_d = AnyBackend::Mssql.dialect();
814 assert_eq!(mssql_d.db_type(), DbType::SqlServer);
815 }
816
817 #[test]
818 fn test_oracle_dialect_pagination() {
819 let d = AnyBackend::Oracle.dialect();
820 let sql = d.build_pagination("SELECT * FROM users", 2, 10);
821 let upper = sql.to_uppercase();
822 assert!(
823 upper.contains("OFFSET") || upper.contains("FETCH") || upper.contains("ROWNUM"),
824 "Oracle 分页 SQL 应含 OFFSET/FETCH/ROWNUM,实际: {}",
825 sql
826 );
827 assert!(
828 !upper.contains("LIMIT"),
829 "Oracle 分页 SQL 不应含 LIMIT,实际: {}",
830 sql
831 );
832 }
833
834 #[test]
835 fn test_mssql_dialect_pagination() {
836 let d = AnyBackend::Mssql.dialect();
837 let sql = d.build_pagination("SELECT * FROM users", 2, 10);
838 let upper = sql.to_uppercase();
839 assert!(
840 upper.contains("OFFSET") || upper.contains("FETCH"),
841 "MSSQL 分页 SQL 应含 OFFSET/FETCH,实际: {}",
842 sql
843 );
844 assert!(
845 !upper.contains("LIMIT"),
846 "MSSQL 分页 SQL 不应含 LIMIT,实际: {}",
847 sql
848 );
849 }
850
851 #[test]
852 fn test_oracle_dialect_no_placeholder() {
853 let d = AnyBackend::Oracle.dialect();
854 let _ = d.db_type();
856 let _ = d.quote("col");
857 let _ = d.quote_checked("col").unwrap();
858 let _ = d.escape_string("val");
859 let _ = d.supports_returning();
860 let _ = d.build_pagination("SELECT 1", 1, 10);
861 let _ = d.json_type();
862 let _ = d.json_extract("col", "$.key");
863 let _ = d.full_text_search(&["col"], "kw");
864 let _ = d.bool_to_int("expr");
865 let _ = d.concat(&["a", "b"]);
866 }
867
868 #[test]
869 fn test_mssql_dialect_no_placeholder() {
870 let d = AnyBackend::Mssql.dialect();
871 let _ = d.db_type();
872 let _ = d.quote("col");
873 let _ = d.quote_checked("col").unwrap();
874 let _ = d.escape_string("val");
875 let _ = d.supports_returning();
876 let _ = d.build_pagination("SELECT 1", 1, 10);
877 let _ = d.json_type();
878 let _ = d.json_extract("col", "$.key");
879 let _ = d.full_text_search(&["col"], "kw");
880 let _ = d.bool_to_int("expr");
881 let _ = d.concat(&["a", "b"]);
882 }
883
884 #[tokio::test]
885 async fn test_any_pool_dialect_sqlite() {
886 let pool = AnyPool::connect("sqlite::memory:").await.unwrap();
887 let d = pool.dialect();
888 assert_eq!(d.db_type(), sz_orm_core::DbType::Sqlite);
889 }
890
891 #[test]
894 fn test_from_db_type_supported() {
895 use sz_orm_core::DbType;
896 assert_eq!(
897 AnyBackend::from_db_type(DbType::MySQL),
898 Some(AnyBackend::MySql)
899 );
900 assert_eq!(
901 AnyBackend::from_db_type(DbType::MariaDB),
902 Some(AnyBackend::MySql)
903 );
904 assert_eq!(
905 AnyBackend::from_db_type(DbType::TiDB),
906 Some(AnyBackend::MySql)
907 );
908 assert_eq!(
909 AnyBackend::from_db_type(DbType::OceanBase),
910 Some(AnyBackend::MySql)
911 );
912 assert_eq!(
913 AnyBackend::from_db_type(DbType::PostgreSQL),
914 Some(AnyBackend::Postgres)
915 );
916 assert_eq!(
917 AnyBackend::from_db_type(DbType::Kingbase),
918 Some(AnyBackend::Postgres)
919 );
920 assert_eq!(
921 AnyBackend::from_db_type(DbType::PolarDB),
922 Some(AnyBackend::Postgres)
923 );
924 assert_eq!(
925 AnyBackend::from_db_type(DbType::GaussDB),
926 Some(AnyBackend::Postgres)
927 );
928 assert_eq!(
929 AnyBackend::from_db_type(DbType::Sqlite),
930 Some(AnyBackend::Sqlite)
931 );
932 assert_eq!(
933 AnyBackend::from_db_type(DbType::Oracle),
934 Some(AnyBackend::Oracle)
935 );
936 assert_eq!(
937 AnyBackend::from_db_type(DbType::Dameng),
938 Some(AnyBackend::Oracle)
939 );
940 assert_eq!(
941 AnyBackend::from_db_type(DbType::SqlServer),
942 Some(AnyBackend::Mssql)
943 );
944 assert_eq!(
945 AnyBackend::from_db_type(DbType::Sybase),
946 Some(AnyBackend::Mssql)
947 );
948 assert_eq!(
949 AnyBackend::from_db_type(DbType::GBase),
950 Some(AnyBackend::Mssql)
951 );
952 }
953
954 #[test]
955 fn test_from_db_type_unsupported() {
956 use sz_orm_core::DbType;
957 assert_eq!(AnyBackend::from_db_type(DbType::Redis), None);
958 assert_eq!(AnyBackend::from_db_type(DbType::MongoDB), None);
959 assert_eq!(AnyBackend::from_db_type(DbType::ClickHouse), None);
960 assert_eq!(AnyBackend::from_db_type(DbType::VectorDb), None);
961 }
962
963 #[tokio::test]
964 async fn test_create_connection_sqlite() {
965 let mut conn = create_connection("sqlite::memory:").await.unwrap();
966 conn.execute("CREATE TABLE cc (id INTEGER PRIMARY KEY, v TEXT)")
967 .await
968 .unwrap();
969 conn.execute("INSERT INTO cc (v) VALUES ('ok')")
970 .await
971 .unwrap();
972 let rows = conn.query("SELECT * FROM cc").await.unwrap();
973 assert_eq!(rows.len(), 1);
974 }
975
976 #[tokio::test]
977 async fn test_create_connection_invalid_dsn() {
978 let result = create_connection("invalid://dsn").await;
979 assert!(result.is_err());
980 }
981
982 #[tokio::test]
983 async fn test_create_connection_by_type_sqlite() {
984 use sz_orm_core::DbType;
985 let mut conn = create_connection_by_type(DbType::Sqlite, "sqlite::memory:")
986 .await
987 .unwrap();
988 conn.execute("SELECT 1").await.unwrap();
989 }
990
991 #[tokio::test]
992 async fn test_create_connection_by_type_unsupported() {
993 use sz_orm_core::DbType;
994 let result = create_connection_by_type(DbType::Redis, "redis://127.0.0.1").await;
995 assert!(result.is_err());
996 }
997
998 #[test]
999 fn test_normalize_dsn_scheme_already_correct() {
1000 assert_eq!(
1001 normalize_dsn_scheme(AnyBackend::MySql, "mysql://root:pass@host/db"),
1002 "mysql://root:pass@host/db"
1003 );
1004 assert_eq!(
1005 normalize_dsn_scheme(AnyBackend::Postgres, "postgres://user@host/db"),
1006 "postgres://user@host/db"
1007 );
1008 assert_eq!(
1009 normalize_dsn_scheme(AnyBackend::Oracle, "oracle://sys:pass@host/svc"),
1010 "oracle://sys:pass@host/svc"
1011 );
1012 }
1013
1014 #[test]
1015 fn test_normalize_dsn_scheme_fix_mismatch() {
1016 let fixed = normalize_dsn_scheme(AnyBackend::MySql, "postgres://root:pass@host/db");
1017 assert!(fixed.starts_with("mysql://"));
1018 assert!(fixed.contains("root:pass@host/db"));
1019 }
1020}