Skip to main content

sz_orm_query/
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_query::lambda::{LambdaWrapper, Column};
29//! use sz_orm_query::define_columns;
30//! use sz_orm_model::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 std::marker::PhantomData;
60use sz_orm_model::Value;
61use sz_orm_model::{Dialect, MySqlDialect};
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_query::lambda::{LambdaWrapper, Column};
226/// use sz_orm_query::define_columns;
227/// use sz_orm_model::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_query::lambda::{LambdaWrapper, SoftDeleteConfig};
332    /// use sz_orm_model::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 sz_orm_model::sql_safety::validate_identifier(&config.column, "soft_delete column")
345            .is_ok()
346        {
347            self.soft_delete = Some(config);
348        }
349        self
350    }
351
352    /// 启用多租户过滤
353    ///
354    /// 启用后:
355    /// - `build_select` / `build_count` / `build_exists` / `build_delete`:
356    ///   自动追加 `column = tenant_id`
357    ///
358    /// # 示例
359    ///
360    /// ```
361    /// use sz_orm_query::lambda::{LambdaWrapper, TenantConfig};
362    /// use sz_orm_model::Value;
363    ///
364    /// struct User;
365    /// let mut w = LambdaWrapper::<User>::new("users")
366    ///     .with_tenant(TenantConfig {
367    ///         column: "tenant_id".to_string(),
368    ///         tenant_id: Value::I64(42),
369    ///     });
370    /// ```
371    pub fn with_tenant(mut self, config: TenantConfig) -> Self {
372        // 校验列名为合法标识符
373        if sz_orm_model::sql_safety::validate_identifier(&config.column, "tenant column").is_ok() {
374            self.tenant = Some(config);
375        }
376        self
377    }
378
379    /// 收集所有隐式 WHERE 条件(软删除 + 多租户)
380    /// 返回 (conditions, 是否非空)
381    fn collect_implicit_wheres(&self) -> Vec<String> {
382        let mut implicit = Vec::new();
383        if let Some(ref sd) = self.soft_delete {
384            let col = self.dialect.quote(&sd.column);
385            let val = sd
386                .not_deleted_value
387                .to_param_with_dialect(self.dialect.as_ref());
388            implicit.push(format!("{} = {}", col, val));
389        }
390        if let Some(ref t) = self.tenant {
391            let col = self.dialect.quote(&t.column);
392            let val = t.tenant_id.to_param_with_dialect(self.dialect.as_ref());
393            implicit.push(format!("{} = {}", col, val));
394        }
395        implicit
396    }
397
398    /// 渲染 WHERE 子句(包含用户 wheres + 隐式软删除/多租户条件)
399    fn render_where_clause(&self) -> String {
400        let user_conds: Vec<String> = self
401            .wheres
402            .iter()
403            .map(|w| w.render(self.dialect.as_ref()))
404            .collect();
405        let implicit_conds = self.collect_implicit_wheres();
406        let all_conds: Vec<String> = user_conds.into_iter().chain(implicit_conds).collect();
407        if all_conds.is_empty() {
408            String::new()
409        } else {
410            format!(" WHERE {}", all_conds.join(" AND "))
411        }
412    }
413
414    // -------------------- SELECT 字段 --------------------
415
416    /// 添加 SELECT 字段(类型安全)
417    ///
418    /// M-4 修复:对列名进行 `validate_identifier` 校验,防止恶意实现 `Column` trait
419    /// 注入非法标识符。
420    pub fn select<C: Column<M>>(&mut self, col: C) -> &mut Self {
421        let name = col.name();
422        // 校验列名为合法 SQL 标识符(非空、仅 ASCII 字母数字+下划线、不以数字开头)
423        // 校验失败时跳过该列(保留向后兼容,不中断调用链)
424        if sz_orm_model::sql_safety::validate_identifier(name, "lambda select column").is_ok() {
425            self.selects.push(name.to_string());
426        }
427        self
428    }
429
430    /// 批量添加 SELECT 字段
431    ///
432    /// M-4 修复:同 `select`,对每个列名校验。
433    pub fn select_many<C: Column<M>>(&mut self, cols: &[C]) -> &mut Self {
434        for c in cols {
435            let name = c.name();
436            if sz_orm_model::sql_safety::validate_identifier(name, "lambda select column").is_ok() {
437                self.selects.push(name.to_string());
438            }
439        }
440        self
441    }
442
443    /// SELECT *(清空已有字段选择)
444    pub fn select_all(&mut self) -> &mut Self {
445        self.selects.clear();
446        self
447    }
448
449    // -------------------- WHERE 条件 --------------------
450
451    /// `col = value`
452    pub fn eq<C: Column<M>>(&mut self, col: C, value: Value) -> &mut Self {
453        self.wheres
454            .push(WhereClause::Eq(col.name().to_string(), value));
455        self
456    }
457
458    /// `col != value`
459    pub fn ne<C: Column<M>>(&mut self, col: C, value: Value) -> &mut Self {
460        self.wheres
461            .push(WhereClause::Ne(col.name().to_string(), value));
462        self
463    }
464
465    /// `col > value`
466    pub fn gt<C: Column<M>>(&mut self, col: C, value: Value) -> &mut Self {
467        self.wheres
468            .push(WhereClause::Gt(col.name().to_string(), value));
469        self
470    }
471
472    /// `col >= value`
473    pub fn ge<C: Column<M>>(&mut self, col: C, value: Value) -> &mut Self {
474        self.wheres
475            .push(WhereClause::Ge(col.name().to_string(), value));
476        self
477    }
478
479    /// `col < value`
480    pub fn lt<C: Column<M>>(&mut self, col: C, value: Value) -> &mut Self {
481        self.wheres
482            .push(WhereClause::Lt(col.name().to_string(), value));
483        self
484    }
485
486    /// `col <= value`
487    pub fn le<C: Column<M>>(&mut self, col: C, value: Value) -> &mut Self {
488        self.wheres
489            .push(WhereClause::Le(col.name().to_string(), value));
490        self
491    }
492
493    /// `col LIKE value`
494    pub fn like<C: Column<M>>(&mut self, col: C, value: Value) -> &mut Self {
495        self.wheres
496            .push(WhereClause::Like(col.name().to_string(), value));
497        self
498    }
499
500    /// `col IS NULL`
501    pub fn is_null<C: Column<M>>(&mut self, col: C) -> &mut Self {
502        self.wheres
503            .push(WhereClause::IsNull(col.name().to_string()));
504        self
505    }
506
507    /// `col IS NOT NULL`
508    pub fn is_not_null<C: Column<M>>(&mut self, col: C) -> &mut Self {
509        self.wheres
510            .push(WhereClause::IsNotNull(col.name().to_string()));
511        self
512    }
513
514    /// `col IN (v1, v2, ...)`
515    pub fn r#in<C: Column<M>>(&mut self, col: C, values: Vec<Value>) -> &mut Self {
516        self.wheres
517            .push(WhereClause::In(col.name().to_string(), values));
518        self
519    }
520
521    /// `col NOT IN (v1, v2, ...)`
522    pub fn not_in<C: Column<M>>(&mut self, col: C, values: Vec<Value>) -> &mut Self {
523        self.wheres
524            .push(WhereClause::NotIn(col.name().to_string(), values));
525        self
526    }
527
528    /// `col BETWEEN a AND b`
529    pub fn between<C: Column<M>>(&mut self, col: C, a: Value, b: Value) -> &mut Self {
530        self.wheres
531            .push(WhereClause::Between(col.name().to_string(), a, b));
532        self
533    }
534
535    /// 追加原始 SQL WHERE 条件(用于 OR 等复杂场景)
536    ///
537    /// # 安全警告
538    ///
539    /// 此方法是 escape hatch,传入的 SQL 会**原样拼接**到最终 SQL 中。
540    /// **严禁将用户输入直接拼接**到 `sql` 参数中(会引入 SQL 注入风险)。
541    /// 若需使用用户输入,请改用 `eq` / `ne` / `lt` 等参数化方法。
542    pub fn raw_where(&mut self, sql: impl Into<String>) -> &mut Self {
543        self.wheres.push(WhereClause::Raw(sql.into()));
544        self
545    }
546
547    // -------------------- ORDER BY / LIMIT / OFFSET --------------------
548
549    /// 添加升序排序
550    pub fn order_by_asc<C: Column<M>>(&mut self, col: C) -> &mut Self {
551        self.orders.push(OrderBy {
552            column: col.name().to_string(),
553            direction: OrderDirection::Asc,
554        });
555        self
556    }
557
558    /// 添加降序排序
559    pub fn order_by_desc<C: Column<M>>(&mut self, col: C) -> &mut Self {
560        self.orders.push(OrderBy {
561            column: col.name().to_string(),
562            direction: OrderDirection::Desc,
563        });
564        self
565    }
566
567    /// 设置 LIMIT
568    pub fn limit(&mut self, n: u64) -> &mut Self {
569        self.limit = Some(n);
570        self
571    }
572
573    /// 设置 OFFSET
574    pub fn offset(&mut self, n: u64) -> &mut Self {
575        self.offset = Some(n);
576        self
577    }
578
579    /// 分页(设置 LIMIT + OFFSET)
580    ///
581    /// page 从 1 开始计数,page=1 时无 OFFSET(从第 0 条开始)。
582    pub fn page(&mut self, page: u64, page_size: u64) -> &mut Self {
583        self.limit = Some(page_size);
584        if page > 1 {
585            self.offset = Some((page - 1) * page_size);
586        } else {
587            self.offset = None;
588        }
589        self
590    }
591
592    // -------------------- SQL 生成 --------------------
593
594    /// 生成 SELECT SQL
595    ///
596    /// P3-2 修复:若启用了软删除/多租户,自动追加隐式 WHERE 条件
597    pub fn build_select(&self) -> String {
598        let quoted_table = self.dialect.quote(&self.table);
599
600        // SELECT 字段
601        let select_sql = if self.selects.is_empty() {
602            "*".to_string()
603        } else {
604            self.selects
605                .iter()
606                .map(|c| self.dialect.quote(c))
607                .collect::<Vec<_>>()
608                .join(", ")
609        };
610
611        let mut sql = format!("SELECT {} FROM {}", select_sql, quoted_table);
612
613        // WHERE(含隐式软删除/多租户条件)
614        sql.push_str(&self.render_where_clause());
615
616        // ORDER BY
617        if !self.orders.is_empty() {
618            let orders: Vec<String> = self
619                .orders
620                .iter()
621                .map(|o| {
622                    let dir = match o.direction {
623                        OrderDirection::Asc => "ASC",
624                        OrderDirection::Desc => "DESC",
625                    };
626                    format!("{} {}", self.dialect.quote(&o.column), dir)
627                })
628                .collect();
629            sql.push_str(" ORDER BY ");
630            sql.push_str(&orders.join(", "));
631        }
632
633        // LIMIT / OFFSET
634        if let Some(l) = self.limit {
635            sql.push_str(&format!(" LIMIT {}", l));
636        }
637        if let Some(o) = self.offset {
638            sql.push_str(&format!(" OFFSET {}", o));
639        }
640
641        sql
642    }
643
644    /// 生成 COUNT SQL(SELECT COUNT(*) FROM ... WHERE ...)
645    ///
646    /// P3-2 修复:若启用了软删除/多租户,自动追加隐式 WHERE 条件
647    pub fn build_count(&self) -> String {
648        let quoted_table = self.dialect.quote(&self.table);
649        let mut sql = format!("SELECT COUNT(*) FROM {}", quoted_table);
650        sql.push_str(&self.render_where_clause());
651        sql
652    }
653
654    /// 生成 EXISTS SQL(SELECT EXISTS(SELECT 1 FROM ... WHERE ...) AS exists_flag)
655    ///
656    /// P3-2 修复:若启用了软删除/多租户,自动追加隐式 WHERE 条件
657    pub fn build_exists(&self) -> String {
658        let inner = self.build_select();
659        // 把 SELECT 字段部分替换为 SELECT 1
660        let inner = if let Some(pos) = inner.find(" FROM ") {
661            format!("SELECT 1{}", &inner[pos..])
662        } else {
663            inner
664        };
665        format!("SELECT EXISTS({}) AS exists_flag", inner)
666    }
667
668    /// 生成 DELETE SQL
669    ///
670    /// P3-2 修复:
671    /// - 若启用软删除,则改为 `UPDATE ... SET column = deleted_value`
672    ///   (即软删除:仅标记,不实际删除行)
673    /// - 若启用多租户,自动追加 `column = tenant_id` 条件,防止跨租户删除
674    pub fn build_delete(&self) -> String {
675        let quoted_table = self.dialect.quote(&self.table);
676
677        // 软删除:改为 UPDATE
678        if let Some(ref sd) = self.soft_delete {
679            let col = self.dialect.quote(&sd.column);
680            let val = sd
681                .deleted_value
682                .to_param_with_dialect(self.dialect.as_ref());
683            let mut sql = format!("UPDATE {} SET {} = {}", quoted_table, col, val);
684
685            // WHERE(含用户条件 + 多租户条件;软删除条件不再追加,因为本就是 UPDATE SET)
686            let user_conds: Vec<String> = self
687                .wheres
688                .iter()
689                .map(|w| w.render(self.dialect.as_ref()))
690                .collect();
691            let mut all_conds = user_conds;
692            // 多租户过滤仍需追加
693            if let Some(ref t) = self.tenant {
694                let tcol = self.dialect.quote(&t.column);
695                let tval = t.tenant_id.to_param_with_dialect(self.dialect.as_ref());
696                all_conds.push(format!("{} = {}", tcol, tval));
697            }
698            // 同时确保只更新未删除的行
699            let not_deleted_cond = format!(
700                "{} = {}",
701                col,
702                sd.not_deleted_value
703                    .to_param_with_dialect(self.dialect.as_ref())
704            );
705            all_conds.push(not_deleted_cond);
706
707            if !all_conds.is_empty() {
708                sql.push_str(" WHERE ");
709                sql.push_str(&all_conds.join(" AND "));
710            }
711            return sql;
712        }
713
714        // 硬删除:DELETE FROM
715        let mut sql = format!("DELETE FROM {}", quoted_table);
716        sql.push_str(&self.render_where_clause());
717        sql
718    }
719
720    // -------------------- 状态查询 --------------------
721
722    /// 返回当前 WHERE 条件数量
723    pub fn where_count(&self) -> usize {
724        self.wheres.len()
725    }
726
727    /// 返回当前 SELECT 字段数量
728    pub fn select_count(&self) -> usize {
729        self.selects.len()
730    }
731
732    /// 返回表名
733    pub fn table(&self) -> &str {
734        &self.table
735    }
736
737    /// 重置所有条件(保留表名和方言)
738    pub fn reset(&mut self) -> &mut Self {
739        self.selects.clear();
740        self.wheres.clear();
741        self.orders.clear();
742        self.limit = None;
743        self.offset = None;
744        self
745    }
746}
747
748// ============================================================================
749// define_columns! 宏 — 为 Model 定义字段标记
750// ============================================================================
751
752/// 为 Model 定义一组类型安全的字段标记
753///
754/// # 示例
755///
756/// ```
757/// use sz_orm_query::define_columns;
758///
759/// struct User;
760///
761/// define_columns! {
762///     UserColumns for User table = "users" {
763///         Id => "id",
764///         Name => "name",
765///         Age => "age",
766///     }
767/// }
768/// ```
769///
770/// 生成如下代码:
771///
772/// ```rust,ignore
773/// #[derive(Clone, Copy)]
774/// pub struct UserColumns;
775///
776/// impl UserColumns {
777///     pub const Id: UserColumn = UserColumn { name: "id", table: "users" };
778///     pub const Name: UserColumn = UserColumn { name: "name", table: "users" };
779///     pub const Age: UserColumn = UserColumn { name: "age", table: "users" };
780/// }
781///
782/// #[derive(Clone, Copy)]
783/// pub struct UserColumn {
784///     pub name: &'static str,
785///     pub table: &'static str,
786/// }
787///
788/// impl Column<User> for UserColumn {
789///     fn name(&self) -> &'static str { self.name }
790///     fn table(&self) -> &'static str { self.table }
791/// }
792/// ```
793#[macro_export]
794macro_rules! define_columns {
795    (
796        $columns_struct:ident for $model:ident table = $table:literal {
797            $( $field:ident => $name:literal ),* $(,)?
798        }
799    ) => {
800        /// 字段标记类型(每个 Model 一组)
801        ///
802        /// 通过 `ModelColumns::FieldName` 引用对应字段,编译期检查拼写错误。
803        #[derive(Debug, Clone, Copy)]
804        pub struct $columns_struct {
805            /// 字段名
806            pub name: &'static str,
807            /// 表名
808            pub table: &'static str,
809        }
810
811        impl $crate::lambda::Column<$model> for $columns_struct {
812            fn name(&self) -> &'static str {
813                self.name
814            }
815
816            fn table(&self) -> &'static str {
817                self.table
818            }
819        }
820
821        impl $columns_struct {
822            // 允许 PascalCase 常量名(如 Id、Name),符合字段命名习惯
823            $(
824                #[allow(non_upper_case_globals, dead_code)]
825                pub const $field: $columns_struct = $columns_struct { name: $name, table: $table };
826            )*
827        }
828    };
829}
830
831// ============================================================================
832// 单元测试
833// ============================================================================
834
835#[cfg(test)]
836mod tests {
837    use super::*;
838    use sz_orm_model::dialect::get_dialect;
839    use sz_orm_model::DbType;
840    use sz_orm_model::PostgreSqlDialect;
841
842    // 测试用 Model
843    struct User;
844
845    // 为 User 定义字段标记
846    define_columns! {
847        UserColumns for User table = "users" {
848            Id => "id",
849            Name => "name",
850            Age => "age",
851            Email => "email",
852        }
853    }
854
855    // 另一个 Model 用于测试表名隔离
856    struct Order;
857
858    define_columns! {
859        OrderColumns for Order table = "orders" {
860            OrderId => "order_id",
861            UserId => "user_id",
862            Total => "total",
863        }
864    }
865
866    // ===== Column trait 测试 =====
867
868    #[test]
869    fn test_column_name_and_table() {
870        assert_eq!(UserColumns::Id.name(), "id");
871        assert_eq!(UserColumns::Id.table(), "users");
872        assert_eq!(UserColumns::Name.name(), "name");
873        assert_eq!(UserColumns::Age.name(), "age");
874        assert_eq!(UserColumns::Email.name(), "email");
875    }
876
877    #[test]
878    fn test_column_for_different_models() {
879        assert_eq!(OrderColumns::OrderId.name(), "order_id");
880        assert_eq!(OrderColumns::OrderId.table(), "orders");
881        assert_eq!(OrderColumns::UserId.name(), "user_id");
882    }
883
884    // ===== LambdaWrapper 基础测试 =====
885
886    #[test]
887    fn test_new_wrapper() {
888        let w = LambdaWrapper::<User>::new("users");
889        assert_eq!(w.table(), "users");
890        assert_eq!(w.where_count(), 0);
891        assert_eq!(w.select_count(), 0);
892    }
893
894    #[test]
895    fn test_select_single() {
896        let mut w = LambdaWrapper::<User>::new("users");
897        w.select(UserColumns::Id);
898        let sql = w.build_select();
899        assert!(sql.contains("SELECT `id` FROM `users`"));
900    }
901
902    #[test]
903    fn test_select_multiple() {
904        let mut w = LambdaWrapper::<User>::new("users");
905        w.select(UserColumns::Id)
906            .select(UserColumns::Name)
907            .select(UserColumns::Age);
908        let sql = w.build_select();
909        assert!(sql.contains("`id`, `name`, `age`"));
910    }
911
912    #[test]
913    fn test_select_many() {
914        let mut w = LambdaWrapper::<User>::new("users");
915        w.select_many(&[UserColumns::Id, UserColumns::Name, UserColumns::Age]);
916        let sql = w.build_select();
917        assert!(sql.contains("`id`, `name`, `age`"));
918    }
919
920    #[test]
921    fn test_select_all_clears_selects() {
922        let mut w = LambdaWrapper::<User>::new("users");
923        w.select(UserColumns::Id);
924        assert_eq!(w.select_count(), 1);
925        w.select_all();
926        assert_eq!(w.select_count(), 0);
927        let sql = w.build_select();
928        assert!(sql.contains("SELECT * FROM"));
929    }
930
931    #[test]
932    fn test_default_select_is_star() {
933        let w = LambdaWrapper::<User>::new("users");
934        let sql = w.build_select();
935        assert!(sql.contains("SELECT * FROM `users`"));
936    }
937
938    // ===== WHERE 条件测试 =====
939
940    #[test]
941    fn test_where_eq() {
942        let mut w = LambdaWrapper::<User>::new("users");
943        w.eq(UserColumns::Id, Value::I64(1));
944        let sql = w.build_select();
945        assert!(sql.contains("WHERE `id` = 1"));
946    }
947
948    #[test]
949    fn test_where_ne() {
950        let mut w = LambdaWrapper::<User>::new("users");
951        w.ne(UserColumns::Id, Value::I64(1));
952        let sql = w.build_select();
953        assert!(sql.contains("`id` != 1"));
954    }
955
956    #[test]
957    fn test_where_gt_ge_lt_le() {
958        let mut w = LambdaWrapper::<User>::new("users");
959        w.gt(UserColumns::Age, Value::I64(18))
960            .ge(UserColumns::Age, Value::I64(20))
961            .lt(UserColumns::Age, Value::I64(65))
962            .le(UserColumns::Age, Value::I64(60));
963        let sql = w.build_select();
964        assert!(sql.contains("`age` > 18"));
965        assert!(sql.contains("`age` >= 20"));
966        assert!(sql.contains("`age` < 65"));
967        assert!(sql.contains("`age` <= 60"));
968    }
969
970    #[test]
971    fn test_where_like() {
972        let mut w = LambdaWrapper::<User>::new("users");
973        w.like(UserColumns::Name, Value::String("%alice%".to_string()));
974        let sql = w.build_select();
975        assert!(sql.contains("`name` LIKE '%alice%'"));
976    }
977
978    #[test]
979    fn test_where_is_null() {
980        let mut w = LambdaWrapper::<User>::new("users");
981        w.is_null(UserColumns::Email);
982        let sql = w.build_select();
983        assert!(sql.contains("`email` IS NULL"));
984    }
985
986    #[test]
987    fn test_where_is_not_null() {
988        let mut w = LambdaWrapper::<User>::new("users");
989        w.is_not_null(UserColumns::Email);
990        let sql = w.build_select();
991        assert!(sql.contains("`email` IS NOT NULL"));
992    }
993
994    #[test]
995    fn test_where_in() {
996        let mut w = LambdaWrapper::<User>::new("users");
997        w.r#in(
998            UserColumns::Id,
999            vec![Value::I64(1), Value::I64(2), Value::I64(3)],
1000        );
1001        let sql = w.build_select();
1002        assert!(sql.contains("`id` IN (1, 2, 3)"));
1003    }
1004
1005    #[test]
1006    fn test_where_not_in() {
1007        let mut w = LambdaWrapper::<User>::new("users");
1008        w.not_in(UserColumns::Id, vec![Value::I64(1), Value::I64(2)]);
1009        let sql = w.build_select();
1010        assert!(sql.contains("`id` NOT IN (1, 2)"));
1011    }
1012
1013    #[test]
1014    fn test_where_between() {
1015        let mut w = LambdaWrapper::<User>::new("users");
1016        w.between(UserColumns::Age, Value::I64(18), Value::I64(65));
1017        let sql = w.build_select();
1018        assert!(sql.contains("`age` BETWEEN 18 AND 65"));
1019    }
1020
1021    #[test]
1022    fn test_where_multiple_anded() {
1023        let mut w = LambdaWrapper::<User>::new("users");
1024        w.eq(UserColumns::Id, Value::I64(1))
1025            .gt(UserColumns::Age, Value::I64(18))
1026            .like(UserColumns::Name, Value::String("alice%".to_string()));
1027        let sql = w.build_select();
1028        assert!(sql.contains("`id` = 1"));
1029        assert!(sql.contains("`age` > 18"));
1030        assert!(sql.contains("`name` LIKE 'alice%'"));
1031        // 所有条件用 AND 连接
1032        assert!(sql.contains(" AND "));
1033    }
1034
1035    #[test]
1036    fn test_where_raw() {
1037        let mut w = LambdaWrapper::<User>::new("users");
1038        w.raw_where("name = 'alice' OR name = 'bob'");
1039        let sql = w.build_select();
1040        assert!(sql.contains("name = 'alice' OR name = 'bob'"));
1041    }
1042
1043    // ===== ORDER BY / LIMIT / OFFSET 测试 =====
1044
1045    #[test]
1046    fn test_order_by_asc() {
1047        let mut w = LambdaWrapper::<User>::new("users");
1048        w.order_by_asc(UserColumns::Name);
1049        let sql = w.build_select();
1050        assert!(sql.contains("ORDER BY `name` ASC"));
1051    }
1052
1053    #[test]
1054    fn test_order_by_desc() {
1055        let mut w = LambdaWrapper::<User>::new("users");
1056        w.order_by_desc(UserColumns::Id);
1057        let sql = w.build_select();
1058        assert!(sql.contains("ORDER BY `id` DESC"));
1059    }
1060
1061    #[test]
1062    fn test_order_by_multiple() {
1063        let mut w = LambdaWrapper::<User>::new("users");
1064        w.order_by_asc(UserColumns::Name)
1065            .order_by_desc(UserColumns::Id);
1066        let sql = w.build_select();
1067        assert!(sql.contains("ORDER BY `name` ASC, `id` DESC"));
1068    }
1069
1070    #[test]
1071    fn test_limit() {
1072        let mut w = LambdaWrapper::<User>::new("users");
1073        w.limit(10);
1074        let sql = w.build_select();
1075        assert!(sql.contains("LIMIT 10"));
1076    }
1077
1078    #[test]
1079    fn test_offset() {
1080        let mut w = LambdaWrapper::<User>::new("users");
1081        w.limit(10).offset(20);
1082        let sql = w.build_select();
1083        assert!(sql.contains("LIMIT 10"));
1084        assert!(sql.contains("OFFSET 20"));
1085    }
1086
1087    #[test]
1088    fn test_page() {
1089        let mut w = LambdaWrapper::<User>::new("users");
1090        w.page(3, 20); // 第 3 页,每页 20 条
1091        let sql = w.build_select();
1092        assert!(sql.contains("LIMIT 20"));
1093        assert!(sql.contains("OFFSET 40")); // (3-1) * 20
1094    }
1095
1096    #[test]
1097    fn test_page_1_no_offset() {
1098        let mut w = LambdaWrapper::<User>::new("users");
1099        w.page(1, 10);
1100        let sql = w.build_select();
1101        assert!(sql.contains("LIMIT 10"));
1102        assert!(!sql.contains("OFFSET")); // 第 1 页无 OFFSET
1103    }
1104
1105    // ===== SQL 生成测试 =====
1106
1107    #[test]
1108    fn test_build_count() {
1109        let mut w = LambdaWrapper::<User>::new("users");
1110        w.gt(UserColumns::Age, Value::I64(18));
1111        let sql = w.build_count();
1112        assert!(sql.contains("SELECT COUNT(*) FROM `users`"));
1113        assert!(sql.contains("`age` > 18"));
1114        // 不应包含 ORDER BY / LIMIT
1115        assert!(!sql.contains("ORDER BY"));
1116        assert!(!sql.contains("LIMIT"));
1117    }
1118
1119    #[test]
1120    fn test_build_exists() {
1121        let mut w = LambdaWrapper::<User>::new("users");
1122        w.eq(UserColumns::Id, Value::I64(1));
1123        let sql = w.build_exists();
1124        assert!(sql.starts_with("SELECT EXISTS("));
1125        assert!(sql.contains("SELECT 1 FROM `users`"));
1126        assert!(sql.contains("`id` = 1"));
1127        assert!(sql.ends_with(") AS exists_flag"));
1128    }
1129
1130    #[test]
1131    fn test_build_delete() {
1132        let mut w = LambdaWrapper::<User>::new("users");
1133        w.eq(UserColumns::Id, Value::I64(1));
1134        let sql = w.build_delete();
1135        assert!(sql.starts_with("DELETE FROM `users`"));
1136        assert!(sql.contains("WHERE `id` = 1"));
1137    }
1138
1139    // ===== P3-2 修复:软删除 / 多租户测试 =====
1140
1141    #[test]
1142    fn test_soft_delete_select_appends_filter() {
1143        // 启用软删除后,build_select 应自动追加 `deleted = 0`
1144        let w = LambdaWrapper::<User>::new("users").with_soft_delete(SoftDeleteConfig {
1145            column: "deleted".to_string(),
1146            not_deleted_value: Value::I64(0),
1147            deleted_value: Value::I64(1),
1148        });
1149        let sql = w.build_select();
1150        assert!(
1151            sql.contains("`deleted` = 0"),
1152            "软删除 SELECT 应追加 `deleted` = 0: {}",
1153            sql
1154        );
1155    }
1156
1157    #[test]
1158    fn test_soft_delete_count_appends_filter() {
1159        // 启用软删除后,build_count 应自动追加 `deleted = 0`
1160        let w = LambdaWrapper::<User>::new("users").with_soft_delete(SoftDeleteConfig {
1161            column: "deleted".to_string(),
1162            not_deleted_value: Value::I64(0),
1163            deleted_value: Value::I64(1),
1164        });
1165        let sql = w.build_count();
1166        assert!(
1167            sql.contains("`deleted` = 0"),
1168            "软删除 COUNT 应追加 `deleted` = 0: {}",
1169            sql
1170        );
1171    }
1172
1173    #[test]
1174    fn test_soft_delete_exists_appends_filter() {
1175        // 启用软删除后,build_exists 应自动追加 `deleted = 0`
1176        let w = LambdaWrapper::<User>::new("users").with_soft_delete(SoftDeleteConfig {
1177            column: "deleted".to_string(),
1178            not_deleted_value: Value::I64(0),
1179            deleted_value: Value::I64(1),
1180        });
1181        let sql = w.build_exists();
1182        assert!(
1183            sql.contains("`deleted` = 0"),
1184            "软删除 EXISTS 应追加 `deleted` = 0: {}",
1185            sql
1186        );
1187    }
1188
1189    #[test]
1190    fn test_soft_delete_delete_becomes_update() {
1191        // 启用软删除后,build_delete 应改为 UPDATE ... SET deleted = 1
1192        let mut w = LambdaWrapper::<User>::new("users").with_soft_delete(SoftDeleteConfig {
1193            column: "deleted".to_string(),
1194            not_deleted_value: Value::I64(0),
1195            deleted_value: Value::I64(1),
1196        });
1197        w.eq(UserColumns::Id, Value::I64(42));
1198        let sql = w.build_delete();
1199        assert!(
1200            sql.starts_with("UPDATE `users` SET `deleted` = 1"),
1201            "软删除应为 UPDATE:{}",
1202            sql
1203        );
1204        assert!(sql.contains("`id` = 42"), "应保留用户 WHERE 条件:{}", sql);
1205        assert!(
1206            sql.contains("`deleted` = 0"),
1207            "应追加未删除条件防止重复删除:{}",
1208            sql
1209        );
1210        assert!(
1211            !sql.starts_with("DELETE"),
1212            "软删除不应生成 DELETE 语句:{}",
1213            sql
1214        );
1215    }
1216
1217    #[test]
1218    fn test_tenant_select_appends_filter() {
1219        // 启用多租户后,build_select 应自动追加 `tenant_id` = 42
1220        let w = LambdaWrapper::<User>::new("users").with_tenant(TenantConfig {
1221            column: "tenant_id".to_string(),
1222            tenant_id: Value::I64(42),
1223        });
1224        let sql = w.build_select();
1225        assert!(
1226            sql.contains("`tenant_id` = 42"),
1227            "多租户 SELECT 应追加 `tenant_id` = 42: {}",
1228            sql
1229        );
1230    }
1231
1232    #[test]
1233    fn test_tenant_delete_appends_filter() {
1234        // 启用多租户后,build_delete 应自动追加 `tenant_id` = 42 防止跨租户删除
1235        let mut w = LambdaWrapper::<User>::new("users").with_tenant(TenantConfig {
1236            column: "tenant_id".to_string(),
1237            tenant_id: Value::I64(42),
1238        });
1239        w.eq(UserColumns::Id, Value::I64(1));
1240        let sql = w.build_delete();
1241        assert!(
1242            sql.contains("`tenant_id` = 42"),
1243            "多租户 DELETE 应追加 `tenant_id` = 42: {}",
1244            sql
1245        );
1246    }
1247
1248    #[test]
1249    fn test_soft_delete_and_tenant_combined() {
1250        // 同时启用软删除和多租户
1251        let mut w = LambdaWrapper::<User>::new("users")
1252            .with_soft_delete(SoftDeleteConfig {
1253                column: "deleted".to_string(),
1254                not_deleted_value: Value::I64(0),
1255                deleted_value: Value::I64(1),
1256            })
1257            .with_tenant(TenantConfig {
1258                column: "tenant_id".to_string(),
1259                tenant_id: Value::I64(99),
1260            });
1261        w.eq(UserColumns::Id, Value::I64(7));
1262
1263        // SELECT 应同时包含两个隐式条件
1264        let sql = w.build_select();
1265        assert!(sql.contains("`deleted` = 0"), "应包含软删除条件: {}", sql);
1266        assert!(
1267            sql.contains("`tenant_id` = 99"),
1268            "应包含多租户条件: {}",
1269            sql
1270        );
1271        assert!(sql.contains("`id` = 7"), "应包含用户条件: {}", sql);
1272
1273        // DELETE 应改为 UPDATE,并追加所有条件
1274        let del_sql = w.build_delete();
1275        assert!(del_sql.starts_with("UPDATE `users` SET `deleted` = 1"));
1276        assert!(del_sql.contains("`tenant_id` = 99"));
1277        assert!(del_sql.contains("`deleted` = 0"));
1278        assert!(del_sql.contains("`id` = 7"));
1279    }
1280
1281    #[test]
1282    fn test_no_soft_delete_no_tenant_backward_compat() {
1283        // 未启用软删除/多租户时,行为与之前一致
1284        let mut w = LambdaWrapper::<User>::new("users");
1285        w.eq(UserColumns::Id, Value::I64(1));
1286        let sql = w.build_select();
1287        assert!(!sql.contains("deleted"));
1288        assert!(!sql.contains("tenant_id"));
1289
1290        let del_sql = w.build_delete();
1291        assert!(del_sql.starts_with("DELETE FROM"));
1292    }
1293
1294    // ===== 方言测试 =====
1295
1296    #[test]
1297    fn test_with_postgres_dialect() {
1298        let dialect: Box<dyn Dialect> = Box::new(PostgreSqlDialect);
1299        let mut w = LambdaWrapper::<User>::with_dialect("users", dialect);
1300        w.eq(UserColumns::Id, Value::I64(1));
1301        let sql = w.build_select();
1302        assert!(sql.contains("\"users\""));
1303        assert!(sql.contains("\"id\" = 1"));
1304    }
1305
1306    #[test]
1307    fn test_postgres_select() {
1308        let dialect: Box<dyn Dialect> = Box::new(PostgreSqlDialect);
1309        let mut w = LambdaWrapper::<User>::with_dialect("users", dialect);
1310        w.select(UserColumns::Id).select(UserColumns::Name);
1311        let sql = w.build_select();
1312        assert!(sql.contains("\"id\", \"name\""));
1313    }
1314
1315    // ===== 完整查询测试 =====
1316
1317    #[test]
1318    fn test_complex_query() {
1319        let mut w = LambdaWrapper::<User>::new("users");
1320        w.select(UserColumns::Id)
1321            .select(UserColumns::Name)
1322            .select(UserColumns::Age)
1323            .gt(UserColumns::Age, Value::I64(18))
1324            .like(UserColumns::Name, Value::String("a%".to_string()))
1325            .is_not_null(UserColumns::Email)
1326            .order_by_desc(UserColumns::Id)
1327            .limit(10)
1328            .offset(20);
1329
1330        let sql = w.build_select();
1331        assert!(sql.contains("SELECT `id`, `name`, `age` FROM `users`"));
1332        assert!(sql.contains("`age` > 18"));
1333        assert!(sql.contains("`name` LIKE 'a%'"));
1334        assert!(sql.contains("`email` IS NOT NULL"));
1335        assert!(sql.contains("ORDER BY `id` DESC"));
1336        assert!(sql.contains("LIMIT 10"));
1337        assert!(sql.contains("OFFSET 20"));
1338    }
1339
1340    #[test]
1341    fn test_reset_clears_all() {
1342        let mut w = LambdaWrapper::<User>::new("users");
1343        w.select(UserColumns::Id)
1344            .eq(UserColumns::Id, Value::I64(1))
1345            .order_by_asc(UserColumns::Name)
1346            .limit(10);
1347
1348        w.reset();
1349        assert_eq!(w.select_count(), 0);
1350        assert_eq!(w.where_count(), 0);
1351        let sql = w.build_select();
1352        assert!(sql.contains("SELECT * FROM `users`"));
1353        assert!(!sql.contains("WHERE"));
1354        assert!(!sql.contains("ORDER BY"));
1355        assert!(!sql.contains("LIMIT"));
1356    }
1357
1358    // ===== 跨 Model 类型隔离测试 =====
1359
1360    #[test]
1361    fn test_different_models_dont_share_columns() {
1362        let mut user_w = LambdaWrapper::<User>::new("users");
1363        user_w.eq(UserColumns::Id, Value::I64(1));
1364
1365        let mut order_w = LambdaWrapper::<Order>::new("orders");
1366        order_w.eq(OrderColumns::OrderId, Value::I64(100));
1367
1368        let user_sql = user_w.build_select();
1369        let order_sql = order_w.build_select();
1370
1371        assert!(user_sql.contains("`users`"));
1372        assert!(user_sql.contains("`id` = 1"));
1373        assert!(order_sql.contains("`orders`"));
1374        assert!(order_sql.contains("`order_id` = 100"));
1375    }
1376
1377    // ===== 编译期类型安全演示(运行时验证)=====
1378
1379    #[test]
1380    fn test_type_safety_compile_time_check() {
1381        // 以下代码若取消注释将无法编译:
1382        // let mut w = LambdaWrapper::<User>::new("users");
1383        // w.eq(OrderColumns::OrderId, Value::I64(1)); // OrderColumns 不能用于 User 的 wrapper
1384
1385        // 这里通过正常调用验证类型隔离工作正常
1386        let mut w = LambdaWrapper::<User>::new("users");
1387        w.eq(UserColumns::Id, Value::I64(1));
1388        assert_eq!(w.where_count(), 1);
1389    }
1390
1391    #[test]
1392    fn test_string_value_escape() {
1393        // v0.2.2 修复 H-1:默认使用 MySqlDialect,单引号转义为 \'
1394        let mut w = LambdaWrapper::<User>::new("users");
1395        w.eq(UserColumns::Name, Value::String("O'Brien".to_string()));
1396        let sql = w.build_select();
1397        // MySQL 方言下字符串中的单引号应被转义为 \'
1398        assert!(sql.contains("'O\\'Brien'"));
1399
1400        // 使用 PostgreSQL 方言时,单引号转义为 ''
1401        let pg_dialect: Box<dyn Dialect> = get_dialect(DbType::PostgreSQL).unwrap();
1402        let mut w_pg = LambdaWrapper::<User>::with_dialect("users", pg_dialect);
1403        w_pg.eq(UserColumns::Name, Value::String("O'Brien".to_string()));
1404        let sql_pg = w_pg.build_select();
1405        assert!(sql_pg.contains("'O''Brien'"));
1406    }
1407
1408    // ===== 使用真实方言验证集成 =====
1409
1410    #[test]
1411    fn test_with_real_mysql_dialect() {
1412        let dialect: Box<dyn Dialect> = get_dialect(DbType::MySQL).unwrap();
1413        let mut w = LambdaWrapper::<User>::with_dialect("users", dialect);
1414        w.select(UserColumns::Id)
1415            .select(UserColumns::Name)
1416            .eq(UserColumns::Id, Value::I64(42))
1417            .order_by_desc(UserColumns::Id);
1418
1419        let sql = w.build_select();
1420        assert!(sql.contains("SELECT `id`, `name` FROM `users`"));
1421        assert!(sql.contains("`id` = 42"));
1422        assert!(sql.contains("ORDER BY `id` DESC"));
1423    }
1424}