Skip to main content

sz_orm_core/
lambda.rs

1//! Lambda 类型安全 Wrapper
2//!
3//! 对应文档 6.8 节改进项 38(Lambda 类型安全 Wrapper)。
4//!
5//! # 核心概念
6//!
7//! - **Column**:字段标记 trait,关联 Model 类型 M,提供字段名与表名
8//! - **`LambdaWrapper<M>`**:类型安全的查询构造器,所有字段引用都通过 `Column` 类型而非 `&str`
9//! - **define_columns!**:宏,为 Model 定义所有字段的类型安全标记
10//!
11//! # 设计灵感
12//!
13//! - MyBatis-Plus `LambdaQueryWrapper` / `LambdaUpdateWrapper`
14//! - JOOQ 类型安全 DSL
15//! - Diesel 强类型 schema
16//! - SeaORM `Column` trait
17//!
18//! # 优势
19//!
20//! 1. **编译期字段名检查**:拼写错误直接编译失败
21//! 2. **IDE 自动补全**:`UserColumns::` 后可补全所有字段
22//! 3. **重构友好**:字段重命名后所有引用处编译失败,便于定位修改
23//! 4. **表名隔离**:不同 Model 的字段不会混淆
24//!
25//! # 使用示例
26//!
27//! ```
28//! use sz_orm_core::lambda::{LambdaWrapper, Column};
29//! use sz_orm_core::define_columns;
30//! use sz_orm_core::Value;
31//!
32//! // 1. 定义 Model(示例)
33//! struct User;
34//!
35//! // 2. 为 User 定义字段标记
36//! define_columns! {
37//!     UserColumns for User table = "users" {
38//!         Id => "id",
39//!         Name => "name",
40//!         Age => "age",
41//!     }
42//! }
43//!
44//! // 3. 使用 LambdaWrapper 构造类型安全查询
45//! let mut wrapper = LambdaWrapper::<User>::new("users");
46//! wrapper
47//!     .select(UserColumns::Id)
48//!     .select(UserColumns::Name)
49//!     .eq(UserColumns::Id, Value::I64(1))
50//!     .gt(UserColumns::Age, Value::I64(18));
51//!
52//! let sql = wrapper.build_select();
53//! assert!(sql.contains("SELECT `id`, `name`"));
54//! assert!(sql.contains("FROM `users`"));
55//! assert!(sql.contains("`id` = 1"));
56//! assert!(sql.contains("`age` > 18"));
57//! ```
58
59use crate::dialect::{Dialect, MySqlDialect};
60use crate::Value;
61use std::marker::PhantomData;
62
63// ============================================================================
64// Column trait — 字段标记
65// ============================================================================
66
67/// 字段标记 trait
68///
69/// 实现此 trait 的类型作为 Model 字段的类型安全引用。
70/// 一个 `Column<M>` 实例携带:
71/// - 字段名(`name()`)
72/// - 所属表名(`table()`,从 Model 关联)
73///
74/// # 实现方式
75///
76/// 通常通过 `define_columns!` 宏自动生成实现,无需手动实现。
77pub trait Column<M>: Send + Sync + Clone {
78    /// 返回字段名
79    fn name(&self) -> &'static str;
80
81    /// 返回字段所属的表名
82    fn table(&self) -> &'static str;
83}
84
85// ============================================================================
86// WhereClause — WHERE 条件子句
87// ============================================================================
88
89/// WHERE 条件子句(内部表示)
90#[derive(Debug, Clone)]
91pub enum WhereClause {
92    /// `col = value`
93    Eq(String, Value),
94    /// `col != value`
95    Ne(String, Value),
96    /// `col > value`
97    Gt(String, Value),
98    /// `col >= value`
99    Ge(String, Value),
100    /// `col < value`
101    Lt(String, Value),
102    /// `col <= value`
103    Le(String, Value),
104    /// `col LIKE value`
105    Like(String, Value),
106    /// `col IS NULL`
107    IsNull(String),
108    /// `col IS NOT NULL`
109    IsNotNull(String),
110    /// `col IN (v1, v2, ...)`
111    In(String, Vec<Value>),
112    /// `col NOT IN (v1, v2, ...)`
113    NotIn(String, Vec<Value>),
114    /// `col BETWEEN v1 AND v2`
115    Between(String, Value, Value),
116    /// 原始 SQL(用于 OR 等复杂条件)
117    Raw(String),
118}
119
120impl WhereClause {
121    /// 渲染为 SQL 片段(不带前缀 AND/OR)
122    fn render(&self, dialect: &dyn Dialect) -> String {
123        match self {
124            // v0.2.2 修复 H-1:使用方言感知的转义
125            WhereClause::Eq(col, v) => format!(
126                "{} = {}",
127                dialect.quote(col),
128                v.to_param_with_dialect(dialect)
129            ),
130            WhereClause::Ne(col, v) => format!(
131                "{} != {}",
132                dialect.quote(col),
133                v.to_param_with_dialect(dialect)
134            ),
135            WhereClause::Gt(col, v) => format!(
136                "{} > {}",
137                dialect.quote(col),
138                v.to_param_with_dialect(dialect)
139            ),
140            WhereClause::Ge(col, v) => format!(
141                "{} >= {}",
142                dialect.quote(col),
143                v.to_param_with_dialect(dialect)
144            ),
145            WhereClause::Lt(col, v) => format!(
146                "{} < {}",
147                dialect.quote(col),
148                v.to_param_with_dialect(dialect)
149            ),
150            WhereClause::Le(col, v) => format!(
151                "{} <= {}",
152                dialect.quote(col),
153                v.to_param_with_dialect(dialect)
154            ),
155            WhereClause::Like(col, v) => format!(
156                "{} LIKE {}",
157                dialect.quote(col),
158                v.to_param_with_dialect(dialect)
159            ),
160            WhereClause::IsNull(col) => format!("{} IS NULL", dialect.quote(col)),
161            WhereClause::IsNotNull(col) => format!("{} IS NOT NULL", dialect.quote(col)),
162            WhereClause::In(col, vs) => {
163                let values: Vec<String> = vs
164                    .iter()
165                    .map(|v| v.to_param_with_dialect(dialect).to_string())
166                    .collect();
167                format!("{} IN ({})", dialect.quote(col), values.join(", "))
168            }
169            WhereClause::NotIn(col, vs) => {
170                let values: Vec<String> = vs
171                    .iter()
172                    .map(|v| v.to_param_with_dialect(dialect).to_string())
173                    .collect();
174                format!("{} NOT IN ({})", dialect.quote(col), values.join(", "))
175            }
176            WhereClause::Between(col, a, b) => format!(
177                "{} BETWEEN {} AND {}",
178                dialect.quote(col),
179                a.to_param_with_dialect(dialect),
180                b.to_param_with_dialect(dialect)
181            ),
182            WhereClause::Raw(sql) => sql.clone(),
183        }
184    }
185}
186
187// ============================================================================
188// OrderBy — 排序子句
189// ============================================================================
190
191/// 排序方向
192#[derive(Debug, Clone, Copy, PartialEq, Eq)]
193pub enum OrderDirection {
194    /// 升序
195    Asc,
196    /// 降序
197    Desc,
198}
199
200/// 排序子句
201#[derive(Debug, Clone)]
202pub struct OrderBy {
203    /// 字段名
204    pub column: String,
205    /// 排序方向
206    pub direction: OrderDirection,
207}
208
209// ============================================================================
210// LambdaWrapper — 类型安全查询构造器
211// ============================================================================
212
213/// Lambda 类型安全查询构造器
214///
215/// 泛型参数 `M` 是 Model 类型(仅用于类型隔离,不实际存储实例)。
216///
217/// # 字段引用方式
218///
219/// 与原始 `QueryBuilder` 使用 `&str` 字段名不同,`LambdaWrapper` 接受 `Column<M>` 实例,
220/// 从而在编译期检查字段拼写错误。
221///
222/// # 示例
223///
224/// ```
225/// use sz_orm_core::lambda::{LambdaWrapper, Column};
226/// use sz_orm_core::define_columns;
227/// use sz_orm_core::Value;
228///
229/// struct User;
230/// define_columns! {
231///     UserColumns for User table = "users" {
232///         Id => "id",
233///         Name => "name",
234///     }
235/// }
236///
237/// let mut w = LambdaWrapper::<User>::new("users");
238/// w.eq(UserColumns::Id, Value::I64(1));
239/// ```
240pub struct LambdaWrapper<M> {
241    /// 表名
242    table: String,
243    /// SELECT 字段列表(空表示 SELECT *)
244    selects: Vec<String>,
245    /// WHERE 条件列表(AND 连接)
246    wheres: Vec<WhereClause>,
247    /// ORDER BY 子句
248    orders: Vec<OrderBy>,
249    /// LIMIT
250    limit: Option<u64>,
251    /// OFFSET
252    offset: Option<u64>,
253    /// 数据库方言
254    dialect: Box<dyn Dialect>,
255    /// P3-2 修复:软删除配置
256    /// - `Some(column)`:启用软删除,build_select/count/exists 自动追加 `column = 0`
257    ///   build_delete 改为 `UPDATE ... SET column = 1`
258    /// - `None`:未启用软删除(默认)
259    soft_delete: Option<SoftDeleteConfig>,
260    /// P3-2 修复:多租户配置
261    /// - `Some(config)`:启用多租户过滤,build_select/count/exists/delete 自动追加 `column = tenant_id`
262    /// - `None`:未启用多租户(默认)
263    tenant: Option<TenantConfig>,
264    _marker: PhantomData<M>,
265}
266
267/// 软删除配置
268#[derive(Debug, Clone)]
269pub struct SoftDeleteConfig {
270    /// 软删除标记列名(如 "deleted"、"is_deleted"、"deleted_at")
271    pub column: String,
272    /// 未删除时的值(通常为 0 / false / NULL)
273    pub not_deleted_value: Value,
274    /// 已删除时的值(通常为 1 / true / 当前时间字符串)
275    pub deleted_value: Value,
276}
277
278/// 多租户配置
279#[derive(Debug, Clone)]
280pub struct TenantConfig {
281    /// 租户列名(如 "tenant_id")
282    pub column: String,
283    /// 当前租户 ID
284    pub tenant_id: Value,
285}
286
287impl<M> LambdaWrapper<M> {
288    /// 创建 LambdaWrapper,默认使用 MySQL 方言
289    pub fn new(table: impl Into<String>) -> Self {
290        Self {
291            table: table.into(),
292            selects: Vec::new(),
293            wheres: Vec::new(),
294            orders: Vec::new(),
295            limit: None,
296            offset: None,
297            dialect: Box::new(MySqlDialect),
298            soft_delete: None,
299            tenant: None,
300            _marker: PhantomData,
301        }
302    }
303
304    /// 创建 LambdaWrapper 并指定方言
305    pub fn with_dialect(table: impl Into<String>, dialect: Box<dyn Dialect>) -> Self {
306        Self {
307            table: table.into(),
308            selects: Vec::new(),
309            wheres: Vec::new(),
310            orders: Vec::new(),
311            limit: None,
312            offset: None,
313            dialect,
314            soft_delete: None,
315            tenant: None,
316            _marker: PhantomData,
317        }
318    }
319
320    // -------------------- P3-2 修复:软删除 / 多租户配置 --------------------
321
322    /// 启用软删除支持
323    ///
324    /// 启用后:
325    /// - `build_select` / `build_count` / `build_exists`:自动追加 `column = not_deleted_value`
326    /// - `build_delete`:改为 `UPDATE ... SET column = deleted_value`
327    ///
328    /// # 示例
329    ///
330    /// ```
331    /// use sz_orm_core::lambda::{LambdaWrapper, SoftDeleteConfig};
332    /// use sz_orm_core::Value;
333    ///
334    /// struct User;
335    /// let mut w = LambdaWrapper::<User>::new("users")
336    ///     .with_soft_delete(SoftDeleteConfig {
337    ///         column: "deleted".to_string(),
338    ///         not_deleted_value: Value::I64(0),
339    ///         deleted_value: Value::I64(1),
340    ///     });
341    /// ```
342    pub fn with_soft_delete(mut self, config: SoftDeleteConfig) -> Self {
343        // 校验列名为合法标识符
344        if crate::sql_safety::validate_identifier(&config.column, "soft_delete column").is_ok() {
345            self.soft_delete = Some(config);
346        }
347        self
348    }
349
350    /// 启用多租户过滤
351    ///
352    /// 启用后:
353    /// - `build_select` / `build_count` / `build_exists` / `build_delete`:
354    ///   自动追加 `column = tenant_id`
355    ///
356    /// # 示例
357    ///
358    /// ```
359    /// use sz_orm_core::lambda::{LambdaWrapper, TenantConfig};
360    /// use sz_orm_core::Value;
361    ///
362    /// struct User;
363    /// let mut w = LambdaWrapper::<User>::new("users")
364    ///     .with_tenant(TenantConfig {
365    ///         column: "tenant_id".to_string(),
366    ///         tenant_id: Value::I64(42),
367    ///     });
368    /// ```
369    pub fn with_tenant(mut self, config: TenantConfig) -> Self {
370        // 校验列名为合法标识符
371        if crate::sql_safety::validate_identifier(&config.column, "tenant column").is_ok() {
372            self.tenant = Some(config);
373        }
374        self
375    }
376
377    /// 收集所有隐式 WHERE 条件(软删除 + 多租户)
378    /// 返回 (conditions, 是否非空)
379    fn collect_implicit_wheres(&self) -> Vec<String> {
380        let mut implicit = Vec::new();
381        if let Some(ref sd) = self.soft_delete {
382            let col = self.dialect.quote(&sd.column);
383            let val = sd
384                .not_deleted_value
385                .to_param_with_dialect(self.dialect.as_ref());
386            implicit.push(format!("{} = {}", col, val));
387        }
388        if let Some(ref t) = self.tenant {
389            let col = self.dialect.quote(&t.column);
390            let val = t.tenant_id.to_param_with_dialect(self.dialect.as_ref());
391            implicit.push(format!("{} = {}", col, val));
392        }
393        implicit
394    }
395
396    /// 渲染 WHERE 子句(包含用户 wheres + 隐式软删除/多租户条件)
397    fn render_where_clause(&self) -> String {
398        let user_conds: Vec<String> = self
399            .wheres
400            .iter()
401            .map(|w| w.render(self.dialect.as_ref()))
402            .collect();
403        let implicit_conds = self.collect_implicit_wheres();
404        let all_conds: Vec<String> = user_conds.into_iter().chain(implicit_conds).collect();
405        if all_conds.is_empty() {
406            String::new()
407        } else {
408            // SAFETY: all_conds 来自 lambda 表达式编译期生成,字段名/值经类型系统约束,非直接用户输入
409            format!(" WHERE {}", all_conds.join(" AND "))
410        }
411    }
412
413    // -------------------- SELECT 字段 --------------------
414
415    /// 添加 SELECT 字段(类型安全)
416    ///
417    /// M-4 修复:对列名进行 `validate_identifier` 校验,防止恶意实现 `Column` trait
418    /// 注入非法标识符。
419    pub fn select<C: Column<M>>(&mut self, col: C) -> &mut Self {
420        let name = col.name();
421        // 校验列名为合法 SQL 标识符(非空、仅 ASCII 字母数字+下划线、不以数字开头)
422        // 校验失败时跳过该列(保留向后兼容,不中断调用链)
423        if crate::sql_safety::validate_identifier(name, "lambda select column").is_ok() {
424            self.selects.push(name.to_string());
425        }
426        self
427    }
428
429    /// 批量添加 SELECT 字段
430    ///
431    /// M-4 修复:同 `select`,对每个列名校验。
432    pub fn select_many<C: Column<M>>(&mut self, cols: &[C]) -> &mut Self {
433        for c in cols {
434            let name = c.name();
435            if crate::sql_safety::validate_identifier(name, "lambda select column").is_ok() {
436                self.selects.push(name.to_string());
437            }
438        }
439        self
440    }
441
442    /// SELECT *(清空已有字段选择)
443    pub fn select_all(&mut self) -> &mut Self {
444        self.selects.clear();
445        self
446    }
447
448    // -------------------- WHERE 条件 --------------------
449
450    /// `col = value`
451    pub fn eq<C: Column<M>>(&mut self, col: C, value: Value) -> &mut Self {
452        self.wheres
453            .push(WhereClause::Eq(col.name().to_string(), value));
454        self
455    }
456
457    /// `col != value`
458    pub fn ne<C: Column<M>>(&mut self, col: C, value: Value) -> &mut Self {
459        self.wheres
460            .push(WhereClause::Ne(col.name().to_string(), value));
461        self
462    }
463
464    /// `col > value`
465    pub fn gt<C: Column<M>>(&mut self, col: C, value: Value) -> &mut Self {
466        self.wheres
467            .push(WhereClause::Gt(col.name().to_string(), value));
468        self
469    }
470
471    /// `col >= value`
472    pub fn ge<C: Column<M>>(&mut self, col: C, value: Value) -> &mut Self {
473        self.wheres
474            .push(WhereClause::Ge(col.name().to_string(), value));
475        self
476    }
477
478    /// `col < value`
479    pub fn lt<C: Column<M>>(&mut self, col: C, value: Value) -> &mut Self {
480        self.wheres
481            .push(WhereClause::Lt(col.name().to_string(), value));
482        self
483    }
484
485    /// `col <= value`
486    pub fn le<C: Column<M>>(&mut self, col: C, value: Value) -> &mut Self {
487        self.wheres
488            .push(WhereClause::Le(col.name().to_string(), value));
489        self
490    }
491
492    /// `col LIKE value`
493    pub fn like<C: Column<M>>(&mut self, col: C, value: Value) -> &mut Self {
494        self.wheres
495            .push(WhereClause::Like(col.name().to_string(), value));
496        self
497    }
498
499    /// `col IS NULL`
500    pub fn is_null<C: Column<M>>(&mut self, col: C) -> &mut Self {
501        self.wheres
502            .push(WhereClause::IsNull(col.name().to_string()));
503        self
504    }
505
506    /// `col IS NOT NULL`
507    pub fn is_not_null<C: Column<M>>(&mut self, col: C) -> &mut Self {
508        self.wheres
509            .push(WhereClause::IsNotNull(col.name().to_string()));
510        self
511    }
512
513    /// `col IN (v1, v2, ...)`
514    pub fn r#in<C: Column<M>>(&mut self, col: C, values: Vec<Value>) -> &mut Self {
515        self.wheres
516            .push(WhereClause::In(col.name().to_string(), values));
517        self
518    }
519
520    /// `col NOT IN (v1, v2, ...)`
521    pub fn not_in<C: Column<M>>(&mut self, col: C, values: Vec<Value>) -> &mut Self {
522        self.wheres
523            .push(WhereClause::NotIn(col.name().to_string(), values));
524        self
525    }
526
527    /// `col BETWEEN a AND b`
528    pub fn between<C: Column<M>>(&mut self, col: C, a: Value, b: Value) -> &mut Self {
529        self.wheres
530            .push(WhereClause::Between(col.name().to_string(), a, b));
531        self
532    }
533
534    /// 追加原始 SQL WHERE 条件(用于 OR 等复杂场景)
535    ///
536    /// # 安全警告
537    ///
538    /// 此方法是 escape hatch,传入的 SQL 会**原样拼接**到最终 SQL 中。
539    /// **严禁将用户输入直接拼接**到 `sql` 参数中(会引入 SQL 注入风险)。
540    /// 若需使用用户输入,请改用 `eq` / `ne` / `lt` 等参数化方法。
541    pub fn raw_where(&mut self, sql: impl Into<String>) -> &mut Self {
542        self.wheres.push(WhereClause::Raw(sql.into()));
543        self
544    }
545
546    // -------------------- ORDER BY / LIMIT / OFFSET --------------------
547
548    /// 添加升序排序
549    pub fn order_by_asc<C: Column<M>>(&mut self, col: C) -> &mut Self {
550        self.orders.push(OrderBy {
551            column: col.name().to_string(),
552            direction: OrderDirection::Asc,
553        });
554        self
555    }
556
557    /// 添加降序排序
558    pub fn order_by_desc<C: Column<M>>(&mut self, col: C) -> &mut Self {
559        self.orders.push(OrderBy {
560            column: col.name().to_string(),
561            direction: OrderDirection::Desc,
562        });
563        self
564    }
565
566    /// 设置 LIMIT
567    pub fn limit(&mut self, n: u64) -> &mut Self {
568        self.limit = Some(n);
569        self
570    }
571
572    /// 设置 OFFSET
573    pub fn offset(&mut self, n: u64) -> &mut Self {
574        self.offset = Some(n);
575        self
576    }
577
578    /// 分页(设置 LIMIT + OFFSET)
579    ///
580    /// page 从 1 开始计数,page=1 时无 OFFSET(从第 0 条开始)。
581    pub fn page(&mut self, page: u64, page_size: u64) -> &mut Self {
582        self.limit = Some(page_size);
583        if page > 1 {
584            self.offset = Some((page - 1) * page_size);
585        } else {
586            self.offset = None;
587        }
588        self
589    }
590
591    // -------------------- SQL 生成 --------------------
592
593    /// 生成 SELECT SQL
594    ///
595    /// P3-2 修复:若启用了软删除/多租户,自动追加隐式 WHERE 条件
596    pub fn build_select(&self) -> String {
597        let quoted_table = self.dialect.quote(&self.table);
598
599        // SELECT 字段
600        let select_sql = if self.selects.is_empty() {
601            "*".to_string()
602        } else {
603            self.selects
604                .iter()
605                .map(|c| self.dialect.quote(c))
606                .collect::<Vec<_>>()
607                .join(", ")
608        };
609
610        let mut sql = format!("SELECT {} FROM {}", select_sql, quoted_table);
611
612        // WHERE(含隐式软删除/多租户条件)
613        sql.push_str(&self.render_where_clause());
614
615        // ORDER BY
616        if !self.orders.is_empty() {
617            let orders: Vec<String> = self
618                .orders
619                .iter()
620                .map(|o| {
621                    let dir = match o.direction {
622                        OrderDirection::Asc => "ASC",
623                        OrderDirection::Desc => "DESC",
624                    };
625                    format!("{} {}", self.dialect.quote(&o.column), dir)
626                })
627                .collect();
628            sql.push_str(" ORDER BY ");
629            sql.push_str(&orders.join(", "));
630        }
631
632        // LIMIT / OFFSET
633        if let Some(l) = self.limit {
634            sql.push_str(&format!(" LIMIT {}", l));
635        }
636        if let Some(o) = self.offset {
637            sql.push_str(&format!(" OFFSET {}", o));
638        }
639
640        sql
641    }
642
643    /// 生成 COUNT SQL(SELECT COUNT(*) FROM ... WHERE ...)
644    ///
645    /// P3-2 修复:若启用了软删除/多租户,自动追加隐式 WHERE 条件
646    pub fn build_count(&self) -> String {
647        let quoted_table = self.dialect.quote(&self.table);
648        let mut sql = format!("SELECT COUNT(*) FROM {}", quoted_table);
649        sql.push_str(&self.render_where_clause());
650        sql
651    }
652
653    /// 生成 EXISTS SQL(SELECT EXISTS(SELECT 1 FROM ... WHERE ...) AS exists_flag)
654    ///
655    /// P3-2 修复:若启用了软删除/多租户,自动追加隐式 WHERE 条件
656    pub fn build_exists(&self) -> String {
657        let inner = self.build_select();
658        // 把 SELECT 字段部分替换为 SELECT 1
659        let inner = if let Some(pos) = inner.find(" FROM ") {
660            format!("SELECT 1{}", &inner[pos..])
661        } else {
662            inner
663        };
664        format!("SELECT EXISTS({}) AS exists_flag", inner)
665    }
666
667    /// 生成 DELETE SQL
668    ///
669    /// P3-2 修复:
670    /// - 若启用软删除,则改为 `UPDATE ... SET column = deleted_value`
671    ///   (即软删除:仅标记,不实际删除行)
672    /// - 若启用多租户,自动追加 `column = tenant_id` 条件,防止跨租户删除
673    pub fn build_delete(&self) -> String {
674        let quoted_table = self.dialect.quote(&self.table);
675
676        // 软删除:改为 UPDATE
677        if let Some(ref sd) = self.soft_delete {
678            let col = self.dialect.quote(&sd.column);
679            let val = sd
680                .deleted_value
681                .to_param_with_dialect(self.dialect.as_ref());
682            let mut sql = format!("UPDATE {} SET {} = {}", quoted_table, col, val);
683
684            // WHERE(含用户条件 + 多租户条件;软删除条件不再追加,因为本就是 UPDATE SET)
685            let user_conds: Vec<String> = self
686                .wheres
687                .iter()
688                .map(|w| w.render(self.dialect.as_ref()))
689                .collect();
690            let mut all_conds = user_conds;
691            // 多租户过滤仍需追加
692            if let Some(ref t) = self.tenant {
693                let tcol = self.dialect.quote(&t.column);
694                let tval = t.tenant_id.to_param_with_dialect(self.dialect.as_ref());
695                all_conds.push(format!("{} = {}", tcol, tval));
696            }
697            // 同时确保只更新未删除的行
698            let not_deleted_cond = format!(
699                "{} = {}",
700                col,
701                sd.not_deleted_value
702                    .to_param_with_dialect(self.dialect.as_ref())
703            );
704            all_conds.push(not_deleted_cond);
705
706            if !all_conds.is_empty() {
707                sql.push_str(" WHERE ");
708                sql.push_str(&all_conds.join(" AND "));
709            }
710            return sql;
711        }
712
713        // 硬删除:DELETE FROM
714        let mut sql = format!("DELETE FROM {}", quoted_table);
715        sql.push_str(&self.render_where_clause());
716        sql
717    }
718
719    // -------------------- 状态查询 --------------------
720
721    /// 返回当前 WHERE 条件数量
722    pub fn where_count(&self) -> usize {
723        self.wheres.len()
724    }
725
726    /// 返回当前 SELECT 字段数量
727    pub fn select_count(&self) -> usize {
728        self.selects.len()
729    }
730
731    /// 返回表名
732    pub fn table(&self) -> &str {
733        &self.table
734    }
735
736    /// 重置所有条件(保留表名和方言)
737    pub fn reset(&mut self) -> &mut Self {
738        self.selects.clear();
739        self.wheres.clear();
740        self.orders.clear();
741        self.limit = None;
742        self.offset = None;
743        self
744    }
745}
746
747// ============================================================================
748// define_columns! 宏 — 为 Model 定义字段标记
749// ============================================================================
750
751/// 为 Model 定义一组类型安全的字段标记
752///
753/// # 示例
754///
755/// ```
756/// use sz_orm_core::define_columns;
757///
758/// struct User;
759///
760/// define_columns! {
761///     UserColumns for User table = "users" {
762///         Id => "id",
763///         Name => "name",
764///         Age => "age",
765///     }
766/// }
767/// ```
768///
769/// 生成如下代码:
770///
771/// ```rust,ignore
772/// #[derive(Clone, Copy)]
773/// pub struct UserColumns;
774///
775/// impl UserColumns {
776///     pub const Id: UserColumn = UserColumn { name: "id", table: "users" };
777///     pub const Name: UserColumn = UserColumn { name: "name", table: "users" };
778///     pub const Age: UserColumn = UserColumn { name: "age", table: "users" };
779/// }
780///
781/// #[derive(Clone, Copy)]
782/// pub struct UserColumn {
783///     pub name: &'static str,
784///     pub table: &'static str,
785/// }
786///
787/// impl Column<User> for UserColumn {
788///     fn name(&self) -> &'static str { self.name }
789///     fn table(&self) -> &'static str { self.table }
790/// }
791/// ```
792#[macro_export]
793macro_rules! define_columns {
794    (
795        $columns_struct:ident for $model:ident table = $table:literal {
796            $( $field:ident => $name:literal ),* $(,)?
797        }
798    ) => {
799        /// 字段标记类型(每个 Model 一组)
800        ///
801        /// 通过 `ModelColumns::FieldName` 引用对应字段,编译期检查拼写错误。
802        #[derive(Debug, Clone, Copy)]
803        pub struct $columns_struct {
804            /// 字段名
805            pub name: &'static str,
806            /// 表名
807            pub table: &'static str,
808        }
809
810        impl $crate::lambda::Column<$model> for $columns_struct {
811            fn name(&self) -> &'static str {
812                self.name
813            }
814
815            fn table(&self) -> &'static str {
816                self.table
817            }
818        }
819
820        impl $columns_struct {
821            // 允许 PascalCase 常量名(如 Id、Name),符合字段命名习惯
822            $(
823                #[allow(non_upper_case_globals, dead_code)]
824                pub const $field: $columns_struct = $columns_struct { name: $name, table: $table };
825            )*
826        }
827    };
828}
829
830// ============================================================================
831// 单元测试
832// ============================================================================
833
834#[cfg(test)]
835mod tests {
836    use super::*;
837    use crate::dialect::PostgreSqlDialect;
838    use crate::get_dialect;
839    use crate::DbType;
840
841    // 测试用 Model
842    struct User;
843
844    // 为 User 定义字段标记
845    define_columns! {
846        UserColumns for User table = "users" {
847            Id => "id",
848            Name => "name",
849            Age => "age",
850            Email => "email",
851        }
852    }
853
854    // 另一个 Model 用于测试表名隔离
855    struct Order;
856
857    define_columns! {
858        OrderColumns for Order table = "orders" {
859            OrderId => "order_id",
860            UserId => "user_id",
861            Total => "total",
862        }
863    }
864
865    // ===== Column trait 测试 =====
866
867    #[test]
868    fn test_column_name_and_table() {
869        assert_eq!(UserColumns::Id.name(), "id");
870        assert_eq!(UserColumns::Id.table(), "users");
871        assert_eq!(UserColumns::Name.name(), "name");
872        assert_eq!(UserColumns::Age.name(), "age");
873        assert_eq!(UserColumns::Email.name(), "email");
874    }
875
876    #[test]
877    fn test_column_for_different_models() {
878        assert_eq!(OrderColumns::OrderId.name(), "order_id");
879        assert_eq!(OrderColumns::OrderId.table(), "orders");
880        assert_eq!(OrderColumns::UserId.name(), "user_id");
881    }
882
883    // ===== LambdaWrapper 基础测试 =====
884
885    #[test]
886    fn test_new_wrapper() {
887        let w = LambdaWrapper::<User>::new("users");
888        assert_eq!(w.table(), "users");
889        assert_eq!(w.where_count(), 0);
890        assert_eq!(w.select_count(), 0);
891    }
892
893    #[test]
894    fn test_select_single() {
895        let mut w = LambdaWrapper::<User>::new("users");
896        w.select(UserColumns::Id);
897        let sql = w.build_select();
898        assert!(sql.contains("SELECT `id` FROM `users`"));
899    }
900
901    #[test]
902    fn test_select_multiple() {
903        let mut w = LambdaWrapper::<User>::new("users");
904        w.select(UserColumns::Id)
905            .select(UserColumns::Name)
906            .select(UserColumns::Age);
907        let sql = w.build_select();
908        assert!(sql.contains("`id`, `name`, `age`"));
909    }
910
911    #[test]
912    fn test_select_many() {
913        let mut w = LambdaWrapper::<User>::new("users");
914        w.select_many(&[UserColumns::Id, UserColumns::Name, UserColumns::Age]);
915        let sql = w.build_select();
916        assert!(sql.contains("`id`, `name`, `age`"));
917    }
918
919    #[test]
920    fn test_select_all_clears_selects() {
921        let mut w = LambdaWrapper::<User>::new("users");
922        w.select(UserColumns::Id);
923        assert_eq!(w.select_count(), 1);
924        w.select_all();
925        assert_eq!(w.select_count(), 0);
926        let sql = w.build_select();
927        assert!(sql.contains("SELECT * FROM"));
928    }
929
930    #[test]
931    fn test_default_select_is_star() {
932        let w = LambdaWrapper::<User>::new("users");
933        let sql = w.build_select();
934        assert!(sql.contains("SELECT * FROM `users`"));
935    }
936
937    // ===== WHERE 条件测试 =====
938
939    #[test]
940    fn test_where_eq() {
941        let mut w = LambdaWrapper::<User>::new("users");
942        w.eq(UserColumns::Id, Value::I64(1));
943        let sql = w.build_select();
944        assert!(sql.contains("WHERE `id` = 1"));
945    }
946
947    #[test]
948    fn test_where_ne() {
949        let mut w = LambdaWrapper::<User>::new("users");
950        w.ne(UserColumns::Id, Value::I64(1));
951        let sql = w.build_select();
952        assert!(sql.contains("`id` != 1"));
953    }
954
955    #[test]
956    fn test_where_gt_ge_lt_le() {
957        let mut w = LambdaWrapper::<User>::new("users");
958        w.gt(UserColumns::Age, Value::I64(18))
959            .ge(UserColumns::Age, Value::I64(20))
960            .lt(UserColumns::Age, Value::I64(65))
961            .le(UserColumns::Age, Value::I64(60));
962        let sql = w.build_select();
963        assert!(sql.contains("`age` > 18"));
964        assert!(sql.contains("`age` >= 20"));
965        assert!(sql.contains("`age` < 65"));
966        assert!(sql.contains("`age` <= 60"));
967    }
968
969    #[test]
970    fn test_where_like() {
971        let mut w = LambdaWrapper::<User>::new("users");
972        w.like(UserColumns::Name, Value::String("%alice%".to_string()));
973        let sql = w.build_select();
974        assert!(sql.contains("`name` LIKE '%alice%'"));
975    }
976
977    #[test]
978    fn test_where_is_null() {
979        let mut w = LambdaWrapper::<User>::new("users");
980        w.is_null(UserColumns::Email);
981        let sql = w.build_select();
982        assert!(sql.contains("`email` IS NULL"));
983    }
984
985    #[test]
986    fn test_where_is_not_null() {
987        let mut w = LambdaWrapper::<User>::new("users");
988        w.is_not_null(UserColumns::Email);
989        let sql = w.build_select();
990        assert!(sql.contains("`email` IS NOT NULL"));
991    }
992
993    #[test]
994    fn test_where_in() {
995        let mut w = LambdaWrapper::<User>::new("users");
996        w.r#in(
997            UserColumns::Id,
998            vec![Value::I64(1), Value::I64(2), Value::I64(3)],
999        );
1000        let sql = w.build_select();
1001        assert!(sql.contains("`id` IN (1, 2, 3)"));
1002    }
1003
1004    #[test]
1005    fn test_where_not_in() {
1006        let mut w = LambdaWrapper::<User>::new("users");
1007        w.not_in(UserColumns::Id, vec![Value::I64(1), Value::I64(2)]);
1008        let sql = w.build_select();
1009        assert!(sql.contains("`id` NOT IN (1, 2)"));
1010    }
1011
1012    #[test]
1013    fn test_where_between() {
1014        let mut w = LambdaWrapper::<User>::new("users");
1015        w.between(UserColumns::Age, Value::I64(18), Value::I64(65));
1016        let sql = w.build_select();
1017        assert!(sql.contains("`age` BETWEEN 18 AND 65"));
1018    }
1019
1020    #[test]
1021    fn test_where_multiple_anded() {
1022        let mut w = LambdaWrapper::<User>::new("users");
1023        w.eq(UserColumns::Id, Value::I64(1))
1024            .gt(UserColumns::Age, Value::I64(18))
1025            .like(UserColumns::Name, Value::String("alice%".to_string()));
1026        let sql = w.build_select();
1027        assert!(sql.contains("`id` = 1"));
1028        assert!(sql.contains("`age` > 18"));
1029        assert!(sql.contains("`name` LIKE 'alice%'"));
1030        // 所有条件用 AND 连接
1031        assert!(sql.contains(" AND "));
1032    }
1033
1034    #[test]
1035    fn test_where_raw() {
1036        let mut w = LambdaWrapper::<User>::new("users");
1037        w.raw_where("name = 'alice' OR name = 'bob'");
1038        let sql = w.build_select();
1039        assert!(sql.contains("name = 'alice' OR name = 'bob'"));
1040    }
1041
1042    // ===== ORDER BY / LIMIT / OFFSET 测试 =====
1043
1044    #[test]
1045    fn test_order_by_asc() {
1046        let mut w = LambdaWrapper::<User>::new("users");
1047        w.order_by_asc(UserColumns::Name);
1048        let sql = w.build_select();
1049        assert!(sql.contains("ORDER BY `name` ASC"));
1050    }
1051
1052    #[test]
1053    fn test_order_by_desc() {
1054        let mut w = LambdaWrapper::<User>::new("users");
1055        w.order_by_desc(UserColumns::Id);
1056        let sql = w.build_select();
1057        assert!(sql.contains("ORDER BY `id` DESC"));
1058    }
1059
1060    #[test]
1061    fn test_order_by_multiple() {
1062        let mut w = LambdaWrapper::<User>::new("users");
1063        w.order_by_asc(UserColumns::Name)
1064            .order_by_desc(UserColumns::Id);
1065        let sql = w.build_select();
1066        assert!(sql.contains("ORDER BY `name` ASC, `id` DESC"));
1067    }
1068
1069    #[test]
1070    fn test_limit() {
1071        let mut w = LambdaWrapper::<User>::new("users");
1072        w.limit(10);
1073        let sql = w.build_select();
1074        assert!(sql.contains("LIMIT 10"));
1075    }
1076
1077    #[test]
1078    fn test_offset() {
1079        let mut w = LambdaWrapper::<User>::new("users");
1080        w.limit(10).offset(20);
1081        let sql = w.build_select();
1082        assert!(sql.contains("LIMIT 10"));
1083        assert!(sql.contains("OFFSET 20"));
1084    }
1085
1086    #[test]
1087    fn test_page() {
1088        let mut w = LambdaWrapper::<User>::new("users");
1089        w.page(3, 20); // 第 3 页,每页 20 条
1090        let sql = w.build_select();
1091        assert!(sql.contains("LIMIT 20"));
1092        assert!(sql.contains("OFFSET 40")); // (3-1) * 20
1093    }
1094
1095    #[test]
1096    fn test_page_1_no_offset() {
1097        let mut w = LambdaWrapper::<User>::new("users");
1098        w.page(1, 10);
1099        let sql = w.build_select();
1100        assert!(sql.contains("LIMIT 10"));
1101        assert!(!sql.contains("OFFSET")); // 第 1 页无 OFFSET
1102    }
1103
1104    // ===== SQL 生成测试 =====
1105
1106    #[test]
1107    fn test_build_count() {
1108        let mut w = LambdaWrapper::<User>::new("users");
1109        w.gt(UserColumns::Age, Value::I64(18));
1110        let sql = w.build_count();
1111        assert!(sql.contains("SELECT COUNT(*) FROM `users`"));
1112        assert!(sql.contains("`age` > 18"));
1113        // 不应包含 ORDER BY / LIMIT
1114        assert!(!sql.contains("ORDER BY"));
1115        assert!(!sql.contains("LIMIT"));
1116    }
1117
1118    #[test]
1119    fn test_build_exists() {
1120        let mut w = LambdaWrapper::<User>::new("users");
1121        w.eq(UserColumns::Id, Value::I64(1));
1122        let sql = w.build_exists();
1123        assert!(sql.starts_with("SELECT EXISTS("));
1124        assert!(sql.contains("SELECT 1 FROM `users`"));
1125        assert!(sql.contains("`id` = 1"));
1126        assert!(sql.ends_with(") AS exists_flag"));
1127    }
1128
1129    #[test]
1130    fn test_build_delete() {
1131        let mut w = LambdaWrapper::<User>::new("users");
1132        w.eq(UserColumns::Id, Value::I64(1));
1133        let sql = w.build_delete();
1134        assert!(sql.starts_with("DELETE FROM `users`"));
1135        assert!(sql.contains("WHERE `id` = 1"));
1136    }
1137
1138    // ===== P3-2 修复:软删除 / 多租户测试 =====
1139
1140    #[test]
1141    fn test_soft_delete_select_appends_filter() {
1142        // 启用软删除后,build_select 应自动追加 `deleted = 0`
1143        let w = LambdaWrapper::<User>::new("users").with_soft_delete(SoftDeleteConfig {
1144            column: "deleted".to_string(),
1145            not_deleted_value: Value::I64(0),
1146            deleted_value: Value::I64(1),
1147        });
1148        let sql = w.build_select();
1149        assert!(
1150            sql.contains("`deleted` = 0"),
1151            "软删除 SELECT 应追加 `deleted` = 0: {}",
1152            sql
1153        );
1154    }
1155
1156    #[test]
1157    fn test_soft_delete_count_appends_filter() {
1158        // 启用软删除后,build_count 应自动追加 `deleted = 0`
1159        let w = LambdaWrapper::<User>::new("users").with_soft_delete(SoftDeleteConfig {
1160            column: "deleted".to_string(),
1161            not_deleted_value: Value::I64(0),
1162            deleted_value: Value::I64(1),
1163        });
1164        let sql = w.build_count();
1165        assert!(
1166            sql.contains("`deleted` = 0"),
1167            "软删除 COUNT 应追加 `deleted` = 0: {}",
1168            sql
1169        );
1170    }
1171
1172    #[test]
1173    fn test_soft_delete_exists_appends_filter() {
1174        // 启用软删除后,build_exists 应自动追加 `deleted = 0`
1175        let w = LambdaWrapper::<User>::new("users").with_soft_delete(SoftDeleteConfig {
1176            column: "deleted".to_string(),
1177            not_deleted_value: Value::I64(0),
1178            deleted_value: Value::I64(1),
1179        });
1180        let sql = w.build_exists();
1181        assert!(
1182            sql.contains("`deleted` = 0"),
1183            "软删除 EXISTS 应追加 `deleted` = 0: {}",
1184            sql
1185        );
1186    }
1187
1188    #[test]
1189    fn test_soft_delete_delete_becomes_update() {
1190        // 启用软删除后,build_delete 应改为 UPDATE ... SET deleted = 1
1191        let mut w = LambdaWrapper::<User>::new("users").with_soft_delete(SoftDeleteConfig {
1192            column: "deleted".to_string(),
1193            not_deleted_value: Value::I64(0),
1194            deleted_value: Value::I64(1),
1195        });
1196        w.eq(UserColumns::Id, Value::I64(42));
1197        let sql = w.build_delete();
1198        assert!(
1199            sql.starts_with("UPDATE `users` SET `deleted` = 1"),
1200            "软删除应为 UPDATE:{}",
1201            sql
1202        );
1203        assert!(sql.contains("`id` = 42"), "应保留用户 WHERE 条件:{}", sql);
1204        assert!(
1205            sql.contains("`deleted` = 0"),
1206            "应追加未删除条件防止重复删除:{}",
1207            sql
1208        );
1209        assert!(
1210            !sql.starts_with("DELETE"),
1211            "软删除不应生成 DELETE 语句:{}",
1212            sql
1213        );
1214    }
1215
1216    #[test]
1217    fn test_tenant_select_appends_filter() {
1218        // 启用多租户后,build_select 应自动追加 `tenant_id` = 42
1219        let w = LambdaWrapper::<User>::new("users").with_tenant(TenantConfig {
1220            column: "tenant_id".to_string(),
1221            tenant_id: Value::I64(42),
1222        });
1223        let sql = w.build_select();
1224        assert!(
1225            sql.contains("`tenant_id` = 42"),
1226            "多租户 SELECT 应追加 `tenant_id` = 42: {}",
1227            sql
1228        );
1229    }
1230
1231    #[test]
1232    fn test_tenant_delete_appends_filter() {
1233        // 启用多租户后,build_delete 应自动追加 `tenant_id` = 42 防止跨租户删除
1234        let mut w = LambdaWrapper::<User>::new("users").with_tenant(TenantConfig {
1235            column: "tenant_id".to_string(),
1236            tenant_id: Value::I64(42),
1237        });
1238        w.eq(UserColumns::Id, Value::I64(1));
1239        let sql = w.build_delete();
1240        assert!(
1241            sql.contains("`tenant_id` = 42"),
1242            "多租户 DELETE 应追加 `tenant_id` = 42: {}",
1243            sql
1244        );
1245    }
1246
1247    #[test]
1248    fn test_soft_delete_and_tenant_combined() {
1249        // 同时启用软删除和多租户
1250        let mut w = LambdaWrapper::<User>::new("users")
1251            .with_soft_delete(SoftDeleteConfig {
1252                column: "deleted".to_string(),
1253                not_deleted_value: Value::I64(0),
1254                deleted_value: Value::I64(1),
1255            })
1256            .with_tenant(TenantConfig {
1257                column: "tenant_id".to_string(),
1258                tenant_id: Value::I64(99),
1259            });
1260        w.eq(UserColumns::Id, Value::I64(7));
1261
1262        // SELECT 应同时包含两个隐式条件
1263        let sql = w.build_select();
1264        assert!(sql.contains("`deleted` = 0"), "应包含软删除条件: {}", sql);
1265        assert!(
1266            sql.contains("`tenant_id` = 99"),
1267            "应包含多租户条件: {}",
1268            sql
1269        );
1270        assert!(sql.contains("`id` = 7"), "应包含用户条件: {}", sql);
1271
1272        // DELETE 应改为 UPDATE,并追加所有条件
1273        let del_sql = w.build_delete();
1274        assert!(del_sql.starts_with("UPDATE `users` SET `deleted` = 1"));
1275        assert!(del_sql.contains("`tenant_id` = 99"));
1276        assert!(del_sql.contains("`deleted` = 0"));
1277        assert!(del_sql.contains("`id` = 7"));
1278    }
1279
1280    #[test]
1281    fn test_no_soft_delete_no_tenant_backward_compat() {
1282        // 未启用软删除/多租户时,行为与之前一致
1283        let mut w = LambdaWrapper::<User>::new("users");
1284        w.eq(UserColumns::Id, Value::I64(1));
1285        let sql = w.build_select();
1286        assert!(!sql.contains("deleted"));
1287        assert!(!sql.contains("tenant_id"));
1288
1289        let del_sql = w.build_delete();
1290        assert!(del_sql.starts_with("DELETE FROM"));
1291    }
1292
1293    // ===== 方言测试 =====
1294
1295    #[test]
1296    fn test_with_postgres_dialect() {
1297        let dialect: Box<dyn Dialect> = Box::new(PostgreSqlDialect);
1298        let mut w = LambdaWrapper::<User>::with_dialect("users", dialect);
1299        w.eq(UserColumns::Id, Value::I64(1));
1300        let sql = w.build_select();
1301        assert!(sql.contains("\"users\""));
1302        assert!(sql.contains("\"id\" = 1"));
1303    }
1304
1305    #[test]
1306    fn test_postgres_select() {
1307        let dialect: Box<dyn Dialect> = Box::new(PostgreSqlDialect);
1308        let mut w = LambdaWrapper::<User>::with_dialect("users", dialect);
1309        w.select(UserColumns::Id).select(UserColumns::Name);
1310        let sql = w.build_select();
1311        assert!(sql.contains("\"id\", \"name\""));
1312    }
1313
1314    // ===== 完整查询测试 =====
1315
1316    #[test]
1317    fn test_complex_query() {
1318        let mut w = LambdaWrapper::<User>::new("users");
1319        w.select(UserColumns::Id)
1320            .select(UserColumns::Name)
1321            .select(UserColumns::Age)
1322            .gt(UserColumns::Age, Value::I64(18))
1323            .like(UserColumns::Name, Value::String("a%".to_string()))
1324            .is_not_null(UserColumns::Email)
1325            .order_by_desc(UserColumns::Id)
1326            .limit(10)
1327            .offset(20);
1328
1329        let sql = w.build_select();
1330        assert!(sql.contains("SELECT `id`, `name`, `age` FROM `users`"));
1331        assert!(sql.contains("`age` > 18"));
1332        assert!(sql.contains("`name` LIKE 'a%'"));
1333        assert!(sql.contains("`email` IS NOT NULL"));
1334        assert!(sql.contains("ORDER BY `id` DESC"));
1335        assert!(sql.contains("LIMIT 10"));
1336        assert!(sql.contains("OFFSET 20"));
1337    }
1338
1339    #[test]
1340    fn test_reset_clears_all() {
1341        let mut w = LambdaWrapper::<User>::new("users");
1342        w.select(UserColumns::Id)
1343            .eq(UserColumns::Id, Value::I64(1))
1344            .order_by_asc(UserColumns::Name)
1345            .limit(10);
1346
1347        w.reset();
1348        assert_eq!(w.select_count(), 0);
1349        assert_eq!(w.where_count(), 0);
1350        let sql = w.build_select();
1351        assert!(sql.contains("SELECT * FROM `users`"));
1352        assert!(!sql.contains("WHERE"));
1353        assert!(!sql.contains("ORDER BY"));
1354        assert!(!sql.contains("LIMIT"));
1355    }
1356
1357    // ===== 跨 Model 类型隔离测试 =====
1358
1359    #[test]
1360    fn test_different_models_dont_share_columns() {
1361        let mut user_w = LambdaWrapper::<User>::new("users");
1362        user_w.eq(UserColumns::Id, Value::I64(1));
1363
1364        let mut order_w = LambdaWrapper::<Order>::new("orders");
1365        order_w.eq(OrderColumns::OrderId, Value::I64(100));
1366
1367        let user_sql = user_w.build_select();
1368        let order_sql = order_w.build_select();
1369
1370        assert!(user_sql.contains("`users`"));
1371        assert!(user_sql.contains("`id` = 1"));
1372        assert!(order_sql.contains("`orders`"));
1373        assert!(order_sql.contains("`order_id` = 100"));
1374    }
1375
1376    // ===== 编译期类型安全演示(运行时验证)=====
1377
1378    #[test]
1379    fn test_type_safety_compile_time_check() {
1380        // 以下代码若取消注释将无法编译:
1381        // let mut w = LambdaWrapper::<User>::new("users");
1382        // w.eq(OrderColumns::OrderId, Value::I64(1)); // OrderColumns 不能用于 User 的 wrapper
1383
1384        // 这里通过正常调用验证类型隔离工作正常
1385        let mut w = LambdaWrapper::<User>::new("users");
1386        w.eq(UserColumns::Id, Value::I64(1));
1387        assert_eq!(w.where_count(), 1);
1388    }
1389
1390    #[test]
1391    fn test_string_value_escape() {
1392        // v0.2.2 修复 H-1:默认使用 MySqlDialect,单引号转义为 \'
1393        let mut w = LambdaWrapper::<User>::new("users");
1394        w.eq(UserColumns::Name, Value::String("O'Brien".to_string()));
1395        let sql = w.build_select();
1396        // MySQL 方言下字符串中的单引号应被转义为 \'
1397        assert!(sql.contains("'O\\'Brien'"));
1398
1399        // 使用 PostgreSQL 方言时,单引号转义为 ''
1400        let pg_dialect: Box<dyn Dialect> = get_dialect(DbType::PostgreSQL).unwrap();
1401        let mut w_pg = LambdaWrapper::<User>::with_dialect("users", pg_dialect);
1402        w_pg.eq(UserColumns::Name, Value::String("O'Brien".to_string()));
1403        let sql_pg = w_pg.build_select();
1404        assert!(sql_pg.contains("'O''Brien'"));
1405    }
1406
1407    // ===== 使用真实方言验证集成 =====
1408
1409    #[test]
1410    fn test_with_real_mysql_dialect() {
1411        let dialect: Box<dyn Dialect> = get_dialect(DbType::MySQL).unwrap();
1412        let mut w = LambdaWrapper::<User>::with_dialect("users", dialect);
1413        w.select(UserColumns::Id)
1414            .select(UserColumns::Name)
1415            .eq(UserColumns::Id, Value::I64(42))
1416            .order_by_desc(UserColumns::Id);
1417
1418        let sql = w.build_select();
1419        assert!(sql.contains("SELECT `id`, `name` FROM `users`"));
1420        assert!(sql.contains("`id` = 42"));
1421        assert!(sql.contains("ORDER BY `id` DESC"));
1422    }
1423}