Skip to main content

sz_orm_core/
lib.rs

1//! # SZ-ORM — 鲜视达 ORM
2//!
3//! Rust 异步 ORM 工作空间(原型阶段),兼容 ThinkORM 风格。
4//!
5//! ## 架构概览
6//!
7//! SZ-ORM 工作空间由 **43 个成员** 组成(41 个 sz-orm-* lib + cli + examples):
8//!
9//! ### 核心引擎 (sz-orm-core)
10//! | 模块 | 功能 |
11//! |------|------|
12//! | `model` | `Model` trait — 定义表名、主键、时间戳、软删除、关联关系 |
13//! | `query` | `QueryBuilder<M>` — 链式 API,支持 SELECT/INSERT/UPDATE/DELETE/聚合/分页/JOIN |
14//! | `dialect` | 多数据库方言 — MySQL (反引号)、PostgreSQL (双引号)、SQLite、Oracle 23ai |
15//! | `pool` | 异步连接池 — 可配置大小、超时、空闲回收、健康检查、最大生命周期 |
16//! | `transaction` | ACID 事务 — 隔离级别、保存点、`TransactionManager` 多事务管理 |
17//! | `migration` | 文件迁移系统 — up/down/rollback/reset/refresh,含 `SchemaBuilder` |
18//! | `cache` | 多级缓存 — `MemoryCache`、`MultiLevelCache`,支持 TTL |
19//! | `value` | 统一值类型 — 20 种变体 (整数/浮点/字符串/字节/UUID/日期/JSON/数组) |
20//! | `db_type` | 数据库类型枚举 — MySQL、PostgreSQL、SQLite、Oracle、Redis、MongoDB 等 11 种 |
21//! | `error` | 错误类型体系 — `DbError`(20 变体)、`PoolError`、`CacheError`、`TxError` |
22//!
23//! ### 数据库适配器
24//! - **sz-orm-sqlx** — sqlx 适配器,连接真实 MySQL/PostgreSQL/SQLite/Oracle
25//! - **sz-orm-sql-validator** — SQL 校验与注入检测
26//!
27//! ### 扩展生态包 (18 个)
28//! | 包名 | 功能 |
29//! |------|------|
30//! | sz-orm-crypto | 加密原语 (AES-256-GCM, PBKDF2, HMAC-SHA256) |
31//! | sz-orm-auth | JWT 鉴权 (HS256) |
32//! | sz-orm-scheduler | Cron 定时任务调度 |
33//! | sz-orm-mqtt | MQTT 客户端 (rumqttc) |
34//! | sz-orm-websocket | WebSocket 服务端 (tokio-tungstenite) |
35//! | sz-orm-queue | 消息队列 (RabbitMQ/lapin, Kafka, NATS, ActiveMQ, RocketMQ, Pulsar) |
36//! | sz-orm-storage | 对象存储 (S3/阿里云/腾讯云/华为云/七牛/又拍云/本地) |
37//! | sz-orm-ai | AI 集成 (Embedding, RAG, Vector) |
38//! | sz-orm-grpc | gRPC 服务/客户端 |
39//! | sz-orm-graphql | GraphQL 查询支持 |
40//! | sz-orm-es | Elasticsearch 集成 |
41//! | sz-orm-tracing | 分布式追踪 |
42//! | sz-orm-logger | 日志系统 |
43//! | sz-orm-swagger | API 文档生成 |
44//! | sz-orm-masking | 数据脱敏 |
45//! | sz-orm-health | 健康检查 |
46//! | sz-orm-audit | 审计日志 |
47//! | sz-orm-batch | 批量操作 |
48//!
49//! ### 高级特性包 (6 个)
50//! | 包名 | 功能 |
51//! |------|------|
52//! | sz-orm-dtx | 分布式事务 |
53//! | sz-orm-rw | 读写分离 |
54//! | sz-orm-sharding | 分库分表 |
55//! | sz-orm-limit | 限流控制 |
56//! | sz-orm-config | 配置管理 |
57//! | sz-orm-mig | 迁移管理增强 |
58//!
59//! ### 平台支持
60//! - **sz-orm-wasm** — WebAssembly 编译目标
61//! - **sz-orm-lc** — 本地/边缘计算
62//! - **sz-orm-back** — 备份与恢复
63//!
64//! ## 快速入门
65//!
66//! ```rust,ignore
67//! use sz_orm_core::*;
68//!
69//! // 1. 定义模型
70//! #[derive(Clone)]
71//! struct User {
72//!     id: i64,
73//!     name: String,
74//!     email: String,
75//! }
76//!
77//! impl Model for User {
78//!     type PrimaryKey = i64;
79//!     fn table_name() -> &'static str { "users" }
80//!     fn pk(&self) -> Self::PrimaryKey { self.id }
81//!     fn set_pk(&mut self, pk: Self::PrimaryKey) { self.id = pk; }
82//! }
83//!
84//! // 2. 构建查询
85//! let dialect = get_dialect(DbType::MySQL).unwrap();
86//! let sql = QueryBuilder::<User>::new(dialect)
87//!     .table("users")
88//!     .select(vec!["id", "name", "email"])
89//!     .where_cond("status = 'active'")
90//!     .order_by("created_at")
91//!     .order_desc("id")
92//!     .limit(10)
93//!     .build_select();
94//!
95//! // 3. 执行前校验
96//! QueryBuilder::<User>::new(get_dialect(DbType::MySQL).unwrap())
97//!     .table("users")
98//!     .select(vec!["id", "name"])
99//!     .validate()?; // 校验 SQL 语法、注入、括号平衡
100//!
101//! // 4. 其他操作
102//! let mut data = std::collections::HashMap::new();
103//! data.insert("name".to_string(), Value::String("Alice".to_string()));
104//! data.insert("age".to_string(), Value::I64(25));
105//!
106//! let insert_sql = QueryBuilder::<User>::new(dialect)
107//!     .table("users")
108//!     .build_insert(&data);
109//!
110//! let update_sql = QueryBuilder::<User>::new(get_dialect(DbType::MySQL).unwrap())
111//!     .table("users")
112//!     .where_cond("id = 1")
113//!     .build_update(&data);
114//!
115//! let delete_sql = QueryBuilder::<User>::new(get_dialect(DbType::MySQL).unwrap())
116//!     .table("users")
117//!     .where_cond("id = 1")
118//!     .build_delete();
119//! ```
120//!
121//! ## 支持的数据库
122//!
123//! | 数据库 | 方言实现 | 真实连接 | 引用方式 |
124//! |--------|---------|---------|---------|
125//! | MySQL | `MySqlDialect` (`` ` `` 反引号) | sz-orm-sqlx | ✅ |
126//! | PostgreSQL | `PostgreSqlDialect` (`"` 双引号) | sz-orm-sqlx | ✅ |
127//! | SQLite 3.35+ | `SqliteDialect` (`"` 双引号) | sz-orm-sqlx | ✅ |
128//! | Oracle 23ai | `OracleDialect` (类型自动映射) | sz-orm-sqlx | ✅ |
129//!
130//! 通过 `get_dialect(DbType::MySQL)` 获取方言实例。每个方言处理:
131//! - 标识符引用风格
132//! - 字符串转义规则
133//! - 分页语法 (LIMIT/OFFSET vs OFFSET/FETCH)
134//! - JSON 提取函数 (JSON_EXTRACT vs #>> vs json_extract vs JSON_VALUE)
135//! - 全文搜索 (MATCH AGAINST vs to_tsvector vs CONTAINS)
136//! - 布尔转整数 (IF/CASE)
137//! - 自增关键字 (AUTO_INCREMENT/GENERATED BY DEFAULT AS IDENTITY)
138//!
139//! ## 核心功能详解
140//!
141//! ### QueryBuilder API
142//!
143//! 所有查询方法返回 `Self`,支持链式调用:
144//!
145//! ```rust,ignore
146//! // 基础查询
147//! QueryBuilder::<M>::new(dialect)
148//!     .table("users")
149//!     .select(vec!["id", "name"])
150//!     .where_cond("status = 'active'")    // AND
151//!     .or_where("role = 'admin'")         // OR
152//!     .where_in("id", vec![Value::I64(1), Value::I64(2)])
153//!     .where_between("age", Value::I64(18), Value::I64(30))
154//!     .where_null("deleted_at")
155//!     .order_by("created_at")
156//!     .order_desc("id")
157//!     .group_by("status")
158//!     .having("COUNT(*) > 5")
159//!     .limit(20)
160//!     .offset(40)
161//!     .page(3, 20)                       // page=3, page_size=20
162//!     .join_inner("posts", "users.id", "posts.user_id")
163//!     .join_left("profiles", "users.id", "profiles.user_id")
164//!     .build_select();
165//!
166//! // 聚合函数
167//! builder.build_count();    // SELECT COUNT(*)
168//! builder.build_exists();   // SELECT EXISTS(...)
169//! builder.build_max("score");
170//! builder.build_min("price");
171//! builder.build_sum("amount");
172//! builder.build_avg("value");
173//! ```
174//!
175//! ### SQL 校验
176//!
177//! ```rust,ignore
178//! // 编译时 + 运行时双重校验
179//! builder.validate()?;              // 校验 SELECT
180//! builder.validate_insert(&data)?;  // 校验 INSERT(含空数据检测)
181//! builder.validate_update(&data)?;  // 校验 UPDATE(含空数据检测)
182//! builder.validate_delete()?;       // 校验 DELETE
183//!
184//! // 校验内容包括:SQL 语法、注入检测、括号平衡、
185//! // 表名/列名合法性、JOIN 列名校验
186//! ```
187//!
188//! ### Model Trait
189//!
190//! ```rust,ignore
191//! pub trait Model: Send + Sync + Sized + 'static {
192//!     type PrimaryKey: Send + Sync + Debug + Display + Clone + Default;
193//!
194//!     fn table_name() -> &'static str;          // 表名(必需)
195//!     fn pk_name() -> &'static str { "id" }     // 主键列名
196//!     fn pk(&self) -> Self::PrimaryKey;         // 获取主键值
197//!     fn set_pk(&mut self, pk: Self::PrimaryKey); // 设置主键值
198//!     fn foreign_key(relation: &str) -> String; // 外键命名 "user_id"
199//!     fn timestamp_fields() -> Option<TimestampFields>; // 自动时间戳
200//!     fn soft_delete_field() -> Option<&'static str>;   // 软删除字段
201//! }
202//!
203//! // ModelExt 扩展
204//! pub trait ModelExt: Model {
205//!     fn columns() -> Vec<&'static str>;     // 所有列
206//!     fn fillable() -> Vec<&'static str>;    // 可填充列
207//!     fn guarded() -> Vec<&'static str>;     // 保护列(默认含主键)
208//!     fn hidden() -> Vec<&'static str>;      // 隐藏列(不序列化)
209//!     fn relations() -> HashMap<&str, Relation>; // 关联关系
210//!     fn fill(&mut self, data: HashMap<String, Value>); // 批量赋值
211//!     fn to_json(&self) -> serde_json::Value; // 序列化
212//! }
213//!
214//! // 四种关联关系
215//! // BelongsTo   — 多对一(Order → User)
216//! // HasMany     — 一对多(User → Orders)
217//! // HasOne      — 一对一(User → Profile)
218//! // BelongsToMany — 多对多(User ↔ Role,通过中间表)
219//! ```
220//!
221//! ### 连接池
222//!
223//! ```rust,ignore
224//! // 通过 Builder 配置
225//! let config = PoolConfigBuilder::new()
226//!     .max_size(100)       // 最大连接数
227//!     .min_idle(10)        // 最小空闲连接
228//!     .acquire_timeout(30) // 获取超时(秒)
229//!     .idle_timeout(600)   // 空闲超时(秒)
230//!     .max_lifetime(1800)  // 最大生命周期(秒)
231//!     .build()?;
232//!
233//! let pool = Pool::new(config, factory)?;
234//! let conn = pool.acquire().await?;  // 获取连接(带超时)
235//! pool.release(conn).await;         // 归还连接
236//! pool.status().await;               // PoolStatus { idle, active, max, min }
237//! pool.reap_idle().await;           // 回收空闲连接
238//! pool.close_all().await;           // 关闭所有连接
239//! ```
240//!
241//! ### 事务
242//!
243//! ```rust,ignore
244//! // 事务选项
245//! let opts = TransactOptions::default()
246//!     .with_isolation(IsolationLevel::Serializable)
247//!     .read_only()
248//!     .with_timeout(Duration::from_secs(30));
249//!
250//! let mut tx = Transaction::new(conn, opts);
251//! tx.execute("INSERT INTO users VALUES (1)").await?;
252//! tx.query("SELECT * FROM users").await?;
253//!
254//! // 保存点(嵌套事务)
255//! let sp = tx.savepoint().await?;         // SAVEPOINT sp_N
256//! tx.rollback_to_savepoint(&sp).await?;   // ROLLBACK TO SAVEPOINT sp_N
257//! tx.release_savepoint(&sp).await?;       // RELEASE SAVEPOINT sp_N
258//!
259//! tx.commit().await?;
260//! // tx.rollback().await?;
261//!
262//! // TransactionManager:管理多个命名事务
263//! let mgr = TransactionManager::new();
264//! mgr.begin("tx1", conn, opts).await?;
265//! mgr.commit("tx1").await?;
266//! mgr.list().await;        // ["tx1"]
267//! mgr.state("tx1").await;  // Some(TransactionState::Committed)
268//! ```
269//!
270//! ### 迁移系统
271//!
272//! ```rust,ignore
273//! // 文件命名:<version>_<name>_up.sql / <version>_<name>_down.sql
274//! // 示例:001_create_users_up.sql, 001_create_users_down.sql
275//!
276//! let resolver = FileMigrationResolver::new(PathBuf::from("./migrations"));
277//! let migrations = resolver.resolve(DbType::MySQL)?;
278//!
279//! let mut migrator = Migrator::new(MigrationContext::default())
280//!     .add_migrations(migrations);
281//!
282//! migrator.migrate().await?;                     // 执行所有待迁移
283//! migrator.up(Some("003")).await?;               // 执行到指定版本
284//! migrator.down(Some("001")).await?;             // 回滚到指定版本
285//! migrator.rollback("002").await?;               // 回滚单个迁移
286//! migrator.reset().await?;                       // 全部回滚 + 重新执行
287//! migrator.refresh().await?;                     // 同 reset
288//! migrator.progress();                            // MigrationProgress { total, applied, pending }
289//!
290//! // SchemaBuilder:程序化建表
291//! let sql = SchemaBuilder::new("users")
292//!     .add_column(ColumnDef::new("id", "INT").not_null().auto_increment())
293//!     .add_column(ColumnDef::new("name", "VARCHAR").length(255).not_null())
294//!     .add_index(IndexDef::new("idx_name", vec!["name"]).unique())
295//!     .add_foreign_key(
296//!         ForeignKeyDef::new("fk_role", "role_id", "roles", "id")
297//!             .on_delete("CASCADE")
298//!     )
299//!     .build(DbType::MySQL);
300//! ```
301//!
302//! ### 值类型 (Value)
303//!
304//! ```rust,ignore
305//! // 20 种变体,覆盖所有数据库类型
306//! Value::Null | Bool(bool) | I8..I64 | U8..U64 | F32 | F64
307//! | String(String) | Bytes(Vec<u8>) | Uuid(String) | Date(String)
308//! | DateTime(String) | Time(String) | Json(String) | Array(Vec<Value>)
309//!
310//! // 类型转换
311//! value.as_str()    // Option<&str>
312//! value.as_i64()    // Option<i64>(支持 F32/F64/Bool/String→i64 转换)
313//! value.as_f64()    // Option<f64>
314//! value.as_bool()   // Option<bool>(支持 "true"/"1"/"yes"/"on" 等)
315//! value.as_bytes()  // Option<&[u8]>
316//! value.to_param()  // Cow<str> — SQL 参数格式
317//!
318//! // From 实现
319//! let v: Value = 42i64.into();
320//! let v: Value = "hello".into();
321//! let v: Value = vec![1u8, 2u8].into();
322//! ```
323//!
324//! ## 错误处理
325//!
326//! 统一错误类型体系,每种错误携带唯一错误码:
327//!
328//! ```rust,ignore
329//! // DbError — 20 种变体,错误码 DB001-DB020
330//! DbError::QueryError("...")
331//! DbError::ConnectionRefused("...")
332//! DbError::ConnectionTimeout("...")
333//! DbError::NotFound("...")
334//! DbError::ConstraintViolation("...")
335//! // ... 等
336//!
337//! // PoolError — 6 种变体,错误码 PL001-PL006
338//! PoolError::Exhausted | Timeout | AlreadyAcquired | InvalidConfig | ...
339//!
340//! // CacheError — 6 种变体,错误码 CH001-CH006
341//! // TxError — 6 种变体(NotStarted, CommitFailed, SavepointError 等)
342//!
343//! // 便捷方法
344//! DbError::query("test failed")        // 创建查询错误
345//! DbError::connection("timeout")       // 创建连接错误
346//! DbError::not_found("user #42")       // 创建未找到错误
347//! err.is_retryable()                   // 是否可重试
348//! err.error_code()                     // "DB001"
349//! ```
350//!
351//! ## 验证方法
352//!
353//! SZ-ORM 通过 **7 线验证体系** 保证质量:
354//!
355//! | 验证方法 | 描述 | 测试文件 |
356//! |---------|------|---------|
357//! | **TDD** | 核心模块 115+ 单元测试 | `core.rs` |
358//! | **集成** | 真实 MySQL/PG/SQLite/Oracle 端到端 | `integration_mysql.rs`, `integration_pg.rs`, `integration_sqlite.rs` |
359//! | **Jepsen** | 29 并发正确性测试 + 10 真实 DB Jepsen | `jepsen.rs`, `real_db_jepsen.rs` |
360//! | **Fuzz** | 11 边界/边缘案例发现 | `fuzz.rs` |
361//! | **Stress** | 77 性能基准测试 | `stress.rs`, `core_bench.rs` |
362//! | **Chaos** | 16 故障鲁棒性测试 | `chaos.rs` |
363//! | **Formal** | 14 形式化验证不变量 | `formal.rs` |
364//!
365//! **总计:1,723 测试**(1,317 `#[test]` + 406 `#[tokio::test]`;部分需真实服务)
366//!
367//! ## 类型别名与常量
368//!
369//! ```rust,ignore
370//! // 类型别名
371//! pub type Shared<T> = Arc<T>;
372//! pub type Boxed<T> = Box<T>;
373//! pub type DbResult<T> = Result<T, DbError>;
374//! pub type PoolResult<T> = Result<T, PoolError>;
375//! pub type CacheResult<T> = Result<T, CacheError>;
376//! pub type TxResult<T> = Result<T, TxError>;
377//!
378//! // 默认常量
379//! pub const DEFAULT_BATCH_SIZE: usize = 1000;
380//! pub const DEFAULT_ACQUIRE_TIMEOUT: u64 = 30;   // 秒
381//! pub const DEFAULT_IDLE_TIMEOUT: u64 = 600;      // 秒
382//! pub const DEFAULT_MAX_LIFETIME: u64 = 1800;     // 秒
383//! pub const DEFAULT_MIN_IDLE: u32 = 5;
384//! pub const DEFAULT_MAX_SIZE: u32 = 100;
385//! ```
386//!
387//! ## 导出清单
388//!
389//! `use sz_orm_core::*;` 将导入以下模块的全部公共符号:
390//!
391//! - `async_trait` (重导出)、`bytes::Bytes`、`chrono::{DateTime, Utc}`、`serde::{Deserialize, Serialize}`
392//! - `cache::*` — `Cache`, `MemoryCache`, `MultiLevelCache`, `CacheStats`
393//! - `db_type::*` — `DbType` 枚举 (11 种数据库)
394//! - `dialect::*` — `Dialect`, `MySqlDialect`, `PostgreSqlDialect`, `SqliteDialect`, `OracleDialect`, `get_dialect()`
395//! - `error::*` — `DbError`, `PoolError`, `CacheError`, `TxError`
396//! - `migration::*` — `Migration`, `Migrator`, `SchemaBuilder`, `ColumnDef`, `IndexDef`, `ForeignKeyDef`
397//! - `model::*` — `Model`, `ModelExt`, `Relation`, `BelongsTo`, `HasMany`, `HasOne`, `BelongsToMany`
398//! - `pool::*` — `Pool`, `PoolConfig`, `PoolConfigBuilder`, `Connection`, `ConnectionFactory`, `PoolStatus`
399//! - `query::*` — `QueryBuilder<M>` (链式 SQL 构造器)
400//! - `transaction::*` — `Transaction`, `TransactionManager`, `TransactOptions`, `IsolationLevel`
401//! - `value::*` — `Value` 枚举 (20 种变体)
402
403// 文档完整性:docs.rs 构建文档时启用 missing_docs lint
404// 本地和 CI clippy 不触发,避免 313 个 pub API 缺文档阻塞开发
405// 待文档逐步补齐后再改为全局 #![warn(missing_docs)]
406#![cfg_attr(docsrs, warn(missing_docs))]
407
408use std::sync::Arc;
409
410/// Re-export async traits
411pub use async_trait::async_trait;
412
413/// Re-export common types
414pub use bytes::Bytes;
415pub use chrono::{DateTime, Utc};
416pub use serde::{Deserialize, Serialize};
417
418pub mod access_control;
419pub mod data_permission;
420pub mod dynamic_filter;
421pub mod dynamic_sql;
422pub mod entity_graph;
423pub mod find_with_related;
424pub mod guard;
425pub mod hooks;
426pub mod hydration_plugin;
427pub mod i18n;
428pub mod join_dsl;
429pub mod json_query;
430pub mod l2_cache;
431pub mod lambda;
432pub mod observer;
433pub mod optimistic_lock;
434pub mod phinx_migration;
435pub mod queryable;
436pub mod quick_query;
437pub mod repository;
438pub mod result_map;
439pub mod retry;
440pub mod schema_gen;
441pub mod shadow;
442pub mod sql_safety;
443pub mod type_handler;
444pub mod typed;
445pub mod typed_ast;
446
447// Re-export proc macros
448pub use sz_orm_macros::schema;
449pub use sz_orm_macros::sql_string;
450pub use sz_orm_macros::typed_query;
451
452// 核心包拆分(v1.2.1):sz-orm-core 退化为纯 re-export 层。
453// model / pool / query / migration 四个子模块已各自独立为 crate,
454// value / db_type / dialect / error 也已移入 sz-orm-model 统一定义。
455// core 通过 glob re-export 统一对外接口,
456// 保持 `sz_orm_core::{Model, QueryBuilder, Pool, Migration, Value, DbError, ...}`
457// 的原有路径不变,下游代码无需修改。
458#[allow(ambiguous_glob_reexports)]
459pub use sz_orm_migration::*;
460#[allow(ambiguous_glob_reexports)]
461pub use sz_orm_model::*;
462#[allow(ambiguous_glob_reexports)]
463pub use sz_orm_pool::*;
464#[allow(ambiguous_glob_reexports)]
465pub use sz_orm_query::*;
466
467/// Alias for `Arc<T>`
468pub type Shared<T> = Arc<T>;
469
470/// Alias for `Box<T>`
471pub type Boxed<T> = Box<T>;
472
473/// Alias for Result<T, DbError>
474pub type DbResult<T> = Result<T, DbError>;
475
476/// Result type for pool operations
477pub type PoolResult<T> = Result<T, PoolError>;
478
479/// Result type for cache operations
480pub type CacheResult<T> = Result<T, CacheError>;
481
482/// Result type for transaction operations
483pub type TxResult<T> = Result<T, TxError>;
484
485// DEFAULT_BATCH_SIZE 由 sz_orm_model 统一定义,通过 glob re-export 引入。
486
487/// Default connection timeout in seconds
488pub const DEFAULT_ACQUIRE_TIMEOUT: u64 = 30;
489
490/// Default idle timeout in seconds
491pub const DEFAULT_IDLE_TIMEOUT: u64 = 600;
492
493/// Default max lifetime in seconds
494pub const DEFAULT_MAX_LIFETIME: u64 = 1800;
495
496/// Default minimum idle connections
497pub const DEFAULT_MIN_IDLE: u32 = 5;
498
499/// Default maximum pool size
500pub const DEFAULT_MAX_SIZE: u32 = 100;
501
502#[cfg(test)]
503mod tests {
504    use super::*;
505    use std::error::Error;
506
507    #[test]
508    fn test_db_type() {
509        assert_eq!(DbType::MySQL.as_str(), "mysql");
510        assert_eq!(DbType::PostgreSQL.as_str(), "postgres");
511        assert_eq!(DbType::Sqlite.as_str(), "sqlite");
512    }
513
514    #[test]
515    fn test_value() {
516        let v = Value::Null;
517        assert!(v.is_null());
518
519        let v = Value::I64(42);
520        assert!(v.is_i64());
521
522        let v = Value::String("hello".to_string());
523        assert!(v.is_string());
524    }
525
526    #[test]
527    fn test_db_error_display() {
528        let err = DbError::QueryError("test query failed".to_string());
529        assert_eq!(format!("{}", err), "Query error: test query failed");
530
531        let err = DbError::ConnectionRefused("localhost".to_string());
532        assert_eq!(format!("{}", err), "Connection refused: localhost");
533    }
534
535    #[test]
536    fn test_db_error_source() {
537        let err = DbError::PoolError(PoolError::Timeout);
538        assert!(err.source().is_some());
539    }
540
541    #[tokio::test]
542    async fn test_async_trait_export() {
543        fn _check_send_sync<T: Send + Sync>() {}
544
545        struct TestImpl;
546        #[async_trait]
547        trait AsyncFoo: Send + Sync {
548            async fn foo(&self);
549        }
550
551        #[async_trait]
552        impl AsyncFoo for TestImpl {
553            async fn foo(&self) {}
554        }
555
556        let impl_ = TestImpl;
557        impl_.foo().await;
558        _check_send_sync::<TestImpl>();
559    }
560
561    // ---- compile-time SQL validation macro tests ----
562
563    /// Valid SQL should compile and be usable as a string
564    #[test]
565    fn test_sql_string_valid_select() {
566        let sql = sql_string!("SELECT * FROM users WHERE id = 1");
567        assert!(sql.contains("SELECT"));
568        assert!(sql.contains("FROM"));
569    }
570
571    #[test]
572    fn test_sql_string_valid_insert() {
573        let sql = sql_string!("INSERT INTO users (name) VALUES ('alice')");
574        assert!(sql.contains("INSERT"));
575    }
576
577    #[test]
578    fn test_sql_string_valid_update() {
579        let sql = sql_string!("UPDATE users SET name = 'bob' WHERE id = 1");
580        assert!(sql.contains("UPDATE"));
581    }
582
583    #[test]
584    fn test_sql_string_valid_delete() {
585        let sql = sql_string!("DELETE FROM users WHERE id = 1");
586        assert!(sql.contains("DELETE"));
587    }
588
589    #[test]
590    fn test_sql_string_valid_create() {
591        let sql = sql_string!("CREATE TABLE test (id INT PRIMARY KEY)");
592        assert!(sql.contains("CREATE"));
593    }
594
595    #[test]
596    fn test_sql_string_with_params() {
597        let sql = sql_string!("SELECT * FROM users WHERE id = ?"; params: 1);
598        assert!(sql.contains("?"));
599    }
600
601    #[test]
602    fn test_sql_string_complex_query() {
603        let sql = sql_string!(
604            "SELECT u.*, o.total FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE u.status = 'active'"
605        );
606        assert!(sql.contains("LEFT JOIN"));
607    }
608
609    #[test]
610    fn test_sql_string_nested_parens() {
611        let sql = sql_string!("SELECT * FROM (SELECT * FROM users) t");
612        assert!(sql.contains("SELECT"));
613    }
614}