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