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 db_type(&self) -> Option<DbType> {
421 Some(match self.backend {
422 AnyBackend::MySql => DbType::MySQL,
423 AnyBackend::Postgres => DbType::PostgreSQL,
424 AnyBackend::Sqlite => DbType::Sqlite,
425 AnyBackend::Oracle => DbType::Oracle,
426 AnyBackend::Mssql => DbType::SqlServer,
427 })
428 }
429
430 fn execute<'a>(
431 &'a mut self,
432 sql: &'a str,
433 ) -> Pin<Box<dyn Future<Output = Result<u64, DbError>> + Send + 'a>> {
434 self.inner.execute(sql)
435 }
436
437 fn query<'a>(
438 &'a mut self,
439 sql: &'a str,
440 ) -> Pin<Box<dyn Future<Output = Result<QueryRows, DbError>> + Send + 'a>> {
441 self.inner.query(sql)
442 }
443
444 fn begin_transaction<'a>(
445 &'a mut self,
446 ) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
447 self.inner.begin_transaction()
448 }
449
450 fn commit<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
451 self.inner.commit()
452 }
453
454 fn rollback<'a>(
455 &'a mut self,
456 ) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
457 self.inner.rollback()
458 }
459
460 fn in_transaction(&self) -> bool {
461 self.inner.in_transaction()
462 }
463
464 fn session_reset_sql(&self) -> Option<&'static str> {
465 self.inner.session_reset_sql()
466 }
467
468 fn is_connected(&self) -> bool {
469 self.inner.is_connected()
470 }
471
472 fn ping<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = bool> + Send + 'a>> {
473 self.inner.ping()
474 }
475
476 fn close<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
477 self.inner.close()
478 }
479
480 fn execute_with_params<'a>(
481 &'a mut self,
482 sql: &'a str,
483 params: &'a [Value],
484 ) -> Pin<Box<dyn Future<Output = Result<u64, DbError>> + Send + 'a>> {
485 self.inner.execute_with_params(sql, params)
486 }
487
488 fn query_with_params<'a>(
489 &'a mut self,
490 sql: &'a str,
491 params: &'a [Value],
492 ) -> Pin<Box<dyn Future<Output = Result<QueryRows, DbError>> + Send + 'a>> {
493 self.inner.query_with_params(sql, params)
494 }
495
496 fn query_values<'a>(
497 &'a mut self,
498 sql: &'a str,
499 ) -> Pin<Box<dyn Future<Output = Result<QueryValues, DbError>> + Send + 'a>> {
500 self.inner.query_values(sql)
501 }
502
503 fn query_values_with_params<'a>(
504 &'a mut self,
505 sql: &'a str,
506 params: &'a [Value],
507 ) -> Pin<Box<dyn Future<Output = Result<QueryValues, DbError>> + Send + 'a>> {
508 self.inner.query_values_with_params(sql, params)
509 }
510
511 fn query_stream<'a>(
512 &'a mut self,
513 sql: &'a str,
514 ) -> Pin<Box<dyn futures::Stream<Item = QueryStreamItem> + Send + 'a>> {
515 self.inner.query_stream(sql)
516 }
517
518 fn query_stream_cursor<'a>(
519 &'a mut self,
520 sql: &'a str,
521 batch_size: usize,
522 ) -> Pin<Box<dyn futures::Stream<Item = QueryStreamItem> + Send + 'a>> {
523 self.inner.query_stream_cursor(sql, batch_size)
524 }
525
526 fn execute_batch<'a>(
527 &'a mut self,
528 sqls: &'a [String],
529 ) -> Pin<Box<dyn Future<Output = Result<u64, DbError>> + Send + 'a>> {
530 self.inner.execute_batch(sqls)
531 }
532}
533
534#[cfg(test)]
535mod tests {
536 use super::*;
537
538 #[test]
539 fn test_any_backend_from_dsn_mysql() {
540 assert_eq!(
541 AnyBackend::from_dsn("mysql://root:pass@127.0.0.1/db").unwrap(),
542 AnyBackend::MySql
543 );
544 assert_eq!(
545 AnyBackend::from_dsn("mariadb://root:pass@127.0.0.1/db").unwrap(),
546 AnyBackend::MySql
547 );
548 }
549
550 #[test]
551 fn test_any_backend_from_dsn_postgres() {
552 assert_eq!(
553 AnyBackend::from_dsn("postgres://user:pass@127.0.0.1/db").unwrap(),
554 AnyBackend::Postgres
555 );
556 assert_eq!(
557 AnyBackend::from_dsn("postgresql://user:pass@127.0.0.1/db").unwrap(),
558 AnyBackend::Postgres
559 );
560 }
561
562 #[test]
563 fn test_any_backend_from_dsn_sqlite() {
564 assert_eq!(
565 AnyBackend::from_dsn("sqlite::memory:").unwrap(),
566 AnyBackend::Sqlite
567 );
568 assert_eq!(
569 AnyBackend::from_dsn("sqlite://./test.db").unwrap(),
570 AnyBackend::Sqlite
571 );
572 }
573
574 #[test]
575 fn test_any_backend_from_dsn_oracle() {
576 assert_eq!(
577 AnyBackend::from_dsn("oracle://sys:test123@127.0.0.1:1521/freepdb1").unwrap(),
578 AnyBackend::Oracle
579 );
580 }
581
582 #[test]
583 fn test_any_backend_from_dsn_mssql() {
584 assert_eq!(
585 AnyBackend::from_dsn("mssql://sa:test123@127.0.0.1:1433/testdb").unwrap(),
586 AnyBackend::Mssql
587 );
588 }
589
590 #[test]
591 fn test_any_backend_from_dsn_sqlserver() {
592 assert_eq!(
593 AnyBackend::from_dsn("sqlserver://sa:test123@127.0.0.1:1433/testdb").unwrap(),
594 AnyBackend::Mssql
595 );
596 }
597
598 #[test]
599 fn test_any_backend_from_dsn_unknown_v22() {
600 let result = AnyBackend::from_dsn("redis://127.0.0.1");
601 assert!(result.is_err());
602 if let Err(e) = result {
603 let msg = format!("{}", e);
604 assert!(msg.contains("mysql"));
605 assert!(msg.contains("postgres"));
606 assert!(msg.contains("sqlite"));
607 assert!(msg.contains("oracle"));
608 assert!(msg.contains("mssql"));
609 }
610 }
611
612 #[test]
613 fn test_any_backend_name_v22() {
614 assert_eq!(AnyBackend::MySql.name(), "mysql");
615 assert_eq!(AnyBackend::Postgres.name(), "postgres");
616 assert_eq!(AnyBackend::Sqlite.name(), "sqlite");
617 assert_eq!(AnyBackend::Oracle.name(), "oracle");
618 assert_eq!(AnyBackend::Mssql.name(), "mssql");
619 }
620
621 #[test]
622 fn test_any_backend_equality() {
623 assert_eq!(AnyBackend::MySql, AnyBackend::MySql);
624 assert_ne!(AnyBackend::MySql, AnyBackend::Postgres);
625 assert_ne!(AnyBackend::Postgres, AnyBackend::Sqlite);
626 assert_ne!(AnyBackend::Oracle, AnyBackend::Mssql);
627 assert_ne!(AnyBackend::Oracle, AnyBackend::MySql);
628 }
629
630 #[test]
631 fn test_parse_oracle_dsn() {
632 let (user, pass, cs) =
633 parse_oracle_dsn("oracle://sys:test123@127.0.0.1:1521/freepdb1").unwrap();
634 assert_eq!(user, "sys");
635 assert_eq!(pass, "test123");
636 assert_eq!(cs, "127.0.0.1:1521/freepdb1");
637 }
638
639 #[test]
640 fn test_parse_mssql_dsn() {
641 let ado = parse_mssql_dsn("mssql://sa:test123@127.0.0.1:1433/testdb").unwrap();
642 assert!(ado.contains("Server=127.0.0.1,1433"));
643 assert!(ado.contains("Database=testdb"));
644 assert!(ado.contains("User Id=sa"));
645 assert!(ado.contains("Password=test123"));
646 }
647
648 #[test]
649 fn test_parse_mssql_dsn_sqlserver_scheme() {
650 let ado = parse_mssql_dsn("sqlserver://sa:pass@localhost/testdb").unwrap();
651 assert!(ado.contains("Server=localhost,1433"));
652 assert!(ado.contains("Database=testdb"));
653 }
654
655 #[tokio::test]
658 async fn test_any_pool_sqlite_connect_and_query() {
659 let pool = AnyPool::connect("sqlite::memory:").await.unwrap();
660 assert_eq!(pool.backend(), AnyBackend::Sqlite);
661
662 let mut conn = pool.create().await.unwrap();
663 assert_eq!(conn.backend(), AnyBackend::Sqlite);
664
665 conn.execute("CREATE TABLE test_any (id INTEGER PRIMARY KEY, name TEXT)")
667 .await
668 .unwrap();
669 conn.execute("INSERT INTO test_any (name) VALUES ('Alice')")
670 .await
671 .unwrap();
672 conn.execute("INSERT INTO test_any (name) VALUES ('Bob')")
673 .await
674 .unwrap();
675
676 let rows = conn
678 .query("SELECT * FROM test_any ORDER BY id")
679 .await
680 .unwrap();
681 assert_eq!(rows.len(), 2);
682 assert_eq!(rows[0].get("name").and_then(|v| v.as_str()), Some("Alice"));
683 assert_eq!(rows[1].get("name").and_then(|v| v.as_str()), Some("Bob"));
684 }
685
686 #[tokio::test]
687 async fn test_any_pool_sqlite_transaction_commit() {
688 let pool = AnyPool::connect("sqlite::memory:").await.unwrap();
689 let mut conn = pool.create().await.unwrap();
690
691 conn.execute("CREATE TABLE tx_test (id INTEGER PRIMARY KEY, val INTEGER)")
692 .await
693 .unwrap();
694
695 conn.begin_transaction().await.unwrap();
697 conn.execute("INSERT INTO tx_test (val) VALUES (1)")
698 .await
699 .unwrap();
700 conn.execute("INSERT INTO tx_test (val) VALUES (2)")
701 .await
702 .unwrap();
703 conn.commit().await.unwrap();
704
705 let rows = conn.query("SELECT * FROM tx_test").await.unwrap();
706 assert_eq!(rows.len(), 2);
707 }
708
709 #[tokio::test]
710 async fn test_any_pool_sqlite_transaction_rollback() {
711 let pool = AnyPool::connect("sqlite::memory:").await.unwrap();
712 let mut conn = pool.create().await.unwrap();
713
714 conn.execute("CREATE TABLE tx_rb (id INTEGER PRIMARY KEY, val INTEGER)")
715 .await
716 .unwrap();
717
718 conn.begin_transaction().await.unwrap();
719 conn.execute("INSERT INTO tx_rb (val) VALUES (1)")
720 .await
721 .unwrap();
722 conn.rollback().await.unwrap();
723
724 let rows = conn.query("SELECT * FROM tx_rb").await.unwrap();
725 assert_eq!(rows.len(), 0);
726 }
727
728 #[tokio::test]
729 async fn test_any_connection_in_transaction_state_tracking() {
730 let pool = AnyPool::connect("sqlite::memory:").await.unwrap();
731 let mut conn = pool.create().await.unwrap();
732
733 assert!(!conn.in_transaction(), "新建连接不应处于事务中");
734
735 conn.begin_transaction().await.unwrap();
736 assert!(conn.in_transaction(), "begin 后应处于事务中");
737
738 conn.execute("CREATE TABLE tx_track (id INTEGER PRIMARY KEY)")
739 .await
740 .unwrap();
741 assert!(conn.in_transaction(), "事务内执行 SQL 后仍应处于事务中");
742
743 conn.commit().await.unwrap();
744 assert!(!conn.in_transaction(), "commit 后不应处于事务中");
745
746 conn.begin_transaction().await.unwrap();
747 assert!(conn.in_transaction());
748 conn.rollback().await.unwrap();
749 assert!(!conn.in_transaction(), "rollback 后不应处于事务中");
750 }
751
752 #[tokio::test]
753 async fn test_any_connection_in_transaction_leak_detection() {
754 let pool = AnyPool::connect("sqlite::memory:").await.unwrap();
755 let mut conn = pool.create().await.unwrap();
756
757 conn.execute("CREATE TABLE tx_leak (id INTEGER PRIMARY KEY)")
758 .await
759 .unwrap();
760
761 conn.begin_transaction().await.unwrap();
762 conn.execute("INSERT INTO tx_leak (id) VALUES (1)")
763 .await
764 .unwrap();
765
766 assert!(
767 conn.in_transaction(),
768 "未提交事务应被检测到(泄漏防护前提)"
769 );
770
771 conn.rollback().await.unwrap();
772 assert!(!conn.in_transaction());
773 }
774
775 #[tokio::test]
776 async fn test_any_pool_sqlite_ping() {
777 let pool = AnyPool::connect("sqlite::memory:").await.unwrap();
778 let mut conn = pool.create().await.unwrap();
779 let ok = conn.ping().await;
780 assert!(ok);
781 assert!(conn.is_connected());
782 }
783
784 #[tokio::test]
785 async fn test_any_pool_invalid_dsn() {
786 let result = AnyPool::connect("invalid://dsn").await;
787 assert!(result.is_err());
788 }
789
790 #[tokio::test]
791 async fn test_any_pool_sqlite_count_query() {
792 let pool = AnyPool::connect("sqlite::memory:").await.unwrap();
793 let mut conn = pool.create().await.unwrap();
794
795 conn.execute("CREATE TABLE cnt (id INTEGER PRIMARY KEY)")
796 .await
797 .unwrap();
798 for i in 1..=5 {
799 conn.execute(&format!("INSERT INTO cnt (id) VALUES ({})", i))
800 .await
801 .unwrap();
802 }
803
804 let rows = conn.query("SELECT * FROM cnt").await.unwrap();
806 assert_eq!(rows.len(), 5);
807 }
808
809 #[test]
812 fn test_any_backend_dialect_mapping() {
813 use sz_orm_core::DbType;
814
815 let mysql_d = AnyBackend::MySql.dialect();
816 assert_eq!(mysql_d.db_type(), DbType::MySQL);
817
818 let pg_d = AnyBackend::Postgres.dialect();
819 assert_eq!(pg_d.db_type(), DbType::PostgreSQL);
820
821 let sqlite_d = AnyBackend::Sqlite.dialect();
822 assert_eq!(sqlite_d.db_type(), DbType::Sqlite);
823
824 let oracle_d = AnyBackend::Oracle.dialect();
825 assert_eq!(oracle_d.db_type(), DbType::Oracle);
826
827 let mssql_d = AnyBackend::Mssql.dialect();
828 assert_eq!(mssql_d.db_type(), DbType::SqlServer);
829 }
830
831 #[test]
832 fn test_oracle_dialect_pagination() {
833 let d = AnyBackend::Oracle.dialect();
834 let sql = d.build_pagination("SELECT * FROM users", 2, 10);
835 let upper = sql.to_uppercase();
836 assert!(
837 upper.contains("OFFSET") || upper.contains("FETCH") || upper.contains("ROWNUM"),
838 "Oracle 分页 SQL 应含 OFFSET/FETCH/ROWNUM,实际: {}",
839 sql
840 );
841 assert!(
842 !upper.contains("LIMIT"),
843 "Oracle 分页 SQL 不应含 LIMIT,实际: {}",
844 sql
845 );
846 }
847
848 #[test]
849 fn test_mssql_dialect_pagination() {
850 let d = AnyBackend::Mssql.dialect();
851 let sql = d.build_pagination("SELECT * FROM users", 2, 10);
852 let upper = sql.to_uppercase();
853 assert!(
854 upper.contains("OFFSET") || upper.contains("FETCH"),
855 "MSSQL 分页 SQL 应含 OFFSET/FETCH,实际: {}",
856 sql
857 );
858 assert!(
859 !upper.contains("LIMIT"),
860 "MSSQL 分页 SQL 不应含 LIMIT,实际: {}",
861 sql
862 );
863 }
864
865 #[test]
866 fn test_oracle_dialect_no_placeholder() {
867 let d = AnyBackend::Oracle.dialect();
868 let _ = d.db_type();
870 let _ = d.quote("col");
871 let _ = d.quote_checked("col").unwrap();
872 let _ = d.escape_string("val");
873 let _ = d.supports_returning();
874 let _ = d.build_pagination("SELECT 1", 1, 10);
875 let _ = d.json_type();
876 let _ = d.json_extract("col", "$.key");
877 let _ = d.full_text_search(&["col"], "kw");
878 let _ = d.bool_to_int("expr");
879 let _ = d.concat(&["a", "b"]);
880 }
881
882 #[test]
883 fn test_mssql_dialect_no_placeholder() {
884 let d = AnyBackend::Mssql.dialect();
885 let _ = d.db_type();
886 let _ = d.quote("col");
887 let _ = d.quote_checked("col").unwrap();
888 let _ = d.escape_string("val");
889 let _ = d.supports_returning();
890 let _ = d.build_pagination("SELECT 1", 1, 10);
891 let _ = d.json_type();
892 let _ = d.json_extract("col", "$.key");
893 let _ = d.full_text_search(&["col"], "kw");
894 let _ = d.bool_to_int("expr");
895 let _ = d.concat(&["a", "b"]);
896 }
897
898 #[tokio::test]
899 async fn test_any_pool_dialect_sqlite() {
900 let pool = AnyPool::connect("sqlite::memory:").await.unwrap();
901 let d = pool.dialect();
902 assert_eq!(d.db_type(), sz_orm_core::DbType::Sqlite);
903 }
904
905 #[test]
908 fn test_from_db_type_supported() {
909 use sz_orm_core::DbType;
910 assert_eq!(
911 AnyBackend::from_db_type(DbType::MySQL),
912 Some(AnyBackend::MySql)
913 );
914 assert_eq!(
915 AnyBackend::from_db_type(DbType::MariaDB),
916 Some(AnyBackend::MySql)
917 );
918 assert_eq!(
919 AnyBackend::from_db_type(DbType::TiDB),
920 Some(AnyBackend::MySql)
921 );
922 assert_eq!(
923 AnyBackend::from_db_type(DbType::OceanBase),
924 Some(AnyBackend::MySql)
925 );
926 assert_eq!(
927 AnyBackend::from_db_type(DbType::PostgreSQL),
928 Some(AnyBackend::Postgres)
929 );
930 assert_eq!(
931 AnyBackend::from_db_type(DbType::Kingbase),
932 Some(AnyBackend::Postgres)
933 );
934 assert_eq!(
935 AnyBackend::from_db_type(DbType::PolarDB),
936 Some(AnyBackend::Postgres)
937 );
938 assert_eq!(
939 AnyBackend::from_db_type(DbType::GaussDB),
940 Some(AnyBackend::Postgres)
941 );
942 assert_eq!(
943 AnyBackend::from_db_type(DbType::Sqlite),
944 Some(AnyBackend::Sqlite)
945 );
946 assert_eq!(
947 AnyBackend::from_db_type(DbType::Oracle),
948 Some(AnyBackend::Oracle)
949 );
950 assert_eq!(
951 AnyBackend::from_db_type(DbType::Dameng),
952 Some(AnyBackend::Oracle)
953 );
954 assert_eq!(
955 AnyBackend::from_db_type(DbType::SqlServer),
956 Some(AnyBackend::Mssql)
957 );
958 assert_eq!(
959 AnyBackend::from_db_type(DbType::Sybase),
960 Some(AnyBackend::Mssql)
961 );
962 assert_eq!(
963 AnyBackend::from_db_type(DbType::GBase),
964 Some(AnyBackend::Mssql)
965 );
966 }
967
968 #[test]
969 fn test_from_db_type_unsupported() {
970 use sz_orm_core::DbType;
971 assert_eq!(AnyBackend::from_db_type(DbType::Redis), None);
972 assert_eq!(AnyBackend::from_db_type(DbType::MongoDB), None);
973 assert_eq!(AnyBackend::from_db_type(DbType::ClickHouse), None);
974 assert_eq!(AnyBackend::from_db_type(DbType::VectorDb), None);
975 }
976
977 #[tokio::test]
978 async fn test_create_connection_sqlite() {
979 let mut conn = create_connection("sqlite::memory:").await.unwrap();
980 conn.execute("CREATE TABLE cc (id INTEGER PRIMARY KEY, v TEXT)")
981 .await
982 .unwrap();
983 conn.execute("INSERT INTO cc (v) VALUES ('ok')")
984 .await
985 .unwrap();
986 let rows = conn.query("SELECT * FROM cc").await.unwrap();
987 assert_eq!(rows.len(), 1);
988 }
989
990 #[tokio::test]
991 async fn test_create_connection_invalid_dsn() {
992 let result = create_connection("invalid://dsn").await;
993 assert!(result.is_err());
994 }
995
996 #[tokio::test]
997 async fn test_create_connection_by_type_sqlite() {
998 use sz_orm_core::DbType;
999 let mut conn = create_connection_by_type(DbType::Sqlite, "sqlite::memory:")
1000 .await
1001 .unwrap();
1002 conn.execute("SELECT 1").await.unwrap();
1003 }
1004
1005 #[tokio::test]
1006 async fn test_create_connection_by_type_unsupported() {
1007 use sz_orm_core::DbType;
1008 let result = create_connection_by_type(DbType::Redis, "redis://127.0.0.1").await;
1009 assert!(result.is_err());
1010 }
1011
1012 #[test]
1013 fn test_normalize_dsn_scheme_already_correct() {
1014 assert_eq!(
1015 normalize_dsn_scheme(AnyBackend::MySql, "mysql://root:pass@host/db"),
1016 "mysql://root:pass@host/db"
1017 );
1018 assert_eq!(
1019 normalize_dsn_scheme(AnyBackend::Postgres, "postgres://user@host/db"),
1020 "postgres://user@host/db"
1021 );
1022 assert_eq!(
1023 normalize_dsn_scheme(AnyBackend::Oracle, "oracle://sys:pass@host/svc"),
1024 "oracle://sys:pass@host/svc"
1025 );
1026 }
1027
1028 #[test]
1029 fn test_normalize_dsn_scheme_fix_mismatch() {
1030 let fixed = normalize_dsn_scheme(AnyBackend::MySql, "postgres://root:pass@host/db");
1031 assert!(fixed.starts_with("mysql://"));
1032 assert!(fixed.contains("root:pass@host/db"));
1033 }
1034}