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