Skip to main content

sz_orm_core/
dialect.rs

1//! 不同数据库的方言抽象
2//!
3//! 为数据库特定的 SQL 语法提供统一接口
4
5pub use crate::db_type::DbType;
6use crate::error::DbError;
7use std::fmt;
8
9/// L-4 修复:SQL 标识符最大长度(取所有主流数据库最严格值)
10///
11/// - PostgreSQL: 63 chars (NAMEDATALEN default 64, minus 1)
12/// - MySQL: 64 chars
13/// - Oracle: 30 chars (12.2R2 之前) / 128 chars (12.2R2+)
14/// - SQL Server: 128 chars
15/// - SQLite: 实际无限制(但建议遵守 63)
16///
17/// 取 63 作为最严格值,覆盖所有主流数据库。
18pub const MAX_IDENTIFIER_LEN: usize = 63;
19
20/// 数据库方言 trait
21///
22/// 实现者负责处理各数据库特有的 SQL 语法差异
23pub trait Dialect: Send + Sync {
24    /// 克隆为 trait object(用于 `QueryBuilder::clone_for_count`)。
25    fn clone_box(&self) -> Box<dyn Dialect>;
26
27    /// 返回该方言对应的数据库类型
28    fn db_type(&self) -> DbType;
29
30    /// 引用标识符(表名、列名等)
31    fn quote(&self, identifier: &str) -> String;
32
33    /// L-4 修复:带校验的引用标识符
34    ///
35    /// 与 `quote()` 不同,此方法会先校验标识符:
36    /// - 非空
37    /// - 长度 ≤ `MAX_IDENTIFIER_LEN` (63 chars)
38    /// - 不含 SQL 元字符(引号、分号、空格、注释等)
39    ///
40    /// 校验失败时返回 `DbError::InvalidInput`。
41    ///
42    /// 建议在调用方不可信的场景(如用户输入的表名/列名)使用此方法替代 `quote()`。
43    fn quote_checked(&self, identifier: &str) -> Result<String, DbError> {
44        crate::sql_safety::validate_identifier(identifier, "identifier")?;
45        Ok(self.quote(identifier))
46    }
47
48    /// 转义字符串字面量,确保可安全嵌入 SQL
49    fn escape_string(&self, s: &str) -> String;
50
51    /// 该方言是否支持 RETURNING 子句
52    fn supports_returning(&self) -> bool;
53
54    /// 生成分页 SQL
55    fn build_pagination(&self, sql: &str, page: u64, limit: u64) -> String;
56
57    /// 获取 JSON 类型的 SQL 类型名
58    fn json_type(&self) -> &'static str;
59
60    /// 生成 JSON_EXTRACT 函数调用
61    fn json_extract(&self, column: &str, path: &str) -> String;
62
63    /// 生成全文检索 SQL
64    fn full_text_search(&self, columns: &[&str], keyword: &str) -> String;
65
66    /// 将布尔表达式转换为整型存储
67    fn bool_to_int(&self, expr: &str) -> String;
68
69    /// 生成 CONCAT 函数调用
70    fn concat(&self, parts: &[&str]) -> String;
71
72    /// 该方言是否支持 IF EXISTS
73    fn supports_if_exists(&self) -> bool;
74
75    /// 该方言是否支持 IF NOT EXISTS
76    fn supports_if_not_exists(&self) -> bool;
77
78    /// 获取自增列关键字
79    fn auto_increment_keyword(&self) -> &'static str;
80
81    /// 获取最近插入 ID 的 SQL(独立可执行语句)
82    ///
83    /// 返回 `None` 表示该方言不支持以独立 SQL 获取最后插入 ID(如 Oracle 只能通过
84    /// 在 INSERT 语句末尾附加 `RETURNING ... INTO :bind` 子句的方式获取,无法独立执行)。
85    /// 调用方在拿到 `None` 时必须改用 `supports_returning()` + 在 INSERT 后追加 RETURNING。
86    fn last_insert_id_sql(&self) -> Option<&'static str>;
87
88    /// 生成 CREATE TABLE 语句
89    fn build_create_table(&self, table: &str, columns: &[ColumnDef]) -> String;
90
91    /// 生成 ALTER TABLE 语句
92    fn build_alter_table(&self, table: &str, changes: &[TableChange]) -> String;
93
94    /// 生成 DROP TABLE 语句
95    ///
96    /// 默认实现生成标准 `DROP TABLE [IF EXISTS] <table>` 语法。
97    /// 不支持 `IF EXISTS` 的方言(如 DB2)应覆盖此方法。
98    fn build_drop_table(&self, table: &str, if_exists: bool) -> String {
99        if if_exists && self.supports_if_exists() {
100            format!("DROP TABLE IF EXISTS {}", self.quote(table))
101        } else {
102            format!("DROP TABLE {}", self.quote(table))
103        }
104    }
105
106    /// P2-6:生成批量 upsert 的冲突处理子句(ON CONFLICT / ON DUPLICATE KEY UPDATE)
107    ///
108    /// 参数(均为原始列名,由各方言自行 quote):
109    /// - `conflict_columns`: 冲突检测列(唯一键/主键),用于 `ON CONFLICT (cols)` 或 `ON DUPLICATE KEY`
110    /// - `update_columns`: 冲突时需要更新的列;空切片表示更新所有 `all_columns` 中非冲突列
111    /// - `all_columns`: 本次 INSERT 的全部列名(原始未 quote),用于确定"更新所有非冲突列"的范围
112    ///
113    /// 返回 `None` 表示该方言不支持 upsert(如 ClickHouse、Db2)。
114    /// 返回 `Some(clause)` 表示完整的冲突处理子句(不含前导空格),如:
115    /// - MySQL: `ON DUPLICATE KEY UPDATE \`c1\`=VALUES(\`c1\`), \`c2\`=VALUES(\`c2\`)`
116    /// - PG/SQLite: `ON CONFLICT ("c1") DO UPDATE SET "c2"=EXCLUDED."c2"`
117    ///
118    /// **L3 实现深度**:返回的子句使用参数化占位符(`VALUES(col)` 或 `EXCLUDED.col`),
119    /// 不拼接用户值,杜绝 SQL 注入。
120    fn build_upsert_on_conflict(
121        &self,
122        conflict_columns: &[&str],
123        update_columns: &[&str],
124        all_columns: &[String],
125    ) -> Option<String> {
126        let _ = (conflict_columns, update_columns, all_columns);
127        None
128    }
129
130    /// P2-3:生成行锁子句(TASK-025/026)
131    ///
132    /// 根据 `lock_type` 生成对应的 SQL 锁子句:
133    /// - `ForUpdate`:MySQL 生成 `FOR UPDATE`,PG 生成 `FOR UPDATE`
134    /// - `Shared`:MySQL 生成 `LOCK IN SHARE MODE`,PG 生成 `FOR SHARE`
135    ///
136    /// 返回 `None` 表示该方言不支持行锁(如 SQLite)。
137    /// 返回 `Some(clause)` 表示完整的锁子句(不含前导空格)。
138    fn build_lock_clause(&self, lock_type: LockType) -> Option<String> {
139        let _ = lock_type;
140        None
141    }
142
143    /// P2-3:该方言是否支持 `FOR UPDATE` 行锁(TASK-029)
144    ///
145    /// 默认返回 `true`。不支持行锁的方言(如 SQLite、ClickHouse、DuckDB)
146    /// 应覆盖此方法返回 `false`。
147    fn supports_lock_for_update(&self) -> bool {
148        true
149    }
150
151    /// P2-3:该方言是否支持共享锁(TASK-029)
152    ///
153    /// 默认返回 `true`。不支持共享锁的方言(如 SQLite、ClickHouse、DuckDB)
154    /// 应覆盖此方法返回 `false`。
155    fn supports_lock_shared(&self) -> bool {
156        true
157    }
158
159    /// P2-4:生成 `INSERT OR IGNORE` 前缀(TASK-027)
160    ///
161    /// - MySQL:返回 `INSERT IGNORE INTO {table}`
162    /// - PostgreSQL/SQLite:返回 `INSERT OR IGNORE INTO {table}`
163    ///
164    /// 用于"存在则忽略"的插入场景,避免主键/唯一键冲突时报错。
165    fn build_insert_or_ignore_prefix(&self, table: &str) -> String {
166        format!("INSERT OR IGNORE INTO {}", self.quote(table))
167    }
168}
169
170/// P2-3:行锁类型(TASK-024)
171///
172/// 用于 `QueryBuilder::lock_for_update()` 和 `QueryBuilder::lock_shared()`。
173/// 不同数据库对共享锁的语法不同:
174/// - MySQL:`LOCK IN SHARE MODE`
175/// - PostgreSQL:`FOR SHARE`
176/// - SQLite:不支持行锁
177#[derive(Debug, Clone, Copy, PartialEq, Eq)]
178pub enum LockType {
179    /// 排他锁(`FOR UPDATE`):锁定行,其他事务无法读取或修改
180    ForUpdate,
181    /// 共享锁(`FOR SHARE` / `LOCK IN SHARE MODE`):锁定行,其他事务可读取但无法修改
182    Shared,
183}
184
185/// 建表时的列定义
186#[derive(Debug, Clone)]
187pub struct ColumnDef {
188    /// 列名
189    pub name: String,
190    /// SQL 类型字符串
191    pub sql_type: String,
192    /// 是否允许 NULL
193    pub nullable: bool,
194    /// 默认值
195    pub default: Option<String>,
196    /// 是否自增
197    pub auto_increment: bool,
198    /// 是否主键
199    pub primary_key: bool,
200}
201
202/// ALTER TABLE 的变更操作
203#[derive(Debug, Clone)]
204pub enum TableChange {
205    /// 新增列
206    AddColumn(ColumnDef),
207    /// 删除列
208    DropColumn(String),
209    /// 修改列定义
210    ModifyColumn(ColumnDef),
211    /// 新增索引
212    AddIndex(String, Vec<String>),
213    /// 删除索引
214    DropIndex(String),
215    /// 新增外键
216    AddForeignKey {
217        /// 本表列
218        columns: Vec<String>,
219        /// 引用表名
220        reference_table: String,
221        /// 引用列
222        reference_columns: Vec<String>,
223    },
224}
225
226/// MySQL 方言实现
227#[derive(Debug, Clone)]
228pub struct MySqlDialect;
229
230impl Dialect for MySqlDialect {
231    fn clone_box(&self) -> Box<dyn Dialect> {
232        Box::new(MySqlDialect)
233    }
234
235    fn db_type(&self) -> DbType {
236        DbType::MySQL
237    }
238
239    fn quote(&self, identifier: &str) -> String {
240        format!("`{}`", identifier.replace('`', "``"))
241    }
242
243    fn escape_string(&self, s: &str) -> String {
244        let mut escaped = String::with_capacity(s.len() * 2);
245        for c in s.chars() {
246            match c {
247                '\\' => escaped.push_str("\\\\"),
248                '\'' => escaped.push_str("\\'"),
249                '\0' => escaped.push_str("\\0"),
250                '\n' => escaped.push_str("\\n"),
251                '\r' => escaped.push_str("\\r"),
252                '\t' => escaped.push_str("\\t"),
253                '\x1a' => escaped.push_str("\\Z"),
254                _ => escaped.push(c),
255            }
256        }
257        escaped
258    }
259
260    fn supports_returning(&self) -> bool {
261        false
262    }
263
264    fn build_pagination(&self, sql: &str, page: u64, limit: u64) -> String {
265        // M-2 修复说明:
266        //
267        // 历史上 MySQL 支持 `SQL_CALC_FOUND_ROWS` 提示配合 `FOUND_ROWS()` 函数
268        // 在不分页情况下获取总行数,但该特性在 MySQL 8.0.17 中被弃用并在后续版本移除。
269        // 官方推荐使用独立的 `COUNT(*)` 查询。
270        //
271        // 因此本实现不使用 `SQL_CALC_FOUND_ROWS`,调用方如需总数应单独执行 COUNT 查询。
272        let offset = page.saturating_sub(1).saturating_mul(limit);
273        format!("{} LIMIT {} OFFSET {}", sql, limit, offset)
274    }
275
276    fn json_type(&self) -> &'static str {
277        "JSON"
278    }
279
280    fn json_extract(&self, column: &str, path: &str) -> String {
281        // 规范化 path:确保以 $. 开头
282        let normalized = if path.starts_with('$') {
283            path.to_string()
284        } else {
285            format!("$.{}", path)
286        };
287        format!(
288            "JSON_EXTRACT({}, '{}')",
289            column,
290            self.escape_string(&normalized)
291        )
292    }
293
294    fn full_text_search(&self, columns: &[&str], keyword: &str) -> String {
295        let cols = columns.join(", ");
296        let escaped = self.escape_string(keyword);
297        format!(
298            "MATCH({}) AGAINST('{}' IN NATURAL LANGUAGE MODE)",
299            cols, escaped
300        )
301    }
302
303    fn bool_to_int(&self, expr: &str) -> String {
304        // 将布尔表达式转换为整数(0/1)用于存储
305        format!("IF({}, 1, 0)", expr)
306    }
307
308    fn concat(&self, parts: &[&str]) -> String {
309        if parts.is_empty() {
310            return "NULL".to_string();
311        }
312        let concat_parts: Vec<String> = parts
313            .iter()
314            .map(|p| format!("CAST({} AS CHAR)", p))
315            .collect();
316        format!("CONCAT({})", concat_parts.join(", "))
317    }
318
319    fn supports_if_exists(&self) -> bool {
320        true
321    }
322
323    fn supports_if_not_exists(&self) -> bool {
324        true
325    }
326
327    fn auto_increment_keyword(&self) -> &'static str {
328        "AUTO_INCREMENT"
329    }
330
331    fn last_insert_id_sql(&self) -> Option<&'static str> {
332        Some("LAST_INSERT_ID()")
333    }
334
335    fn build_create_table(&self, table: &str, columns: &[ColumnDef]) -> String {
336        let cols: Vec<String> = columns
337            .iter()
338            .map(|col| {
339                let mut sql = format!("{} {}", self.quote(&col.name), col.sql_type);
340                if !col.nullable {
341                    sql.push_str(" NOT NULL");
342                }
343                if let Some(default) = &col.default {
344                    sql.push_str(&format!(" DEFAULT {}", default));
345                }
346                if col.auto_increment {
347                    sql.push_str(&format!(" {}", self.auto_increment_keyword()));
348                }
349                if col.primary_key {
350                    sql.push_str(" PRIMARY KEY");
351                }
352                sql
353            })
354            .collect();
355
356        format!("CREATE TABLE {} ({})", self.quote(table), cols.join(", "))
357    }
358
359    fn build_alter_table(&self, table: &str, changes: &[TableChange]) -> String {
360        let stmts: Vec<String> = changes.iter().map(|change| {
361            match change {
362                TableChange::AddColumn(col) => {
363                    let mut sql = format!("ALTER TABLE {} ADD {}", self.quote(table), self.quote(&col.name));
364                    sql.push_str(&format!(" {}", col.sql_type));
365                    if !col.nullable {
366                        sql.push_str(" NOT NULL");
367                    }
368                    if let Some(default) = &col.default {
369                        sql.push_str(&format!(" DEFAULT {}", default));
370                    }
371                    sql
372                }
373                TableChange::DropColumn(name) => {
374                    format!("ALTER TABLE {} DROP COLUMN {}", self.quote(table), self.quote(name))
375                }
376                TableChange::ModifyColumn(col) => {
377                    // MySQL 使用 MODIFY COLUMN
378                    let mut sql = format!("ALTER TABLE {} MODIFY COLUMN {} {}", self.quote(table), self.quote(&col.name), col.sql_type);
379                    if !col.nullable {
380                        sql.push_str(" NOT NULL");
381                    }
382                    if let Some(default) = &col.default {
383                        sql.push_str(&format!(" DEFAULT {}", default));
384                    }
385                    sql
386                }
387                TableChange::AddIndex(name, cols) => {
388                    format!("ALTER TABLE {} ADD INDEX {} ({})", self.quote(table), name, cols.join(", "))
389                }
390                TableChange::DropIndex(name) => {
391                    format!("ALTER TABLE {} DROP INDEX {}", self.quote(table), name)
392                }
393                TableChange::AddForeignKey { columns, reference_table, reference_columns } => {
394                    format!("ALTER TABLE {} ADD CONSTRAINT fk_{}_{} FOREIGN KEY ({}) REFERENCES {} ({})",
395                        self.quote(table),
396                        table,
397                        columns.join("_"),
398                        columns.iter().map(|c| self.quote(c)).collect::<Vec<_>>().join(", "),
399                        self.quote(reference_table),
400                        reference_columns.iter().map(|c| self.quote(c)).collect::<Vec<_>>().join(", "))
401                }
402            }
403        }).collect();
404
405        stmts.join("; ")
406    }
407
408    /// P2-6:MySQL 批量 upsert — `ON DUPLICATE KEY UPDATE col=VALUES(col), ...`
409    ///
410    /// MySQL 不需要指定冲突列(自动检测唯一键/主键冲突)。
411    /// `conflict_columns` 参数在 MySQL 方言中被忽略。
412    fn build_upsert_on_conflict(
413        &self,
414        _conflict_columns: &[&str],
415        update_columns: &[&str],
416        all_columns: &[String],
417    ) -> Option<String> {
418        // 确定要更新的列:优先使用 update_columns,为空则使用所有列(均已 quote)
419        let cols_to_update: Vec<String> = if update_columns.is_empty() {
420            all_columns.iter().map(|c| self.quote(c)).collect()
421        } else {
422            update_columns.iter().map(|c| self.quote(c)).collect()
423        };
424        if cols_to_update.is_empty() {
425            return None;
426        }
427        let set_clauses: Vec<String> = cols_to_update
428            .iter()
429            .map(|col| format!("{}=VALUES({})", col, col))
430            .collect();
431        Some(format!(
432            "ON DUPLICATE KEY UPDATE {}",
433            set_clauses.join(", ")
434        ))
435    }
436
437    fn build_lock_clause(&self, lock_type: LockType) -> Option<String> {
438        match lock_type {
439            LockType::ForUpdate => Some("FOR UPDATE".to_string()),
440            LockType::Shared => Some("LOCK IN SHARE MODE".to_string()),
441        }
442    }
443
444    fn build_insert_or_ignore_prefix(&self, table: &str) -> String {
445        format!("INSERT IGNORE INTO {}", self.quote(table))
446    }
447}
448
449/// PostgreSQL 方言实现
450#[derive(Debug, Clone)]
451pub struct PostgreSqlDialect;
452
453impl Dialect for PostgreSqlDialect {
454    fn clone_box(&self) -> Box<dyn Dialect> {
455        Box::new(PostgreSqlDialect)
456    }
457
458    fn db_type(&self) -> DbType {
459        DbType::PostgreSQL
460    }
461
462    fn quote(&self, identifier: &str) -> String {
463        format!("\"{}\"", identifier.replace('"', "\"\""))
464    }
465
466    fn escape_string(&self, s: &str) -> String {
467        // PostgreSQL 标准:使用双单引号转义单引号(standard_conforming_strings=on 默认)
468        // 反斜杠在 standard_conforming_strings=on 时不是转义字符
469        let mut escaped = String::with_capacity(s.len() * 2);
470        for c in s.chars() {
471            match c {
472                '\'' => escaped.push_str("''"),
473                _ => escaped.push(c),
474            }
475        }
476        escaped
477    }
478
479    fn supports_returning(&self) -> bool {
480        true
481    }
482
483    fn build_pagination(&self, sql: &str, page: u64, limit: u64) -> String {
484        let offset = page.saturating_sub(1).saturating_mul(limit);
485        format!("{} LIMIT {} OFFSET {}", sql, limit, offset)
486    }
487
488    fn json_type(&self) -> &'static str {
489        "JSONB"
490    }
491
492    fn json_extract(&self, column: &str, path: &str) -> String {
493        // PostgreSQL 使用 #>> 提取文本,path 以字符串数组形式
494        // 支持 $.a.b 或 a.b 格式
495        // 输出形式:column#>>'{a,b,c}'
496        // 路径组件中的特殊字符(逗号、花括号、双引号、反斜杠)需用双引号包裹并转义
497        let normalized = path.trim_start_matches("$.");
498        let parts: Vec<&str> = normalized.split('.').filter(|s| !s.is_empty()).collect();
499        let path_lit = parts
500            .iter()
501            .map(|p| {
502                // 如果包含特殊字符,用双引号包裹并转义内部双引号和反斜杠
503                let needs_quoting = p.chars().any(|c| matches!(c, ',' | '{' | '}' | '"' | '\\'));
504                if needs_quoting {
505                    let escaped = p.replace('\\', "\\\\").replace('"', "\\\"");
506                    format!("\"{}\"", escaped)
507                } else {
508                    p.to_string()
509                }
510            })
511            .collect::<Vec<_>>()
512            .join(",");
513        // 转义 SQL 字符串字面量中的单引号(PG 使用双单引号转义)
514        let path_lit_escaped = path_lit.replace('\'', "''");
515        format!("{}#>>'{{{}}}'", column, path_lit_escaped)
516    }
517
518    fn full_text_search(&self, columns: &[&str], keyword: &str) -> String {
519        let cols = columns
520            .iter()
521            .map(|c| format!("{}::text", c))
522            .collect::<Vec<_>>()
523            .join(" || ' ' || ");
524        let escaped = self.escape_string(keyword);
525        format!("to_tsvector({}) @@ to_tsquery('{}')", cols, escaped)
526    }
527
528    fn bool_to_int(&self, expr: &str) -> String {
529        format!("(CASE WHEN {} THEN 1 ELSE 0 END)", expr)
530    }
531
532    fn concat(&self, parts: &[&str]) -> String {
533        if parts.is_empty() {
534            return "NULL".to_string();
535        }
536        format!("CONCAT({})", parts.join(", "))
537    }
538
539    fn supports_if_exists(&self) -> bool {
540        true
541    }
542
543    fn supports_if_not_exists(&self) -> bool {
544        true
545    }
546
547    fn auto_increment_keyword(&self) -> &'static str {
548        "GENERATED BY DEFAULT AS IDENTITY"
549    }
550
551    fn last_insert_id_sql(&self) -> Option<&'static str> {
552        Some("lastval()")
553    }
554
555    fn build_create_table(&self, table: &str, columns: &[ColumnDef]) -> String {
556        let cols: Vec<String> = columns
557            .iter()
558            .map(|col| {
559                let mut sql = format!("{} {}", self.quote(&col.name), col.sql_type);
560                if !col.nullable {
561                    sql.push_str(" NOT NULL");
562                }
563                if let Some(default) = &col.default {
564                    sql.push_str(&format!(" DEFAULT {}", default));
565                }
566                if col.primary_key {
567                    sql.push_str(" PRIMARY KEY");
568                }
569                sql
570            })
571            .collect();
572
573        format!("CREATE TABLE {} ({})", self.quote(table), cols.join(", "))
574    }
575
576    fn build_alter_table(&self, table: &str, changes: &[TableChange]) -> String {
577        let stmts: Vec<String> = changes.iter().map(|change| {
578            match change {
579                TableChange::AddColumn(col) => {
580                    let mut sql = format!("ALTER TABLE {} ADD COLUMN {} {}", self.quote(table), self.quote(&col.name), col.sql_type);
581                    if !col.nullable {
582                        sql.push_str(" NOT NULL");
583                    }
584                    if let Some(default) = &col.default {
585                        sql.push_str(&format!(" DEFAULT {}", default));
586                    }
587                    sql
588                }
589                TableChange::DropColumn(name) => {
590                    format!("ALTER TABLE {} DROP COLUMN {}", self.quote(table), self.quote(name))
591                }
592                TableChange::ModifyColumn(col) => {
593                    // PostgreSQL 使用 ALTER COLUMN TYPE
594                    let mut sql = format!("ALTER TABLE {} ALTER COLUMN {} TYPE {}", self.quote(table), self.quote(&col.name), col.sql_type);
595                    if !col.nullable {
596                        sql.push_str(&format!(", ALTER COLUMN {} SET NOT NULL", self.quote(&col.name)));
597                    }
598                    if let Some(default) = &col.default {
599                        sql.push_str(&format!(", ALTER COLUMN {} SET DEFAULT {}", self.quote(&col.name), default));
600                    }
601                    sql
602                }
603                TableChange::AddIndex(name, cols) => {
604                    format!("CREATE INDEX {} ON {} ({})", name, self.quote(table), cols.join(", "))
605                }
606                TableChange::DropIndex(name) => {
607                    format!("DROP INDEX {}", name)
608                }
609                TableChange::AddForeignKey { columns, reference_table, reference_columns } => {
610                    format!("ALTER TABLE {} ADD CONSTRAINT fk_{}_{} FOREIGN KEY ({}) REFERENCES {} ({})",
611                        self.quote(table),
612                        table,
613                        columns.join("_"),
614                        columns.iter().map(|c| self.quote(c)).collect::<Vec<_>>().join(", "),
615                        self.quote(reference_table),
616                        reference_columns.iter().map(|c| self.quote(c)).collect::<Vec<_>>().join(", "))
617                }
618            }
619        }).collect();
620
621        stmts.join("; ")
622    }
623
624    /// P2-6:PostgreSQL 批量 upsert — `ON CONFLICT (cols) DO UPDATE SET col=EXCLUDED.col, ...`
625    ///
626    /// PostgreSQL 要求显式指定冲突列(主键或唯一键)。
627    fn build_upsert_on_conflict(
628        &self,
629        conflict_columns: &[&str],
630        update_columns: &[&str],
631        all_columns: &[String],
632    ) -> Option<String> {
633        if conflict_columns.is_empty() {
634            return None;
635        }
636        let conflict_cols: Vec<String> = conflict_columns.iter().map(|c| self.quote(c)).collect();
637        // 确定要更新的列:优先使用 update_columns,为空则使用所有非冲突列
638        let conflict_set: std::collections::HashSet<&str> =
639            conflict_columns.iter().copied().collect();
640        let cols_to_update: Vec<String> = if update_columns.is_empty() {
641            all_columns
642                .iter()
643                .filter(|c| !conflict_set.contains(c.as_str()))
644                .map(|c| self.quote(c))
645                .collect()
646        } else {
647            update_columns.iter().map(|c| self.quote(c)).collect()
648        };
649        if cols_to_update.is_empty() {
650            // 没有要更新的列,使用 DO NOTHING
651            return Some(format!(
652                "ON CONFLICT ({}) DO NOTHING",
653                conflict_cols.join(", ")
654            ));
655        }
656        let set_clauses: Vec<String> = cols_to_update
657            .iter()
658            .map(|col| format!("{}=EXCLUDED.{}", col, col))
659            .collect();
660        Some(format!(
661            "ON CONFLICT ({}) DO UPDATE SET {}",
662            conflict_cols.join(", "),
663            set_clauses.join(", ")
664        ))
665    }
666
667    fn build_lock_clause(&self, lock_type: LockType) -> Option<String> {
668        match lock_type {
669            LockType::ForUpdate => Some("FOR UPDATE".to_string()),
670            LockType::Shared => Some("FOR SHARE".to_string()),
671        }
672    }
673}
674
675/// SQLite 方言实现
676#[derive(Debug, Clone)]
677pub struct SqliteDialect;
678
679impl Dialect for SqliteDialect {
680    fn clone_box(&self) -> Box<dyn Dialect> {
681        Box::new(SqliteDialect)
682    }
683
684    fn db_type(&self) -> DbType {
685        DbType::Sqlite
686    }
687
688    fn quote(&self, identifier: &str) -> String {
689        format!("\"{}\"", identifier.replace('"', "\"\""))
690    }
691
692    fn escape_string(&self, s: &str) -> String {
693        let mut escaped = String::with_capacity(s.len() * 2);
694        for c in s.chars() {
695            match c {
696                '\'' => escaped.push_str("''"),
697                _ => escaped.push(c),
698            }
699        }
700        escaped
701    }
702
703    fn supports_returning(&self) -> bool {
704        true
705    }
706
707    fn build_pagination(&self, sql: &str, page: u64, limit: u64) -> String {
708        let offset = page.saturating_sub(1).saturating_mul(limit);
709        format!("{} LIMIT {} OFFSET {}", sql, limit, offset)
710    }
711
712    fn json_type(&self) -> &'static str {
713        "TEXT"
714    }
715
716    fn json_extract(&self, column: &str, path: &str) -> String {
717        // SQLite 使用 json_extract(column, '$.path')
718        let normalized = if path.starts_with('$') {
719            path.to_string()
720        } else {
721            format!("$.{}", path)
722        };
723        format!(
724            "json_extract({}, '{}')",
725            column,
726            self.escape_string(&normalized)
727        )
728    }
729
730    fn full_text_search(&self, columns: &[&str], keyword: &str) -> String {
731        // SQLite FTS 的 MATCH 操作符必须作用于 FTS 虚拟表本身(`tbl MATCH 'query'`),
732        // 不能用于单个列;本接口仅传入列名,没有 FTS 表名,因此降级使用 LIKE。
733        // 这样既能避免生成错误的 MATCH 语法,又能在普通表上工作(不依赖 FTS 索引)。
734        if columns.is_empty() {
735            return "0".to_string();
736        }
737        let escaped = self.escape_string(keyword);
738        columns
739            .iter()
740            .map(|c| format!("{} LIKE '%{}%'", c.trim(), escaped))
741            .collect::<Vec<_>>()
742            .join(" OR ")
743    }
744
745    fn bool_to_int(&self, expr: &str) -> String {
746        expr.to_string()
747    }
748
749    fn concat(&self, parts: &[&str]) -> String {
750        if parts.is_empty() {
751            return "NULL".to_string();
752        }
753        // SQLite 的 || 操作符在任意参数为 NULL 时整体结果为 NULL,
754        // 需要先用 COALESCE 把每个参数替换为空串,才能保证拼接结果非 NULL。
755        let coalesced: Vec<String> = parts
756            .iter()
757            .map(|p| format!("COALESCE({}, '')", p))
758            .collect();
759        coalesced.join(" || ")
760    }
761
762    fn supports_if_exists(&self) -> bool {
763        true
764    }
765
766    fn supports_if_not_exists(&self) -> bool {
767        true
768    }
769
770    fn auto_increment_keyword(&self) -> &'static str {
771        "AUTOINCREMENT"
772    }
773
774    fn last_insert_id_sql(&self) -> Option<&'static str> {
775        Some("last_insert_rowid()")
776    }
777
778    fn build_create_table(&self, table: &str, columns: &[ColumnDef]) -> String {
779        let cols: Vec<String> = columns
780            .iter()
781            .map(|col| {
782                let mut sql = format!("{} {}", self.quote(&col.name), col.sql_type);
783                if !col.nullable {
784                    sql.push_str(" NOT NULL");
785                }
786                if let Some(default) = &col.default {
787                    sql.push_str(&format!(" DEFAULT {}", default));
788                }
789                if col.auto_increment {
790                    sql.push_str(" PRIMARY KEY AUTOINCREMENT");
791                } else if col.primary_key {
792                    sql.push_str(" PRIMARY KEY");
793                }
794                sql
795            })
796            .collect();
797
798        format!("CREATE TABLE {} ({})", self.quote(table), cols.join(", "))
799    }
800
801    fn build_alter_table(&self, table: &str, changes: &[TableChange]) -> String {
802        // SQLite 支持的 ALTER 操作:ADD COLUMN, RENAME COLUMN, DROP COLUMN (3.35+), RENAME TABLE
803        // 不支持:MODIFY COLUMN, ADD INDEX (需用 CREATE INDEX), ADD FOREIGN KEY (语法层面不支持)
804        let stmts: Vec<String> = changes
805            .iter()
806            .map(|change| {
807                match change {
808                    TableChange::AddColumn(col) => {
809                        let mut sql = format!(
810                            "ALTER TABLE {} ADD COLUMN {} {}",
811                            self.quote(table),
812                            self.quote(&col.name),
813                            col.sql_type
814                        );
815                        if !col.nullable {
816                            sql.push_str(" NOT NULL");
817                        }
818                        if let Some(default) = &col.default {
819                            sql.push_str(&format!(" DEFAULT {}", default));
820                        }
821                        sql
822                    }
823                    TableChange::DropColumn(name) => {
824                        // SQLite 3.35.0+ 支持 DROP COLUMN
825                        format!(
826                            "ALTER TABLE {} DROP COLUMN {}",
827                            self.quote(table),
828                            self.quote(name)
829                        )
830                    }
831                    TableChange::ModifyColumn(col) => {
832                        // SQLite 不直接支持 MODIFY COLUMN,需要用 12 步流程
833                        // 这里生成注释 SQL,提示用户需手动处理
834                        format!(
835                            "-- SQLite 不支持 MODIFY COLUMN({} {}),需重建表",
836                            col.name, col.sql_type
837                        )
838                    }
839                    TableChange::AddIndex(name, cols) => {
840                        format!(
841                            "CREATE INDEX {} ON {} ({})",
842                            name,
843                            self.quote(table),
844                            cols.join(", ")
845                        )
846                    }
847                    TableChange::DropIndex(name) => {
848                        format!("DROP INDEX {}", name)
849                    }
850                    TableChange::AddForeignKey {
851                        columns,
852                        reference_table,
853                        reference_columns: _,
854                    } => {
855                        // SQLite 不支持 ALTER TABLE ADD FOREIGN KEY,需重建表
856                        format!(
857                            "-- SQLite 不支持 ADD FOREIGN KEY({} -> {}),需重建表",
858                            columns.join(","),
859                            reference_table
860                        )
861                    }
862                }
863            })
864            .collect();
865
866        stmts.join("; ")
867    }
868
869    /// P2-6:SQLite 批量 upsert — `ON CONFLICT (cols) DO UPDATE SET col=EXCLUDED.col, ...`
870    ///
871    /// SQLite 语法与 PostgreSQL 一致(SQLite 3.24.0+ 支持 ON CONFLICT 子句)。
872    fn build_upsert_on_conflict(
873        &self,
874        conflict_columns: &[&str],
875        update_columns: &[&str],
876        all_columns: &[String],
877    ) -> Option<String> {
878        if conflict_columns.is_empty() {
879            return None;
880        }
881        let conflict_cols: Vec<String> = conflict_columns.iter().map(|c| self.quote(c)).collect();
882        let conflict_set: std::collections::HashSet<&str> =
883            conflict_columns.iter().copied().collect();
884        let cols_to_update: Vec<String> = if update_columns.is_empty() {
885            all_columns
886                .iter()
887                .filter(|c| !conflict_set.contains(c.as_str()))
888                .map(|c| self.quote(c))
889                .collect()
890        } else {
891            update_columns.iter().map(|c| self.quote(c)).collect()
892        };
893        if cols_to_update.is_empty() {
894            return Some(format!(
895                "ON CONFLICT ({}) DO NOTHING",
896                conflict_cols.join(", ")
897            ));
898        }
899        let set_clauses: Vec<String> = cols_to_update
900            .iter()
901            .map(|col| format!("{}=EXCLUDED.{}", col, col))
902            .collect();
903        Some(format!(
904            "ON CONFLICT ({}) DO UPDATE SET {}",
905            conflict_cols.join(", "),
906            set_clauses.join(", ")
907        ))
908    }
909
910    fn supports_lock_for_update(&self) -> bool {
911        false
912    }
913
914    fn supports_lock_shared(&self) -> bool {
915        false
916    }
917}
918
919/// 将通用 SQL 类型映射为 Oracle 23ai 类型
920///
921/// Oracle 类型与常见类型的对应关系:
922/// - BIGINT → NUMBER(19)
923/// - INT/INTEGER → NUMBER(10)
924/// - VARCHAR(n) → VARCHAR2(n)
925/// - TEXT 系列 → CLOB
926/// - BOOLEAN/BOOL → NUMBER(1)
927fn map_to_oracle_type(sql_type: &str) -> String {
928    let upper = sql_type.to_uppercase();
929    let trimmed = upper.trim();
930
931    if trimmed.starts_with("BIGINT") {
932        sql_type.replacen("BIGINT", "NUMBER(19)", 1)
933    } else if trimmed.starts_with("VARCHAR2") {
934        sql_type.to_string()
935    } else if trimmed.starts_with("VARCHAR") {
936        sql_type.replacen("VARCHAR", "VARCHAR2", 1)
937    } else if matches!(trimmed, "TEXT" | "MEDIUMTEXT" | "LONGTEXT" | "TINYTEXT") {
938        "CLOB".to_string()
939    } else if matches!(trimmed, "BOOLEAN" | "BOOL") {
940        "NUMBER(1)".to_string()
941    } else if trimmed == "INTEGER" {
942        "NUMBER(10)".to_string()
943    } else if trimmed.starts_with("INT") {
944        sql_type.replacen("INT", "NUMBER(10)", 1)
945    } else {
946        sql_type.to_string()
947    }
948}
949
950/// Oracle 方言实现(Oracle 23ai)
951#[derive(Debug, Clone)]
952pub struct OracleDialect;
953
954impl Dialect for OracleDialect {
955    fn clone_box(&self) -> Box<dyn Dialect> {
956        Box::new(OracleDialect)
957    }
958
959    fn db_type(&self) -> DbType {
960        DbType::Oracle
961    }
962
963    fn quote(&self, identifier: &str) -> String {
964        // Oracle 标准使用双引号包裹标识符,内部双引号双写
965        format!("\"{}\"", identifier.replace('"', "\"\""))
966    }
967
968    fn escape_string(&self, s: &str) -> String {
969        // Oracle 标准转义:单引号双写('O''Brien'),反斜杠不转义
970        let mut escaped = String::with_capacity(s.len() * 2);
971        for c in s.chars() {
972            match c {
973                '\'' => escaped.push_str("''"),
974                _ => escaped.push(c),
975            }
976        }
977        escaped
978    }
979
980    fn supports_returning(&self) -> bool {
981        // Oracle 12c+ 支持 RETURNING,23ai 当然支持
982        true
983    }
984
985    fn build_pagination(&self, sql: &str, page: u64, limit: u64) -> String {
986        // Oracle 12c+ 使用 OFFSET/FETCH NEXT 语法
987        // 与其他方言保持一致:page=1 为第一页(offset=0)
988        let offset = page.saturating_sub(1).saturating_mul(limit);
989        format!(
990            "{} OFFSET {} ROWS FETCH NEXT {} ROWS ONLY",
991            sql, offset, limit
992        )
993    }
994
995    fn json_type(&self) -> &'static str {
996        // Oracle 21+ 原生支持 JSON 类型,23ai 完整支持
997        "JSON"
998    }
999
1000    fn json_extract(&self, column: &str, path: &str) -> String {
1001        // Oracle 使用 JSON_VALUE 提取标量值,path 需以 $. 开头
1002        let normalized = if path.starts_with('$') {
1003            path.to_string()
1004        } else {
1005            format!("$.{}", path)
1006        };
1007        format!(
1008            "JSON_VALUE({}, '{}')",
1009            column,
1010            self.escape_string(&normalized)
1011        )
1012    }
1013
1014    fn full_text_search(&self, columns: &[&str], keyword: &str) -> String {
1015        // Oracle 使用 CONTAINS 函数(需要 CONTEXT 索引)
1016        // CONTAINS(column, keyword, 1) > 0
1017        if columns.is_empty() {
1018            return "0".to_string();
1019        }
1020        let escaped = self.escape_string(keyword);
1021        let parts: Vec<String> = columns
1022            .iter()
1023            .map(|c| format!("CONTAINS({}, '{}', 1) > 0", c, escaped))
1024            .collect();
1025        parts.join(" OR ")
1026    }
1027
1028    fn bool_to_int(&self, expr: &str) -> String {
1029        // Oracle 没有原生 BOOL 类型,使用 CASE WHEN 转换为 0/1
1030        format!("(CASE WHEN {} THEN 1 ELSE 0 END)", expr)
1031    }
1032
1033    fn concat(&self, parts: &[&str]) -> String {
1034        // Oracle 使用 || 操作符进行字符串拼接
1035        if parts.is_empty() {
1036            return "NULL".to_string();
1037        }
1038        parts.join(" || ")
1039    }
1040
1041    fn supports_if_exists(&self) -> bool {
1042        // Oracle 23ai 支持 DROP TABLE IF EXISTS
1043        true
1044    }
1045
1046    fn supports_if_not_exists(&self) -> bool {
1047        // Oracle 23ai 支持 CREATE TABLE IF NOT EXISTS
1048        true
1049    }
1050
1051    fn auto_increment_keyword(&self) -> &'static str {
1052        // Oracle 12c+ 使用 IDENTITY 列
1053        "GENERATED BY DEFAULT AS IDENTITY"
1054    }
1055
1056    fn last_insert_id_sql(&self) -> Option<&'static str> {
1057        // Oracle 没有独立可执行的"获取最后插入 ID"语句。
1058        // `RETURNING {pk} INTO :bind` 是 PL/SQL 子句,必须附加在 INSERT 之后,
1059        // 不能作为独立 SQL 执行。调用方应改用 `supports_returning()` 在 INSERT
1060        // 末尾追加 RETURNING 子句获取自增值。
1061        None
1062    }
1063
1064    fn build_create_table(&self, table: &str, columns: &[ColumnDef]) -> String {
1065        let cols: Vec<String> = columns
1066            .iter()
1067            .map(|col| {
1068                let oracle_type = map_to_oracle_type(&col.sql_type);
1069                let mut sql = format!("{} {}", self.quote(&col.name), oracle_type);
1070                // Oracle IDENTITY 列隐式 NOT NULL,不允许显式 NOT NULL(ORA-03076)
1071                if !col.nullable && !col.auto_increment {
1072                    sql.push_str(" NOT NULL");
1073                }
1074                if let Some(default) = &col.default {
1075                    sql.push_str(&format!(" DEFAULT {}", default));
1076                }
1077                if col.auto_increment {
1078                    sql.push_str(&format!(" {}", self.auto_increment_keyword()));
1079                }
1080                if col.primary_key {
1081                    sql.push_str(" PRIMARY KEY");
1082                }
1083                sql
1084            })
1085            .collect();
1086
1087        format!("CREATE TABLE {} ({})", self.quote(table), cols.join(", "))
1088    }
1089
1090    fn build_alter_table(&self, table: &str, changes: &[TableChange]) -> String {
1091        let stmts: Vec<String> = changes
1092            .iter()
1093            .map(|change| match change {
1094                TableChange::AddColumn(col) => {
1095                    let oracle_type = map_to_oracle_type(&col.sql_type);
1096                    let mut sql = format!(
1097                        "ALTER TABLE {} ADD {} {}",
1098                        self.quote(table),
1099                        self.quote(&col.name),
1100                        oracle_type
1101                    );
1102                    if !col.nullable {
1103                        sql.push_str(" NOT NULL");
1104                    }
1105                    if let Some(default) = &col.default {
1106                        sql.push_str(&format!(" DEFAULT {}", default));
1107                    }
1108                    sql
1109                }
1110                TableChange::DropColumn(name) => {
1111                    format!(
1112                        "ALTER TABLE {} DROP COLUMN {}",
1113                        self.quote(table),
1114                        self.quote(name)
1115                    )
1116                }
1117                TableChange::ModifyColumn(col) => {
1118                    // Oracle 使用 MODIFY 关键字(不需 COLUMN)
1119                    let oracle_type = map_to_oracle_type(&col.sql_type);
1120                    let mut sql = format!(
1121                        "ALTER TABLE {} MODIFY {} {}",
1122                        self.quote(table),
1123                        self.quote(&col.name),
1124                        oracle_type
1125                    );
1126                    if !col.nullable {
1127                        sql.push_str(" NOT NULL");
1128                    }
1129                    if let Some(default) = &col.default {
1130                        sql.push_str(&format!(" DEFAULT {}", default));
1131                    }
1132                    sql
1133                }
1134                TableChange::AddIndex(name, cols) => {
1135                    format!(
1136                        "CREATE INDEX {} ON {} ({})",
1137                        name,
1138                        self.quote(table),
1139                        cols.join(", ")
1140                    )
1141                }
1142                TableChange::DropIndex(name) => {
1143                    format!("DROP INDEX {}", name)
1144                }
1145                TableChange::AddForeignKey {
1146                    columns,
1147                    reference_table,
1148                    reference_columns,
1149                } => {
1150                    format!(
1151                        "ALTER TABLE {} ADD CONSTRAINT fk_{}_{} FOREIGN KEY ({}) REFERENCES {} ({})",
1152                        self.quote(table),
1153                        table,
1154                        columns.join("_"),
1155                        columns.iter().map(|c| self.quote(c)).collect::<Vec<_>>().join(", "),
1156                        self.quote(reference_table),
1157                        reference_columns.iter().map(|c| self.quote(c)).collect::<Vec<_>>().join(", ")
1158                    )
1159                }
1160            })
1161            .collect();
1162
1163        stmts.join("; ")
1164    }
1165}
1166
1167/// 将通用 SQL 类型映射为 SQL Server 类型
1168///
1169/// - BIGINT → BIGINT(保持)
1170/// - INT/INTEGER → INT
1171/// - VARCHAR(n) → NVARCHAR(n)(统一使用 Unicode)
1172/// - TEXT 系列 → NVARCHAR(MAX)
1173/// - BOOLEAN/BOOL → BIT
1174/// - 其他类型保持不变
1175fn map_to_sqlserver_type(sql_type: &str) -> String {
1176    let upper = sql_type.to_uppercase();
1177    let trimmed = upper.trim();
1178
1179    if trimmed.starts_with("BIGINT") {
1180        sql_type.to_string()
1181    } else if matches!(trimmed, "INT" | "INTEGER") {
1182        "INT".to_string()
1183    } else if trimmed.starts_with("NVARCHAR") {
1184        sql_type.to_string()
1185    } else if trimmed.starts_with("VARCHAR") {
1186        sql_type.replacen("VARCHAR", "NVARCHAR", 1)
1187    } else if matches!(trimmed, "TEXT" | "MEDIUMTEXT" | "LONGTEXT" | "TINYTEXT") {
1188        "NVARCHAR(MAX)".to_string()
1189    } else if matches!(trimmed, "BOOLEAN" | "BOOL") {
1190        "BIT".to_string()
1191    } else {
1192        sql_type.to_string()
1193    }
1194}
1195
1196/// SQL Server 方言实现(SQL Server 2012+ / T-SQL)
1197#[derive(Debug, Clone)]
1198pub struct SqlServerDialect;
1199
1200impl Dialect for SqlServerDialect {
1201    fn clone_box(&self) -> Box<dyn Dialect> {
1202        Box::new(SqlServerDialect)
1203    }
1204
1205    fn db_type(&self) -> DbType {
1206        DbType::SqlServer
1207    }
1208
1209    fn quote(&self, identifier: &str) -> String {
1210        // SQL Server 使用 [name] 包裹标识符,内部 ] 双写为 ]]
1211        format!("[{}]", identifier.replace(']', "]]"))
1212    }
1213
1214    fn escape_string(&self, s: &str) -> String {
1215        // SQL Server 标准:单引号双写('O''Brien'),反斜杠不转义
1216        let mut escaped = String::with_capacity(s.len() * 2);
1217        for c in s.chars() {
1218            match c {
1219                '\'' => escaped.push_str("''"),
1220                _ => escaped.push(c),
1221            }
1222        }
1223        escaped
1224    }
1225
1226    fn supports_returning(&self) -> bool {
1227        // SQL Server 使用 OUTPUT 子句,语义上等价于 RETURNING
1228        true
1229    }
1230
1231    fn build_pagination(&self, sql: &str, page: u64, limit: u64) -> String {
1232        // SQL Server 2012+ 使用 OFFSET ... ROWS FETCH NEXT ... ROWS ONLY
1233        let offset = page.saturating_sub(1).saturating_mul(limit);
1234        format!(
1235            "{} OFFSET {} ROWS FETCH NEXT {} ROWS ONLY",
1236            sql, offset, limit
1237        )
1238    }
1239
1240    fn json_type(&self) -> &'static str {
1241        // SQL Server 2016+ 使用 NVARCHAR(MAX) 存储 JSON
1242        "NVARCHAR(MAX)"
1243    }
1244
1245    fn json_extract(&self, column: &str, path: &str) -> String {
1246        // SQL Server 2016+ 使用 JSON_VALUE 提取标量
1247        let normalized = if path.starts_with('$') {
1248            path.to_string()
1249        } else {
1250            format!("$.{}", path)
1251        };
1252        format!(
1253            "JSON_VALUE({}, '{}')",
1254            column,
1255            self.escape_string(&normalized)
1256        )
1257    }
1258
1259    fn full_text_search(&self, columns: &[&str], keyword: &str) -> String {
1260        // SQL Server 使用 CONTAINS(需要 FULLTEXT 索引)
1261        if columns.is_empty() {
1262            return "0".to_string();
1263        }
1264        let escaped = self.escape_string(keyword);
1265        let cols = columns.join(", ");
1266        format!("CONTAINS({}, '{}')", cols, escaped)
1267    }
1268
1269    fn bool_to_int(&self, expr: &str) -> String {
1270        // SQL Server 使用 BIT 类型,CASE WHEN 转换为 0/1
1271        format!("(CASE WHEN {} THEN 1 ELSE 0 END)", expr)
1272    }
1273
1274    fn concat(&self, parts: &[&str]) -> String {
1275        if parts.is_empty() {
1276            return "NULL".to_string();
1277        }
1278        format!("CONCAT({})", parts.join(", "))
1279    }
1280
1281    fn supports_if_exists(&self) -> bool {
1282        // SQL Server 2016+ 支持 IF EXISTS
1283        true
1284    }
1285
1286    fn supports_if_not_exists(&self) -> bool {
1287        // SQL Server 2016+ 支持 IF NOT EXISTS
1288        true
1289    }
1290
1291    fn auto_increment_keyword(&self) -> &'static str {
1292        // SQL Server 使用 IDENTITY(1,1) 列属性
1293        "IDENTITY(1,1)"
1294    }
1295
1296    fn last_insert_id_sql(&self) -> Option<&'static str> {
1297        // SCOPE_IDENTITY() 返回当前作用域内最后生成的标识值
1298        Some("SCOPE_IDENTITY()")
1299    }
1300
1301    fn build_create_table(&self, table: &str, columns: &[ColumnDef]) -> String {
1302        let cols: Vec<String> = columns
1303            .iter()
1304            .map(|col| {
1305                let sqlserver_type = map_to_sqlserver_type(&col.sql_type);
1306                let mut sql = format!("{} {}", self.quote(&col.name), sqlserver_type);
1307                if !col.nullable {
1308                    sql.push_str(" NOT NULL");
1309                }
1310                if let Some(default) = &col.default {
1311                    sql.push_str(&format!(" DEFAULT {}", default));
1312                }
1313                if col.auto_increment {
1314                    sql.push_str(&format!(" {}", self.auto_increment_keyword()));
1315                }
1316                if col.primary_key {
1317                    sql.push_str(" PRIMARY KEY");
1318                }
1319                sql
1320            })
1321            .collect();
1322
1323        format!("CREATE TABLE {} ({})", self.quote(table), cols.join(", "))
1324    }
1325
1326    fn build_alter_table(&self, table: &str, changes: &[TableChange]) -> String {
1327        let stmts: Vec<String> = changes
1328            .iter()
1329            .map(|change| match change {
1330                TableChange::AddColumn(col) => {
1331                    let sqlserver_type = map_to_sqlserver_type(&col.sql_type);
1332                    let mut sql = format!(
1333                        "ALTER TABLE {} ADD {} {}",
1334                        self.quote(table),
1335                        self.quote(&col.name),
1336                        sqlserver_type
1337                    );
1338                    if !col.nullable {
1339                        sql.push_str(" NOT NULL");
1340                    }
1341                    if let Some(default) = &col.default {
1342                        sql.push_str(&format!(" DEFAULT {}", default));
1343                    }
1344                    sql
1345                }
1346                TableChange::DropColumn(name) => {
1347                    format!(
1348                        "ALTER TABLE {} DROP COLUMN {}",
1349                        self.quote(table),
1350                        self.quote(name)
1351                    )
1352                }
1353                TableChange::ModifyColumn(col) => {
1354                    // SQL Server 使用 ALTER COLUMN(不是 MODIFY)
1355                    let sqlserver_type = map_to_sqlserver_type(&col.sql_type);
1356                    let mut sql = format!(
1357                        "ALTER TABLE {} ALTER COLUMN {} {}",
1358                        self.quote(table),
1359                        self.quote(&col.name),
1360                        sqlserver_type
1361                    );
1362                    if !col.nullable {
1363                        sql.push_str(" NOT NULL");
1364                    }
1365                    if let Some(default) = &col.default {
1366                        sql.push_str(&format!(" DEFAULT {}", default));
1367                    }
1368                    sql
1369                }
1370                TableChange::AddIndex(name, cols) => {
1371                    format!(
1372                        "CREATE INDEX {} ON {} ({})",
1373                        name,
1374                        self.quote(table),
1375                        cols.join(", ")
1376                    )
1377                }
1378                TableChange::DropIndex(name) => {
1379                    // SQL Server 的 DROP INDEX 必须指定表名
1380                    format!("DROP INDEX {} ON {}", name, self.quote(table))
1381                }
1382                TableChange::AddForeignKey {
1383                    columns,
1384                    reference_table,
1385                    reference_columns,
1386                } => {
1387                    format!(
1388                        "ALTER TABLE {} ADD CONSTRAINT fk_{}_{} FOREIGN KEY ({}) REFERENCES {} ({})",
1389                        self.quote(table),
1390                        table,
1391                        columns.join("_"),
1392                        columns.iter().map(|c| self.quote(c)).collect::<Vec<_>>().join(", "),
1393                        self.quote(reference_table),
1394                        reference_columns.iter().map(|c| self.quote(c)).collect::<Vec<_>>().join(", ")
1395                    )
1396                }
1397            })
1398            .collect();
1399
1400        stmts.join("; ")
1401    }
1402
1403    fn build_insert_or_ignore_prefix(&self, table: &str) -> String {
1404        // SQL Server 不支持 `INSERT OR IGNORE` 语法,也没有等价的前缀写法:
1405        // - MERGE 需要完整的 USING/WHEN NOT MATCHED 结构,无法以"前缀"形式表达
1406        // - `IF NOT EXISTS (...) INSERT ...` 是语句级控制流,同样无法作为前缀
1407        // 因此回退为普通 INSERT,应用层可通过唯一索引 + 捕获重复键冲突
1408        // (SQLSTATE 2601/2627)或 MERGE 语句实现幂等插入。
1409        format!("INSERT INTO {}", self.quote(table))
1410    }
1411}
1412
1413// 架构说明:build_create_table / build_alter_table / build_drop_table 在四个方言中
1414// 存在部分重复代码。当前选择保持各方言独立实现以确保 SQL 方言差异的显式性,
1415// 避免过度抽象导致维护复杂度上升。如需重构,可抽出公共构建器(参见 dialect 重构 RFC)。
1416
1417// ============================================================================
1418// 兼容方言(基于现有方言委派实现)
1419//
1420// 以下方言在 SQL 语法上与某个基础方言完全兼容,仅在 db_type() 上有区别。
1421// 使用宏减少重复代码,保持维护性。
1422// ============================================================================
1423
1424/// 将方言实现委派给基础方言的宏
1425///
1426/// `$wrapper`:新方言结构体名
1427/// `$base`:基础方言结构体名(如 MySqlDialect)
1428/// `$db_type`:返回的 DbType 变体
1429macro_rules! delegate_dialect_to {
1430    ($wrapper:ident, $base:ident, $db_type:expr) => {
1431        /// 兼容方言(委派给基础方言实现)
1432        #[derive(Debug, Clone)]
1433        pub struct $wrapper;
1434
1435        impl Dialect for $wrapper {
1436            fn clone_box(&self) -> Box<dyn Dialect> {
1437                Box::new($wrapper)
1438            }
1439
1440            fn db_type(&self) -> DbType {
1441                $db_type
1442            }
1443            fn quote(&self, identifier: &str) -> String {
1444                $base.quote(identifier)
1445            }
1446            fn escape_string(&self, s: &str) -> String {
1447                $base.escape_string(s)
1448            }
1449            fn supports_returning(&self) -> bool {
1450                $base.supports_returning()
1451            }
1452            fn build_pagination(&self, sql: &str, page: u64, limit: u64) -> String {
1453                $base.build_pagination(sql, page, limit)
1454            }
1455            fn json_type(&self) -> &'static str {
1456                $base.json_type()
1457            }
1458            fn json_extract(&self, column: &str, path: &str) -> String {
1459                $base.json_extract(column, path)
1460            }
1461            fn full_text_search(&self, columns: &[&str], keyword: &str) -> String {
1462                $base.full_text_search(columns, keyword)
1463            }
1464            fn bool_to_int(&self, expr: &str) -> String {
1465                $base.bool_to_int(expr)
1466            }
1467            fn concat(&self, parts: &[&str]) -> String {
1468                $base.concat(parts)
1469            }
1470            fn supports_if_exists(&self) -> bool {
1471                $base.supports_if_exists()
1472            }
1473            fn supports_if_not_exists(&self) -> bool {
1474                $base.supports_if_not_exists()
1475            }
1476            fn auto_increment_keyword(&self) -> &'static str {
1477                $base.auto_increment_keyword()
1478            }
1479            fn last_insert_id_sql(&self) -> Option<&'static str> {
1480                $base.last_insert_id_sql()
1481            }
1482            fn build_create_table(&self, table: &str, columns: &[ColumnDef]) -> String {
1483                $base.build_create_table(table, columns)
1484            }
1485            fn build_alter_table(&self, table: &str, changes: &[TableChange]) -> String {
1486                $base.build_alter_table(table, changes)
1487            }
1488            fn build_drop_table(&self, table: &str, if_exists: bool) -> String {
1489                $base.build_drop_table(table, if_exists)
1490            }
1491        }
1492    };
1493}
1494
1495// MariaDB:MySQL 兼容方言
1496delegate_dialect_to!(MariaDbDialect, MySqlDialect, DbType::MariaDB);
1497
1498// TiDB:MySQL 兼容分布式数据库
1499delegate_dialect_to!(TiDbDialect, MySqlDialect, DbType::TiDB);
1500
1501// KingbaseES:人大金仓,PostgreSQL 兼容方言
1502delegate_dialect_to!(KingbaseDialect, PostgreSqlDialect, DbType::Kingbase);
1503
1504// PolarDB:阿里云,PostgreSQL 兼容(PG 版本)
1505delegate_dialect_to!(PolarDbDialect, PostgreSqlDialect, DbType::PolarDB);
1506
1507// GaussDB:华为云,PostgreSQL 兼容分布式数据库
1508delegate_dialect_to!(GaussDbDialect, PostgreSqlDialect, DbType::GaussDB);
1509
1510// Dameng:达梦 DM8,Oracle 兼容方言
1511delegate_dialect_to!(DamengDialect, OracleDialect, DbType::Dameng);
1512
1513// Sybase ASE:与 SQL Server T-SQL 高度兼容
1514delegate_dialect_to!(SybaseDialect, SqlServerDialect, DbType::Sybase);
1515
1516// GBase 8s:南大通用,Informix 兼容方言,SQL 语法接近 T-SQL
1517delegate_dialect_to!(GBaseDialect, SqlServerDialect, DbType::GBase);
1518
1519// CockroachDB:PostgreSQL 兼容分布式数据库(v3.5.0 新增)
1520//
1521// CockroachDB 是 Google Spanner 风格的分布式 SQL 数据库,完全兼容 PostgreSQL 协议:
1522// - 使用 PG wire protocol,可直接用 sqlx::Postgres 驱动连接
1523// - 支持 PG 方言的 SQL 语法(SERIAL/RETURNING/LIMIT OFFSET 等)
1524// - 支持分布式事务(SERIALIZABLE 隔离级别)
1525// - 不支持 PG 的某些高级特性(如 LISTEN/NOTIFY、advisory locks)
1526//
1527// 方言实现通过 delegate_dialect_to 宏委派 PostgreSqlDialect,
1528// 复用 PG 的 quote/escape_string/build_pagination 等方法。
1529#[cfg(feature = "dialect-cockroachdb")]
1530delegate_dialect_to!(CockroachDbDialect, PostgreSqlDialect, DbType::CockroachDB);
1531
1532// YugabyteDB:PostgreSQL 兼容分布式数据库(v3.5.0 新增)
1533//
1534// YugabyteDB 是 Yugabyte 开源的分布式 SQL 数据库,兼容 PostgreSQL 协议:
1535// - 使用 PG wire protocol(YSQL API),可直接用 sqlx::Postgres 驱动连接
1536// - 支持 PG 方言的 SQL 语法(SERIAL/RETURNING/LIMIT OFFSET 等)
1537// - 支持分布式事务(ACID + 全局一致性)
1538// - 支持 PG 存储过程(PL/pgSQL)
1539//
1540// 方言实现通过 delegate_dialect_to 宏委派 PostgreSqlDialect,
1541// 复用 PG 的 quote/escape_string/build_pagination 等方法。
1542#[cfg(feature = "dialect-yugabytedb")]
1543delegate_dialect_to!(YugabyteDbDialect, PostgreSqlDialect, DbType::YugabyteDB);
1544
1545// ============================================================================
1546// ClickHouse 方言(独立实现)
1547//
1548// ClickHouse 是列式 OLAP 数据库,有独特的类型系统和函数:
1549// - 使用 backtick 标识符(与 MySQL 一致)
1550// - 字符串字面量单引号转义为 \'(与 MySQL 一致)
1551// - 不支持事务、不支持 RETURNING
1552// - 分页使用 LIMIT offset, limit 语法(与 MySQL 一致)
1553// - 自增列不支持(使用 UUID 或物化列)
1554// - 类型系统:String/UInt64/Int64/Float64/DateTime 等
1555// ============================================================================
1556
1557/// ClickHouse 方言实现(列式 OLAP 数据库)
1558#[derive(Debug, Clone)]
1559pub struct ClickHouseDialect;
1560
1561impl Dialect for ClickHouseDialect {
1562    fn clone_box(&self) -> Box<dyn Dialect> {
1563        Box::new(ClickHouseDialect)
1564    }
1565
1566    fn db_type(&self) -> DbType {
1567        DbType::ClickHouse
1568    }
1569
1570    fn quote(&self, identifier: &str) -> String {
1571        // ClickHouse 使用 backquote 包裹标识符(与 MySQL 一致)
1572        format!("`{}`", identifier.replace('`', "``"))
1573    }
1574
1575    fn escape_string(&self, s: &str) -> String {
1576        // ClickHouse 使用反斜杠转义(与 MySQL 一致)
1577        let mut escaped = String::with_capacity(s.len() * 2);
1578        for c in s.chars() {
1579            match c {
1580                '\'' => escaped.push_str("\\'"),
1581                '\\' => escaped.push_str("\\\\"),
1582                '\n' => escaped.push_str("\\n"),
1583                '\r' => escaped.push_str("\\r"),
1584                '\t' => escaped.push_str("\\t"),
1585                _ => escaped.push(c),
1586            }
1587        }
1588        escaped
1589    }
1590
1591    fn supports_returning(&self) -> bool {
1592        // ClickHouse 不支持 RETURNING 子句
1593        false
1594    }
1595
1596    fn build_pagination(&self, sql: &str, page: u64, limit: u64) -> String {
1597        // ClickHouse 使用 LIMIT offset, limit 语法
1598        let offset = page.saturating_sub(1).saturating_mul(limit);
1599        format!("{} LIMIT {}, {}", sql, offset, limit)
1600    }
1601
1602    fn json_type(&self) -> &'static str {
1603        // ClickHouse 使用 String 存储 JSON,或使用 JSON 类型(实验性)
1604        "String"
1605    }
1606
1607    fn json_extract(&self, column: &str, path: &str) -> String {
1608        // ClickHouse 使用 JSONExtractString 函数
1609        let normalized = if path.starts_with('$') {
1610            path.to_string()
1611        } else {
1612            format!("$.{}", path)
1613        };
1614        format!(
1615            "JSONExtractString({}, '{}')",
1616            column,
1617            self.escape_string(&normalized)
1618        )
1619    }
1620
1621    fn full_text_search(&self, columns: &[&str], keyword: &str) -> String {
1622        // ClickHouse 使用 position() + like 进行全文检索(无原生 FTS)
1623        if columns.is_empty() {
1624            return "0".to_string();
1625        }
1626        let escaped = self.escape_string(keyword);
1627        let parts: Vec<String> = columns
1628            .iter()
1629            .map(|c| format!("position({}, '{}') > 0", c, escaped))
1630            .collect();
1631        parts.join(" OR ")
1632    }
1633
1634    fn bool_to_int(&self, expr: &str) -> String {
1635        // ClickHouse 支持 toUInt8 转换
1636        format!("toUInt8({})", expr)
1637    }
1638
1639    fn concat(&self, parts: &[&str]) -> String {
1640        // ClickHouse 使用 concat() 函数
1641        if parts.is_empty() {
1642            return "''".to_string();
1643        }
1644        format!("concat({})", parts.join(", "))
1645    }
1646
1647    fn supports_if_exists(&self) -> bool {
1648        true
1649    }
1650
1651    fn supports_if_not_exists(&self) -> bool {
1652        true
1653    }
1654
1655    fn auto_increment_keyword(&self) -> &'static str {
1656        // ClickHouse 不支持自增列,使用 UUID 默认值
1657        ""
1658    }
1659
1660    fn last_insert_id_sql(&self) -> Option<&'static str> {
1661        // ClickHouse 不支持 last_insert_id
1662        None
1663    }
1664
1665    fn build_create_table(&self, table: &str, columns: &[ColumnDef]) -> String {
1666        let cols: Vec<String> = columns
1667            .iter()
1668            .map(|col| {
1669                let ch_type = map_to_clickhouse_type(&col.sql_type);
1670                let mut sql = format!("{} {}", self.quote(&col.name), ch_type);
1671                if let Some(default) = &col.default {
1672                    sql.push_str(&format!(" DEFAULT {}", default));
1673                }
1674                if col.primary_key {
1675                    sql.push_str(" PRIMARY KEY");
1676                }
1677                sql
1678            })
1679            .collect();
1680
1681        // ClickHouse 必须指定 Engine,默认使用 MergeTree
1682        format!(
1683            "CREATE TABLE {} ({}) ENGINE = MergeTree()",
1684            self.quote(table),
1685            cols.join(", ")
1686        )
1687    }
1688
1689    fn build_alter_table(&self, table: &str, changes: &[TableChange]) -> String {
1690        let stmts: Vec<String> = changes
1691            .iter()
1692            .map(|change| match change {
1693                TableChange::AddColumn(col) => {
1694                    let ch_type = map_to_clickhouse_type(&col.sql_type);
1695                    format!(
1696                        "ALTER TABLE {} ADD COLUMN {} {}",
1697                        self.quote(table),
1698                        self.quote(&col.name),
1699                        ch_type
1700                    )
1701                }
1702                TableChange::DropColumn(name) => {
1703                    format!(
1704                        "ALTER TABLE {} DROP COLUMN {}",
1705                        self.quote(table),
1706                        self.quote(name)
1707                    )
1708                }
1709                TableChange::ModifyColumn(col) => {
1710                    let ch_type = map_to_clickhouse_type(&col.sql_type);
1711                    format!(
1712                        "ALTER TABLE {} MODIFY COLUMN {} {}",
1713                        self.quote(table),
1714                        self.quote(&col.name),
1715                        ch_type
1716                    )
1717                }
1718                TableChange::AddIndex(name, cols) => {
1719                    format!(
1720                        "ALTER TABLE {} ADD INDEX {} ({})",
1721                        self.quote(table),
1722                        name,
1723                        cols.join(", ")
1724                    )
1725                }
1726                TableChange::DropIndex(name) => {
1727                    format!("ALTER TABLE {} DROP INDEX {}", self.quote(table), name)
1728                }
1729                TableChange::AddForeignKey { .. } => {
1730                    // ClickHouse 不支持外键,跳过
1731                    String::new()
1732                }
1733            })
1734            .filter(|s| !s.is_empty())
1735            .collect();
1736
1737        stmts.join("; ")
1738    }
1739
1740    fn supports_lock_for_update(&self) -> bool {
1741        // ClickHouse 是列式 OLAP 数据库:无事务、无行级锁
1742        false
1743    }
1744
1745    fn supports_lock_shared(&self) -> bool {
1746        // ClickHouse 是列式 OLAP 数据库:无事务、无共享锁
1747        false
1748    }
1749
1750    fn build_insert_or_ignore_prefix(&self, table: &str) -> String {
1751        // ClickHouse 不支持 INSERT OR IGNORE 语法,回退为普通 INSERT
1752        format!("INSERT INTO {}", self.quote(table))
1753    }
1754}
1755
1756/// 将通用 SQL 类型映射为 ClickHouse 类型
1757///
1758/// - BIGINT → Int64
1759/// - INT/INTEGER → Int32
1760/// - VARCHAR(n)/TEXT → String
1761/// - BOOLEAN/BOOL → UInt8
1762/// - FLOAT → Float32
1763/// - DOUBLE → Float64
1764/// - DATETIME/TIMESTAMP → DateTime
1765fn map_to_clickhouse_type(sql_type: &str) -> String {
1766    let upper = sql_type.to_uppercase();
1767    let trimmed = upper.trim();
1768
1769    if trimmed.starts_with("BIGINT") {
1770        "Int64".to_string()
1771    } else if matches!(trimmed, "INT" | "INTEGER") {
1772        "Int32".to_string()
1773    } else if matches!(trimmed, "TINYINT" | "SMALLINT") {
1774        "Int16".to_string()
1775    } else if trimmed.starts_with("VARCHAR")
1776        || trimmed.starts_with("CHAR")
1777        || matches!(trimmed, "TEXT" | "MEDIUMTEXT" | "LONGTEXT" | "TINYTEXT")
1778    {
1779        "String".to_string()
1780    } else if matches!(trimmed, "BOOLEAN" | "BOOL") {
1781        "UInt8".to_string()
1782    } else if matches!(trimmed, "FLOAT" | "REAL") {
1783        "Float32".to_string()
1784    } else if matches!(trimmed, "DOUBLE" | "DOUBLE PRECISION") {
1785        "Float64".to_string()
1786    } else if matches!(trimmed, "DATETIME" | "TIMESTAMP") {
1787        "DateTime".to_string()
1788    } else if matches!(trimmed, "DATE") {
1789        "Date".to_string()
1790    } else if trimmed.starts_with("DECIMAL") || trimmed.starts_with("NUMERIC") {
1791        "Decimal(38, 4)".to_string()
1792    } else {
1793        sql_type.to_string()
1794    }
1795}
1796
1797// ============================================================================
1798// DuckDB 方言(嵌入式 OLAP 数据库)
1799//
1800// DuckDB 语法特性:
1801// - 双引号标识符(标准 SQL 风格)
1802// - 字符串字面量单引号转义为 ''(标准 SQL 风格)
1803// - 不支持 RETURNING 子句
1804// - 分页使用 LIMIT x OFFSET y(与 PostgreSQL 一致)
1805// - 自增列使用 SERIAL 或 BIGINT PRIMARY KEY
1806// - 类型系统:BIGINT/INTEGER/VARCHAR/BOOLEAN/DOUBLE/TIMESTAMP/DATE 等
1807// - 支持 INSERT OR IGNORE(与 SQLite 一致)
1808// ============================================================================
1809
1810/// DuckDB 方言实现(嵌入式 OLAP 数据库)
1811#[derive(Debug, Clone)]
1812pub struct DuckDBDialect;
1813
1814impl Dialect for DuckDBDialect {
1815    fn clone_box(&self) -> Box<dyn Dialect> {
1816        Box::new(DuckDBDialect)
1817    }
1818
1819    fn db_type(&self) -> DbType {
1820        DbType::DuckDB
1821    }
1822
1823    fn quote(&self, identifier: &str) -> String {
1824        // DuckDB 使用双引号包裹标识符(标准 SQL 风格)
1825        format!("\"{}\"", identifier.replace('"', "\"\""))
1826    }
1827
1828    fn escape_string(&self, s: &str) -> String {
1829        // DuckDB 标准转义:单引号双写('O''Brien')
1830        let mut escaped = String::with_capacity(s.len() * 2);
1831        for c in s.chars() {
1832            match c {
1833                '\'' => escaped.push_str("''"),
1834                _ => escaped.push(c),
1835            }
1836        }
1837        escaped
1838    }
1839
1840    fn supports_returning(&self) -> bool {
1841        // DuckDB 不支持 RETURNING 子句
1842        false
1843    }
1844
1845    fn build_pagination(&self, sql: &str, page: u64, limit: u64) -> String {
1846        // DuckDB 使用 LIMIT x OFFSET y(与 PostgreSQL 一致)
1847        let offset = page.saturating_sub(1).saturating_mul(limit);
1848        format!("{} LIMIT {} OFFSET {}", sql, limit, offset)
1849    }
1850
1851    fn json_type(&self) -> &'static str {
1852        // DuckDB 使用 JSON 类型
1853        "JSON"
1854    }
1855
1856    fn json_extract(&self, column: &str, path: &str) -> String {
1857        // DuckDB 使用 -> 或 ->> 操作符(与 PostgreSQL 一致)
1858        let normalized = if path.starts_with('$') {
1859            path[2..].to_string()
1860        } else {
1861            path.to_string()
1862        };
1863        format!("{} -> '{}'", column, normalized)
1864    }
1865
1866    fn full_text_search(&self, columns: &[&str], keyword: &str) -> String {
1867        // DuckDB 无原生 FTS,降级使用 LIKE
1868        if columns.is_empty() {
1869            return "0".to_string();
1870        }
1871        let escaped = self.escape_string(keyword);
1872        let parts: Vec<String> = columns
1873            .iter()
1874            .map(|c| format!("{} LIKE '%{}%'", c, escaped))
1875            .collect();
1876        parts.join(" OR ")
1877    }
1878
1879    fn bool_to_int(&self, expr: &str) -> String {
1880        // DuckDB 支持 BOOLEAN 类型,但可使用 CASE WHEN 转换
1881        format!("(CASE WHEN {} THEN 1 ELSE 0 END)", expr)
1882    }
1883
1884    fn concat(&self, parts: &[&str]) -> String {
1885        // DuckDB 使用 || 操作符进行字符串拼接
1886        if parts.is_empty() {
1887            return "''".to_string();
1888        }
1889        parts.join(" || ")
1890    }
1891
1892    fn supports_if_exists(&self) -> bool {
1893        true
1894    }
1895
1896    fn supports_if_not_exists(&self) -> bool {
1897        true
1898    }
1899
1900    fn auto_increment_keyword(&self) -> &'static str {
1901        // DuckDB 不支持自增列关键字,使用 SERIAL 或 BIGINT PRIMARY KEY
1902        ""
1903    }
1904
1905    fn last_insert_id_sql(&self) -> Option<&'static str> {
1906        // DuckDB 不支持 last_insert_id
1907        None
1908    }
1909
1910    fn supports_lock_for_update(&self) -> bool {
1911        // DuckDB 不支持行锁(嵌入式数据库)
1912        false
1913    }
1914
1915    fn supports_lock_shared(&self) -> bool {
1916        // DuckDB 不支持共享锁
1917        false
1918    }
1919
1920    fn build_insert_or_ignore_prefix(&self, table: &str) -> String {
1921        // DuckDB 使用 INSERT OR IGNORE INTO(与 SQLite 一致)
1922        format!("INSERT OR IGNORE INTO {}", self.quote(table))
1923    }
1924
1925    fn build_create_table(&self, table: &str, columns: &[ColumnDef]) -> String {
1926        let cols: Vec<String> = columns
1927            .iter()
1928            .map(|col| {
1929                let mut sql = format!("{} {}", self.quote(&col.name), col.sql_type);
1930                if col.auto_increment {
1931                    // DuckDB 不支持 AUTO_INCREMENT,使用 BIGINT PRIMARY KEY
1932                    sql = format!("{} BIGINT PRIMARY KEY", self.quote(&col.name));
1933                }
1934                if let Some(default) = &col.default {
1935                    sql.push_str(&format!(" DEFAULT {}", default));
1936                }
1937                if col.primary_key && !col.auto_increment {
1938                    sql.push_str(" PRIMARY KEY");
1939                }
1940                sql
1941            })
1942            .collect();
1943
1944        format!("CREATE TABLE {} ({})", self.quote(table), cols.join(", "))
1945    }
1946
1947    fn build_alter_table(&self, table: &str, changes: &[TableChange]) -> String {
1948        let stmts: Vec<String> = changes
1949            .iter()
1950            .map(|change| match change {
1951                TableChange::AddColumn(col) => {
1952                    let mut sql = format!(
1953                        "ALTER TABLE {} ADD COLUMN {} {}",
1954                        self.quote(table),
1955                        self.quote(&col.name),
1956                        col.sql_type
1957                    );
1958                    if let Some(default) = &col.default {
1959                        sql.push_str(&format!(" DEFAULT {}", default));
1960                    }
1961                    sql
1962                }
1963                TableChange::DropColumn(name) => {
1964                    format!(
1965                        "ALTER TABLE {} DROP COLUMN {}",
1966                        self.quote(table),
1967                        self.quote(name)
1968                    )
1969                }
1970                TableChange::ModifyColumn(col) => {
1971                    format!(
1972                        "ALTER TABLE {} ALTER COLUMN {} SET DATA TYPE {}",
1973                        self.quote(table),
1974                        self.quote(&col.name),
1975                        col.sql_type
1976                    )
1977                }
1978                TableChange::AddIndex(name, _cols) => {
1979                    // DuckDB 支持 CREATE INDEX,但不支持 ALTER TABLE ADD INDEX
1980                    format!(
1981                        "CREATE INDEX {} ON {} (id)",
1982                        self.quote(name),
1983                        self.quote(table)
1984                    )
1985                }
1986                TableChange::DropIndex(name) => {
1987                    format!("DROP INDEX {}", self.quote(name))
1988                }
1989                TableChange::AddForeignKey { .. } => {
1990                    // DuckDB 不支持外键约束
1991                    String::new()
1992                }
1993            })
1994            .filter(|s| !s.is_empty())
1995            .collect();
1996
1997        stmts.join("; ")
1998    }
1999}
2000
2001// ============================================================================
2002// IBM DB2 方言(独立实现)
2003//
2004// DB2 LUW 语法特性:
2005// - 双引号标识符(标准 SQL 风格)
2006// - 字符串字面量单引号转义为 ''(标准 SQL 风格)
2007// - 支持 RETURNING(DB2 11.5+)
2008// - 分页使用 OFFSET x ROWS FETCH NEXT y ROWS ONLY(标准 SQL:2008)
2009// - 自增列使用 GENERATED ALWAYS AS IDENTITY
2010// - 类型系统:VARCHAR/INTEGER/BIGINT/TIMESTAMP/DECFLOAT 等
2011// ============================================================================
2012
2013/// IBM DB2 LUW 方言实现
2014#[derive(Debug, Clone)]
2015pub struct Db2Dialect;
2016
2017impl Dialect for Db2Dialect {
2018    fn clone_box(&self) -> Box<dyn Dialect> {
2019        Box::new(Db2Dialect)
2020    }
2021
2022    fn db_type(&self) -> DbType {
2023        DbType::Db2
2024    }
2025
2026    fn quote(&self, identifier: &str) -> String {
2027        // DB2 使用双引号包裹标识符(标准 SQL 风格)
2028        format!("\"{}\"", identifier.replace('"', "\"\""))
2029    }
2030
2031    fn escape_string(&self, s: &str) -> String {
2032        // DB2 标准转义:单引号双写('O''Brien')
2033        let mut escaped = String::with_capacity(s.len() * 2);
2034        for c in s.chars() {
2035            match c {
2036                '\'' => escaped.push_str("''"),
2037                _ => escaped.push(c),
2038            }
2039        }
2040        escaped
2041    }
2042
2043    fn supports_returning(&self) -> bool {
2044        // DB2 11.5+ 支持 RETURNING(实际使用 SELECT FROM FINAL TABLE 代替)
2045        false
2046    }
2047
2048    fn build_pagination(&self, sql: &str, page: u64, limit: u64) -> String {
2049        // DB2 使用 OFFSET ... ROWS FETCH NEXT ... ROWS ONLY(SQL:2008 标准)
2050        let offset = page.saturating_sub(1).saturating_mul(limit);
2051        format!(
2052            "{} OFFSET {} ROWS FETCH NEXT {} ROWS ONLY",
2053            sql, offset, limit
2054        )
2055    }
2056
2057    fn json_type(&self) -> &'static str {
2058        // DB2 11.5+ 原生支持 JSON 类型
2059        "JSON"
2060    }
2061
2062    fn json_extract(&self, column: &str, path: &str) -> String {
2063        // DB2 使用 JSON_VALUE 函数
2064        let normalized = if path.starts_with('$') {
2065            path.to_string()
2066        } else {
2067            format!("$.{}", path)
2068        };
2069        format!(
2070            "JSON_VALUE({}, '{}')",
2071            column,
2072            self.escape_string(&normalized)
2073        )
2074    }
2075
2076    fn full_text_search(&self, columns: &[&str], keyword: &str) -> String {
2077        // DB2 使用 CONTAINS 函数(需 DB2TEXT 索引)
2078        if columns.is_empty() {
2079            return "0".to_string();
2080        }
2081        let escaped = self.escape_string(keyword);
2082        let parts: Vec<String> = columns
2083            .iter()
2084            .map(|c| format!("CONTAINS({}, '{}') > 0", c, escaped))
2085            .collect();
2086        parts.join(" OR ")
2087    }
2088
2089    fn bool_to_int(&self, expr: &str) -> String {
2090        // DB2 没有原生 BOOL(11.5+ 有 BOOLEAN),使用 CASE WHEN 转换
2091        format!("(CASE WHEN {} THEN 1 ELSE 0 END)", expr)
2092    }
2093
2094    fn concat(&self, parts: &[&str]) -> String {
2095        // DB2 使用 || 操作符进行字符串拼接
2096        if parts.is_empty() {
2097            return "''".to_string();
2098        }
2099        parts.join(" || ")
2100    }
2101
2102    fn supports_if_exists(&self) -> bool {
2103        // DB2 不支持 DROP TABLE IF EXISTS(直到 11.5)
2104        false
2105    }
2106
2107    fn supports_if_not_exists(&self) -> bool {
2108        // DB2 不支持 CREATE TABLE IF NOT EXISTS
2109        false
2110    }
2111
2112    fn auto_increment_keyword(&self) -> &'static str {
2113        // DB2 使用 GENERATED ALWAYS AS IDENTITY
2114        "GENERATED ALWAYS AS IDENTITY"
2115    }
2116
2117    fn last_insert_id_sql(&self) -> Option<&'static str> {
2118        // DB2 使用 IDENTITY_VAL_LOCAL() 函数获取最后插入的 IDENTITY 值
2119        Some("SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1")
2120    }
2121
2122    fn build_create_table(&self, table: &str, columns: &[ColumnDef]) -> String {
2123        let cols: Vec<String> = columns
2124            .iter()
2125            .map(|col| {
2126                let db2_type = map_to_db2_type(&col.sql_type);
2127                let mut sql = format!("{} {}", self.quote(&col.name), db2_type);
2128                if !col.nullable && !col.auto_increment {
2129                    sql.push_str(" NOT NULL");
2130                }
2131                if let Some(default) = &col.default {
2132                    sql.push_str(&format!(" DEFAULT {}", default));
2133                }
2134                if col.auto_increment {
2135                    sql.push_str(&format!(" {}", self.auto_increment_keyword()));
2136                }
2137                if col.primary_key {
2138                    sql.push_str(" PRIMARY KEY");
2139                }
2140                sql
2141            })
2142            .collect();
2143
2144        format!("CREATE TABLE {} ({})", self.quote(table), cols.join(", "))
2145    }
2146
2147    fn build_alter_table(&self, table: &str, changes: &[TableChange]) -> String {
2148        let stmts: Vec<String> = changes
2149            .iter()
2150            .map(|change| match change {
2151                TableChange::AddColumn(col) => {
2152                    let db2_type = map_to_db2_type(&col.sql_type);
2153                    let mut sql = format!(
2154                        "ALTER TABLE {} ADD COLUMN {} {}",
2155                        self.quote(table),
2156                        self.quote(&col.name),
2157                        db2_type
2158                    );
2159                    if !col.nullable {
2160                        sql.push_str(" NOT NULL");
2161                    }
2162                    if let Some(default) = &col.default {
2163                        sql.push_str(&format!(" DEFAULT {}", default));
2164                    }
2165                    sql
2166                }
2167                TableChange::DropColumn(name) => {
2168                    format!(
2169                        "ALTER TABLE {} DROP COLUMN {}",
2170                        self.quote(table),
2171                        self.quote(name)
2172                    )
2173                }
2174                TableChange::ModifyColumn(col) => {
2175                    let db2_type = map_to_db2_type(&col.sql_type);
2176                    format!(
2177                        "ALTER TABLE {} ALTER COLUMN {} SET DATA TYPE {}",
2178                        self.quote(table),
2179                        self.quote(&col.name),
2180                        db2_type
2181                    )
2182                }
2183                TableChange::AddIndex(name, cols) => {
2184                    format!(
2185                        "CREATE INDEX {} ON {} ({})",
2186                        name,
2187                        self.quote(table),
2188                        cols.join(", ")
2189                    )
2190                }
2191                TableChange::DropIndex(name) => {
2192                    format!("DROP INDEX {}", name)
2193                }
2194                TableChange::AddForeignKey {
2195                    columns,
2196                    reference_table,
2197                    reference_columns,
2198                } => {
2199                    format!(
2200                        "ALTER TABLE {} ADD CONSTRAINT fk_{}_{} FOREIGN KEY ({}) REFERENCES {} ({})",
2201                        self.quote(table),
2202                        table,
2203                        columns.join("_"),
2204                        columns.iter().map(|c| self.quote(c)).collect::<Vec<_>>().join(", "),
2205                        self.quote(reference_table),
2206                        reference_columns.iter().map(|c| self.quote(c)).collect::<Vec<_>>().join(", ")
2207                    )
2208                }
2209            })
2210            .collect();
2211
2212        stmts.join("; ")
2213    }
2214
2215    fn build_drop_table(&self, table: &str, if_exists: bool) -> String {
2216        // DB2 不支持 IF EXISTS,但为兼容性保留参数
2217        let _ = if_exists;
2218        format!("DROP TABLE {}", self.quote(table))
2219    }
2220}
2221
2222/// 将通用 SQL 类型映射为 IBM DB2 类型
2223///
2224/// - BIGINT → BIGINT
2225/// - INT/INTEGER → INTEGER
2226/// - VARCHAR(n) → VARCHAR(n)
2227/// - TEXT 系列 → CLOB(2G)
2228/// - BOOLEAN/BOOL → SMALLINT(DB2 11.5+ 才有 BOOLEAN)
2229/// - DATETIME/TIMESTAMP → TIMESTAMP
2230fn map_to_db2_type(sql_type: &str) -> String {
2231    let upper = sql_type.to_uppercase();
2232    let trimmed = upper.trim();
2233
2234    if trimmed.starts_with("BIGINT") {
2235        "BIGINT".to_string()
2236    } else if matches!(trimmed, "INT" | "INTEGER") {
2237        "INTEGER".to_string()
2238    } else if matches!(trimmed, "TINYINT" | "SMALLINT") {
2239        "SMALLINT".to_string()
2240    } else if trimmed.starts_with("VARCHAR") || trimmed.starts_with("CHAR") {
2241        sql_type.to_string()
2242    } else if matches!(trimmed, "TEXT" | "MEDIUMTEXT" | "LONGTEXT" | "TINYTEXT") {
2243        "CLOB(2G)".to_string()
2244    } else if matches!(trimmed, "BOOLEAN" | "BOOL") {
2245        "SMALLINT".to_string()
2246    } else if matches!(trimmed, "FLOAT" | "REAL") {
2247        "REAL".to_string()
2248    } else if matches!(trimmed, "DOUBLE" | "DOUBLE PRECISION") {
2249        "DOUBLE".to_string()
2250    } else if matches!(trimmed, "DATETIME" | "TIMESTAMP") {
2251        "TIMESTAMP".to_string()
2252    } else if matches!(trimmed, "DATE") {
2253        "DATE".to_string()
2254    } else {
2255        // DECIMAL/NUMERIC 和其他未匹配类型保持原样
2256        sql_type.to_string()
2257    }
2258}
2259
2260/// 根据数据库类型获取对应的方言实例
2261///
2262/// L-5 修复:补充示例文档
2263///
2264/// 返回 `Box<dyn Dialect>`,可用于与 `QueryBuilder`、`Schema` 等组件配合。
2265/// 对于不支持 SQL 方言的数据库类型(如 Redis、MongoDB、向量库),返回
2266/// `DbError::Unsupported`。
2267///
2268/// # 示例
2269///
2270/// ```ignore
2271/// use sz_orm_core::db_type::DbType;
2272/// use sz_orm_core::dialect::{get_dialect, Dialect};
2273///
2274/// let dialect = get_dialect(DbType::MySQL).unwrap();
2275/// assert_eq!(dialect.quote("user"), "`user`");
2276/// assert_eq!(dialect.db_type(), DbType::MySQL);
2277///
2278/// // 不支持的类型返回错误
2279/// let err = get_dialect(DbType::Redis).unwrap_err();
2280/// assert!(matches!(err, sz_orm_core::DbError::Unsupported(_)));
2281/// ```
2282pub fn get_dialect(db_type: DbType) -> Result<Box<dyn Dialect>, DbError> {
2283    match db_type {
2284        DbType::MySQL => Ok(Box::new(MySqlDialect)),
2285        DbType::PostgreSQL => Ok(Box::new(PostgreSqlDialect)),
2286        DbType::Sqlite => Ok(Box::new(SqliteDialect)),
2287        DbType::Redis => Err(DbError::Unsupported(
2288            "Redis does not support standard SQL dialect".to_string(),
2289        )),
2290        DbType::MongoDB => Err(DbError::Unsupported(
2291            "MongoDB uses different query syntax".to_string(),
2292        )),
2293        DbType::ClickHouse => Ok(Box::new(ClickHouseDialect)),
2294        DbType::Oracle => Ok(Box::new(OracleDialect)),
2295        DbType::OceanBase => Ok(Box::new(MySqlDialect)),
2296        DbType::SqlServer => Ok(Box::new(SqlServerDialect)),
2297        DbType::VectorDb => Err(DbError::Unsupported(
2298            "Vector databases have specific APIs".to_string(),
2299        )),
2300        DbType::PureJsDb => Err(DbError::Unsupported(
2301            "PureJS database uses JavaScript".to_string(),
2302        )),
2303        // 国产数据库 & 兼容方言
2304        DbType::Dameng => Ok(Box::new(DamengDialect)),
2305        DbType::Kingbase => Ok(Box::new(KingbaseDialect)),
2306        DbType::Db2 => Ok(Box::new(Db2Dialect)),
2307        DbType::MariaDB => Ok(Box::new(MariaDbDialect)),
2308        DbType::TiDB => Ok(Box::new(TiDbDialect)),
2309        DbType::PolarDB => Ok(Box::new(PolarDbDialect)),
2310        DbType::GaussDB => Ok(Box::new(GaussDbDialect)),
2311        DbType::GBase => Ok(Box::new(GBaseDialect)),
2312        DbType::Sybase => Ok(Box::new(SybaseDialect)),
2313        DbType::DuckDB => Ok(Box::new(DuckDBDialect)),
2314        #[cfg(feature = "dialect-cockroachdb")]
2315        DbType::CockroachDB => Ok(Box::new(CockroachDbDialect)),
2316        #[cfg(feature = "dialect-yugabytedb")]
2317        DbType::YugabyteDB => Ok(Box::new(YugabyteDbDialect)),
2318    }
2319}
2320
2321impl fmt::Display for dyn Dialect {
2322    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2323        write!(f, "Dialect({})", self.db_type())
2324    }
2325}
2326
2327#[cfg(test)]
2328mod tests {
2329    use super::*;
2330
2331    #[test]
2332    fn test_mysql_quote() {
2333        let dialect = MySqlDialect;
2334        assert_eq!(dialect.quote("users"), "`users`");
2335        assert_eq!(dialect.quote("user`id"), "`user``id`");
2336    }
2337
2338    #[test]
2339    fn test_mysql_escape() {
2340        let dialect = MySqlDialect;
2341        assert_eq!(dialect.escape_string("hello"), "hello");
2342        assert_eq!(dialect.escape_string("it's"), "it\\'s");
2343        assert_eq!(dialect.escape_string("line\nbreak"), "line\\nbreak");
2344    }
2345
2346    #[test]
2347    fn test_mysql_pagination() {
2348        let dialect = MySqlDialect;
2349        let sql = dialect.build_pagination("SELECT * FROM users", 2, 10);
2350        assert_eq!(sql, "SELECT * FROM users LIMIT 10 OFFSET 10");
2351    }
2352
2353    #[test]
2354    fn test_postgres_quote() {
2355        let dialect = PostgreSqlDialect;
2356        assert_eq!(dialect.quote("users"), "\"users\"");
2357        assert_eq!(dialect.quote("user\"id"), "\"user\"\"id\"");
2358    }
2359
2360    #[test]
2361    fn test_postgres_pagination() {
2362        let dialect = PostgreSqlDialect;
2363        let sql = dialect.build_pagination("SELECT * FROM users", 3, 20);
2364        assert_eq!(sql, "SELECT * FROM users LIMIT 20 OFFSET 40");
2365    }
2366
2367    #[test]
2368    fn test_postgres_returning() {
2369        let dialect = PostgreSqlDialect;
2370        assert!(dialect.supports_returning());
2371    }
2372
2373    #[test]
2374    fn test_sqlite_quote() {
2375        let dialect = SqliteDialect;
2376        assert_eq!(dialect.quote("users"), "\"users\"");
2377        assert_eq!(dialect.quote("user\"id"), "\"user\"\"id\"");
2378    }
2379
2380    #[test]
2381    fn test_sqlite_escape() {
2382        let dialect = SqliteDialect;
2383        assert_eq!(dialect.escape_string("hello"), "hello");
2384        assert_eq!(dialect.escape_string("it's"), "it''s");
2385    }
2386
2387    #[test]
2388    fn test_get_dialect() {
2389        let dialect = get_dialect(DbType::MySQL);
2390        assert!(dialect.is_ok());
2391
2392        let dialect = get_dialect(DbType::Redis);
2393        assert!(dialect.is_err());
2394    }
2395
2396    #[test]
2397    fn test_bool_to_int() {
2398        let mysql = MySqlDialect;
2399        assert_eq!(mysql.bool_to_int("active"), "IF(active, 1, 0)");
2400
2401        let pg = PostgreSqlDialect;
2402        assert_eq!(
2403            pg.bool_to_int("active"),
2404            "(CASE WHEN active THEN 1 ELSE 0 END)"
2405        );
2406    }
2407
2408    #[test]
2409    fn test_json_extract_with_path() {
2410        let mysql = MySqlDialect;
2411        let sql = mysql.json_extract("data", "$.user.name");
2412        assert!(sql.contains("$.user.name"));
2413        assert!(sql.contains("JSON_EXTRACT"));
2414
2415        let pg = PostgreSqlDialect;
2416        let sql = pg.json_extract("data", "user.name");
2417        assert!(sql.contains("#>>"));
2418
2419        let sqlite = SqliteDialect;
2420        let sql = sqlite.json_extract("data", "$.user.name");
2421        assert!(sql.contains("$.user.name"));
2422        assert!(sql.contains("json_extract"));
2423    }
2424
2425    #[test]
2426    fn test_sqlite_full_text_search() {
2427        let sqlite = SqliteDialect;
2428        let sql = sqlite.full_text_search(&["title", "content"], "hello");
2429        // SQLite 接口只传入列名,无 FTS 表名,因此降级使用 LIKE
2430        assert!(sql.contains("LIKE"));
2431        assert!(sql.contains("title LIKE '%hello%'"));
2432        assert!(sql.contains("content LIKE '%hello%'"));
2433        assert!(sql.contains(" OR "));
2434
2435        // 空列列表返回 "0"(短路避免空 IN/OR)
2436        assert_eq!(sqlite.full_text_search(&[], "hello"), "0");
2437
2438        // 含单引号的关键字需正确转义(SQLite 双单引号)
2439        let sql = sqlite.full_text_search(&["title"], "it's");
2440        assert!(sql.contains("title LIKE '%it''s%'"));
2441    }
2442
2443    #[test]
2444    fn test_alter_table_modify_column() {
2445        let mysql = MySqlDialect;
2446        let col = ColumnDef {
2447            name: "name".to_string(),
2448            sql_type: "VARCHAR(255)".to_string(),
2449            nullable: false,
2450            default: None,
2451            auto_increment: false,
2452            primary_key: false,
2453        };
2454        let sql = mysql.build_alter_table("users", &[TableChange::ModifyColumn(col)]);
2455        assert!(sql.contains("MODIFY COLUMN"));
2456
2457        let pg = PostgreSqlDialect;
2458        let col = ColumnDef {
2459            name: "name".to_string(),
2460            sql_type: "VARCHAR(255)".to_string(),
2461            nullable: false,
2462            default: None,
2463            auto_increment: false,
2464            primary_key: false,
2465        };
2466        let sql = pg.build_alter_table("users", &[TableChange::ModifyColumn(col)]);
2467        assert!(sql.contains("ALTER COLUMN"));
2468        assert!(sql.contains("TYPE"));
2469    }
2470
2471    #[test]
2472    fn test_alter_table_add_foreign_key() {
2473        let mysql = MySqlDialect;
2474        let sql = mysql.build_alter_table(
2475            "orders",
2476            &[TableChange::AddForeignKey {
2477                columns: vec!["user_id".to_string()],
2478                reference_table: "users".to_string(),
2479                reference_columns: vec!["id".to_string()],
2480            }],
2481        );
2482        assert!(sql.contains("FOREIGN KEY"));
2483        assert!(sql.contains("REFERENCES"));
2484
2485        let sqlite = SqliteDialect;
2486        let sql = sqlite.build_alter_table(
2487            "orders",
2488            &[TableChange::AddForeignKey {
2489                columns: vec!["user_id".to_string()],
2490                reference_table: "users".to_string(),
2491                reference_columns: vec!["id".to_string()],
2492            }],
2493        );
2494        // SQLite 不支持,应返回注释
2495        assert!(sql.starts_with("--"));
2496    }
2497
2498    #[test]
2499    fn test_sqlite_alter_table_add_column() {
2500        let sqlite = SqliteDialect;
2501        let col = ColumnDef {
2502            name: "email".to_string(),
2503            sql_type: "TEXT".to_string(),
2504            nullable: true,
2505            default: None,
2506            auto_increment: false,
2507            primary_key: false,
2508        };
2509        let sql = sqlite.build_alter_table("users", &[TableChange::AddColumn(col)]);
2510        assert!(sql.contains("ADD COLUMN"));
2511        assert!(sql.contains("email"));
2512    }
2513
2514    // ===================== Oracle 方言测试 =====================
2515
2516    #[test]
2517    fn test_oracle_quote_and_escape() {
2518        let dialect = OracleDialect;
2519        // 标识符使用双引号包裹,内部双引号双写
2520        assert_eq!(dialect.quote("users"), "\"users\"");
2521        assert_eq!(dialect.quote("user\"id"), "\"user\"\"id\"");
2522        assert_eq!(dialect.quote("column_name"), "\"column_name\"");
2523
2524        // 字符串字面量转义:单引号双写,反斜杠不转义
2525        assert_eq!(dialect.escape_string("hello"), "hello");
2526        assert_eq!(dialect.escape_string("it's"), "it''s");
2527        assert_eq!(dialect.escape_string("O'Brien"), "O''Brien");
2528        assert_eq!(dialect.escape_string("a'b'c"), "a''b''c");
2529        // 反斜杠按原样保留(Oracle 标准行为)
2530        assert_eq!(dialect.escape_string("path\\to"), "path\\to");
2531    }
2532
2533    #[test]
2534    fn test_oracle_pagination() {
2535        let dialect = OracleDialect;
2536        // page=1 为第一页(offset=0),与其他方言保持一致
2537        let sql = dialect.build_pagination("SELECT * FROM users", 1, 10);
2538        assert_eq!(
2539            sql,
2540            "SELECT * FROM users OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY"
2541        );
2542        // page=3, limit=20 → offset=40
2543        let sql = dialect.build_pagination("SELECT * FROM users", 3, 20);
2544        assert_eq!(
2545            sql,
2546            "SELECT * FROM users OFFSET 40 ROWS FETCH NEXT 20 ROWS ONLY"
2547        );
2548        // page=0(边界)→ offset=0
2549        let sql = dialect.build_pagination("SELECT * FROM users", 0, 10);
2550        assert_eq!(
2551            sql,
2552            "SELECT * FROM users OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY"
2553        );
2554    }
2555
2556    #[test]
2557    fn test_oracle_json_extract() {
2558        let dialect = OracleDialect;
2559        // 标准 $.path 格式
2560        let sql = dialect.json_extract("data", "$.user.name");
2561        assert!(sql.contains("JSON_VALUE"));
2562        assert!(sql.contains("$.user.name"));
2563        assert!(sql.starts_with("JSON_VALUE(data, '$.user.name')"));
2564
2565        // 自动补全 $. 前缀
2566        let sql = dialect.json_extract("data", "user.name");
2567        assert!(sql.contains("$.user.name"));
2568        assert!(sql.contains("JSON_VALUE"));
2569
2570        // 单引号转义
2571        let sql = dialect.json_extract("data", "$.key's");
2572        assert!(sql.contains("$.key''s"));
2573    }
2574
2575    #[test]
2576    fn test_oracle_create_table() {
2577        let dialect = OracleDialect;
2578        let columns = vec![
2579            ColumnDef {
2580                name: "id".to_string(),
2581                sql_type: "BIGINT".to_string(),
2582                nullable: false,
2583                default: None,
2584                auto_increment: true,
2585                primary_key: true,
2586            },
2587            ColumnDef {
2588                name: "name".to_string(),
2589                sql_type: "VARCHAR(255)".to_string(),
2590                nullable: false,
2591                default: None,
2592                auto_increment: false,
2593                primary_key: false,
2594            },
2595            ColumnDef {
2596                name: "bio".to_string(),
2597                sql_type: "TEXT".to_string(),
2598                nullable: true,
2599                default: None,
2600                auto_increment: false,
2601                primary_key: false,
2602            },
2603            ColumnDef {
2604                name: "is_active".to_string(),
2605                sql_type: "BOOLEAN".to_string(),
2606                nullable: false,
2607                default: Some("1".to_string()),
2608                auto_increment: false,
2609                primary_key: false,
2610            },
2611        ];
2612        let sql = dialect.build_create_table("users", &columns);
2613        // Oracle 类型映射
2614        assert!(
2615            sql.contains("NUMBER(19)"),
2616            "BIGINT should map to NUMBER(19): {}",
2617            sql
2618        );
2619        assert!(
2620            sql.contains("VARCHAR2(255)"),
2621            "VARCHAR should map to VARCHAR2: {}",
2622            sql
2623        );
2624        assert!(sql.contains("CLOB"), "TEXT should map to CLOB: {}", sql);
2625        assert!(
2626            sql.contains("NUMBER(1)"),
2627            "BOOLEAN should map to NUMBER(1): {}",
2628            sql
2629        );
2630        // 自增与主键
2631        assert!(sql.contains("GENERATED BY DEFAULT AS IDENTITY"));
2632        assert!(sql.contains("PRIMARY KEY"));
2633        assert!(sql.contains("NOT NULL"));
2634        assert!(sql.contains("DEFAULT 1"));
2635        // 标识符使用双引号
2636        assert!(sql.contains("\"users\""));
2637        assert!(sql.contains("\"id\""));
2638    }
2639
2640    #[test]
2641    fn test_oracle_bool_to_int_and_concat() {
2642        let dialect = OracleDialect;
2643        // bool_to_int 使用 CASE WHEN
2644        assert_eq!(
2645            dialect.bool_to_int("active"),
2646            "(CASE WHEN active THEN 1 ELSE 0 END)"
2647        );
2648        assert_eq!(
2649            dialect.bool_to_int("x > 0"),
2650            "(CASE WHEN x > 0 THEN 1 ELSE 0 END)"
2651        );
2652        // concat 使用 || 操作符
2653        assert_eq!(dialect.concat(&["a", "b", "c"]), "a || b || c");
2654        assert_eq!(
2655            dialect.concat(&["first_name", "last_name"]),
2656            "first_name || last_name"
2657        );
2658        // 空列表返回 NULL
2659        assert_eq!(dialect.concat(&[]), "NULL");
2660    }
2661
2662    #[test]
2663    fn test_oracle_misc_dialect_methods() {
2664        let dialect = OracleDialect;
2665        // db_type
2666        assert_eq!(dialect.db_type(), DbType::Oracle);
2667        // supports_returning: Oracle 12c+ 支持
2668        assert!(dialect.supports_returning());
2669        // supports_if_exists / supports_if_not_exists: Oracle 23ai 支持
2670        assert!(dialect.supports_if_exists());
2671        assert!(dialect.supports_if_not_exists());
2672        // auto_increment_keyword
2673        assert_eq!(
2674            dialect.auto_increment_keyword(),
2675            "GENERATED BY DEFAULT AS IDENTITY"
2676        );
2677        // last_insert_id_sql: Oracle 不支持独立可执行的获取最后插入 ID 语句
2678        // RETURNING ... INTO 是 PL/SQL 子句,必须附加在 INSERT 之后
2679        assert_eq!(dialect.last_insert_id_sql(), None);
2680        // json_type
2681        assert_eq!(dialect.json_type(), "JSON");
2682    }
2683
2684    #[test]
2685    fn test_oracle_get_dialect() {
2686        // get_dialect 应返回 OracleDialect 而非 MySqlDialect
2687        let dialect = get_dialect(DbType::Oracle);
2688        assert!(dialect.is_ok(), "Oracle dialect should be available");
2689        let dialect = dialect.unwrap();
2690        assert_eq!(dialect.db_type(), DbType::Oracle);
2691        // 验证不是 MySqlDialect 的回退:Oracle 使用双引号,MySQL 使用反引号
2692        assert_eq!(dialect.quote("users"), "\"users\"");
2693        // 验证 Oracle 特有功能
2694        assert!(dialect.supports_returning());
2695        // Oracle 没有 standalone 的 last_insert_id SQL
2696        assert_eq!(dialect.last_insert_id_sql(), None);
2697    }
2698
2699    #[test]
2700    fn test_oracle_drop_table() {
2701        let dialect = OracleDialect;
2702        // IF EXISTS
2703        let sql = dialect.build_drop_table("users", true);
2704        assert_eq!(sql, "DROP TABLE IF EXISTS \"users\"");
2705        // 不带 IF EXISTS
2706        let sql = dialect.build_drop_table("users", false);
2707        assert_eq!(sql, "DROP TABLE \"users\"");
2708    }
2709
2710    #[test]
2711    fn test_oracle_alter_table() {
2712        let dialect = OracleDialect;
2713        // MODIFY COLUMN: Oracle 使用 MODIFY(不带 COLUMN 关键字)
2714        let col = ColumnDef {
2715            name: "name".to_string(),
2716            sql_type: "VARCHAR(255)".to_string(),
2717            nullable: false,
2718            default: None,
2719            auto_increment: false,
2720            primary_key: false,
2721        };
2722        let sql = dialect.build_alter_table("users", &[TableChange::ModifyColumn(col)]);
2723        assert!(sql.contains("MODIFY"));
2724        assert!(sql.contains("VARCHAR2(255)"));
2725        assert!(!sql.contains("MODIFY COLUMN")); // Oracle 不使用 COLUMN 关键字
2726
2727        // ADD COLUMN
2728        let col = ColumnDef {
2729            name: "email".to_string(),
2730            sql_type: "VARCHAR(255)".to_string(),
2731            nullable: true,
2732            default: None,
2733            auto_increment: false,
2734            primary_key: false,
2735        };
2736        let sql = dialect.build_alter_table("users", &[TableChange::AddColumn(col)]);
2737        assert!(sql.contains("ADD \"email\""));
2738        assert!(sql.contains("VARCHAR2(255)"));
2739
2740        // DROP COLUMN
2741        let sql =
2742            dialect.build_alter_table("users", &[TableChange::DropColumn("email".to_string())]);
2743        assert!(sql.contains("DROP COLUMN"));
2744        assert!(sql.contains("\"email\""));
2745    }
2746
2747    // ===================== SQLite concat 修复测试 =====================
2748
2749    #[test]
2750    fn test_sqlite_concat_handles_null() {
2751        let sqlite = SqliteDialect;
2752        // 修复前:COALESCE(a || b) 在 a 为 NULL 时整体已 NULL,COALESCE 失效
2753        // 修复后:每个参数 COALESCE 为空串,保证拼接结果非 NULL
2754        let sql = sqlite.concat(&["a", "b"]);
2755        assert_eq!(sql, "COALESCE(a, '') || COALESCE(b, '')");
2756        // 单参数
2757        let sql = sqlite.concat(&["a"]);
2758        assert_eq!(sql, "COALESCE(a, '')");
2759        // 空列表
2760        assert_eq!(sqlite.concat(&[]), "NULL");
2761    }
2762
2763    // ===================== SQL Server 方言测试 =====================
2764
2765    #[test]
2766    fn test_sqlserver_quote_and_escape() {
2767        let dialect = SqlServerDialect;
2768        // 标识符使用 [name] 包裹,内部 ] 双写
2769        assert_eq!(dialect.quote("users"), "[users]");
2770        assert_eq!(dialect.quote("col]name"), "[col]]name]");
2771        // 字符串字面量:单引号双写,反斜杠不转义
2772        assert_eq!(dialect.escape_string("hello"), "hello");
2773        assert_eq!(dialect.escape_string("it's"), "it''s");
2774        assert_eq!(dialect.escape_string("O'Brien"), "O''Brien");
2775        assert_eq!(dialect.escape_string("path\\to"), "path\\to");
2776    }
2777
2778    #[test]
2779    fn test_sqlserver_pagination() {
2780        let dialect = SqlServerDialect;
2781        // SQL Server 2012+ 使用 OFFSET ... ROWS FETCH NEXT ... ROWS ONLY
2782        let sql = dialect.build_pagination("SELECT * FROM users", 1, 10);
2783        assert_eq!(
2784            sql,
2785            "SELECT * FROM users OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY"
2786        );
2787        let sql = dialect.build_pagination("SELECT * FROM users", 3, 20);
2788        assert_eq!(
2789            sql,
2790            "SELECT * FROM users OFFSET 40 ROWS FETCH NEXT 20 ROWS ONLY"
2791        );
2792        // page=0 边界
2793        let sql = dialect.build_pagination("SELECT * FROM users", 0, 10);
2794        assert_eq!(
2795            sql,
2796            "SELECT * FROM users OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY"
2797        );
2798    }
2799
2800    #[test]
2801    fn test_sqlserver_misc_dialect_methods() {
2802        let dialect = SqlServerDialect;
2803        assert_eq!(dialect.db_type(), DbType::SqlServer);
2804        // supports_returning: SQL Server 使用 OUTPUT,语义等价
2805        assert!(dialect.supports_returning());
2806        // supports_if_exists / supports_if_not_exists: SQL Server 2016+
2807        assert!(dialect.supports_if_exists());
2808        assert!(dialect.supports_if_not_exists());
2809        // auto_increment_keyword
2810        assert_eq!(dialect.auto_increment_keyword(), "IDENTITY(1,1)");
2811        // last_insert_id_sql
2812        assert_eq!(dialect.last_insert_id_sql(), Some("SCOPE_IDENTITY()"));
2813        // json_type
2814        assert_eq!(dialect.json_type(), "NVARCHAR(MAX)");
2815    }
2816
2817    #[test]
2818    fn test_sqlserver_insert_or_ignore_fallback_to_plain_insert() {
2819        let dialect = SqlServerDialect;
2820        let sql = dialect.build_insert_or_ignore_prefix("users");
2821        // SQL Server 不支持 INSERT OR IGNORE,回退为普通 INSERT(不生成非法 SQL)
2822        assert_eq!(sql, "INSERT INTO [users]");
2823        assert!(!sql.contains("OR IGNORE"));
2824    }
2825
2826    #[test]
2827    fn test_sqlserver_json_extract() {
2828        let dialect = SqlServerDialect;
2829        let sql = dialect.json_extract("data", "$.user.name");
2830        assert!(sql.starts_with("JSON_VALUE(data, '$.user.name')"));
2831        // 自动补 $. 前缀
2832        let sql = dialect.json_extract("data", "user.name");
2833        assert!(sql.contains("$.user.name"));
2834        assert!(sql.contains("JSON_VALUE"));
2835        // 单引号转义
2836        let sql = dialect.json_extract("data", "$.key's");
2837        assert!(sql.contains("$.key''s"));
2838    }
2839
2840    #[test]
2841    fn test_sqlserver_full_text_search() {
2842        let dialect = SqlServerDialect;
2843        let sql = dialect.full_text_search(&["title", "content"], "hello");
2844        assert!(sql.starts_with("CONTAINS(title, content, 'hello')"));
2845        // 空列列表
2846        assert_eq!(dialect.full_text_search(&[], "hello"), "0");
2847        // 单引号转义
2848        let sql = dialect.full_text_search(&["title"], "it's");
2849        assert!(sql.contains("it''s"));
2850    }
2851
2852    #[test]
2853    fn test_sqlserver_bool_to_int_and_concat() {
2854        let dialect = SqlServerDialect;
2855        assert_eq!(
2856            dialect.bool_to_int("active"),
2857            "(CASE WHEN active THEN 1 ELSE 0 END)"
2858        );
2859        assert_eq!(dialect.concat(&["a", "b", "c"]), "CONCAT(a, b, c)");
2860        assert_eq!(dialect.concat(&[]), "NULL");
2861    }
2862
2863    #[test]
2864    fn test_sqlserver_create_table() {
2865        let dialect = SqlServerDialect;
2866        let columns = vec![
2867            ColumnDef {
2868                name: "id".to_string(),
2869                sql_type: "BIGINT".to_string(),
2870                nullable: false,
2871                default: None,
2872                auto_increment: true,
2873                primary_key: true,
2874            },
2875            ColumnDef {
2876                name: "name".to_string(),
2877                sql_type: "VARCHAR(255)".to_string(),
2878                nullable: false,
2879                default: None,
2880                auto_increment: false,
2881                primary_key: false,
2882            },
2883            ColumnDef {
2884                name: "bio".to_string(),
2885                sql_type: "TEXT".to_string(),
2886                nullable: true,
2887                default: None,
2888                auto_increment: false,
2889                primary_key: false,
2890            },
2891            ColumnDef {
2892                name: "is_active".to_string(),
2893                sql_type: "BOOLEAN".to_string(),
2894                nullable: false,
2895                default: Some("1".to_string()),
2896                auto_increment: false,
2897                primary_key: false,
2898            },
2899        ];
2900        let sql = dialect.build_create_table("users", &columns);
2901        // 标识符使用方括号
2902        assert!(sql.contains("[users]"));
2903        assert!(sql.contains("[id]"));
2904        // 类型映射
2905        assert!(sql.contains("IDENTITY(1,1)"));
2906        assert!(
2907            sql.contains("NVARCHAR(255)"),
2908            "VARCHAR should map to NVARCHAR: {}",
2909            sql
2910        );
2911        assert!(
2912            sql.contains("NVARCHAR(MAX)"),
2913            "TEXT should map to NVARCHAR(MAX): {}",
2914            sql
2915        );
2916        assert!(sql.contains("BIT"), "BOOLEAN should map to BIT: {}", sql);
2917        assert!(sql.contains("PRIMARY KEY"));
2918        assert!(sql.contains("NOT NULL"));
2919        assert!(sql.contains("DEFAULT 1"));
2920    }
2921
2922    #[test]
2923    fn test_sqlserver_drop_table() {
2924        let dialect = SqlServerDialect;
2925        assert_eq!(
2926            dialect.build_drop_table("users", true),
2927            "DROP TABLE IF EXISTS [users]"
2928        );
2929        assert_eq!(
2930            dialect.build_drop_table("users", false),
2931            "DROP TABLE [users]"
2932        );
2933    }
2934
2935    #[test]
2936    fn test_sqlserver_alter_table() {
2937        let dialect = SqlServerDialect;
2938        // MODIFY COLUMN: SQL Server 使用 ALTER COLUMN(不是 MODIFY)
2939        let col = ColumnDef {
2940            name: "name".to_string(),
2941            sql_type: "VARCHAR(255)".to_string(),
2942            nullable: false,
2943            default: None,
2944            auto_increment: false,
2945            primary_key: false,
2946        };
2947        let sql = dialect.build_alter_table("users", &[TableChange::ModifyColumn(col)]);
2948        assert!(sql.contains("ALTER COLUMN"));
2949        assert!(sql.contains("NVARCHAR(255)"));
2950        assert!(!sql.contains("MODIFY"));
2951
2952        // ADD COLUMN
2953        let col = ColumnDef {
2954            name: "email".to_string(),
2955            sql_type: "VARCHAR(255)".to_string(),
2956            nullable: true,
2957            default: None,
2958            auto_increment: false,
2959            primary_key: false,
2960        };
2961        let sql = dialect.build_alter_table("users", &[TableChange::AddColumn(col)]);
2962        assert!(sql.contains("ADD [email]"));
2963        assert!(sql.contains("NVARCHAR(255)"));
2964
2965        // DROP COLUMN
2966        let sql =
2967            dialect.build_alter_table("users", &[TableChange::DropColumn("email".to_string())]);
2968        assert!(sql.contains("DROP COLUMN"));
2969        assert!(sql.contains("[email]"));
2970
2971        // DROP INDEX 必须指定表名
2972        let sql =
2973            dialect.build_alter_table("users", &[TableChange::DropIndex("idx_name".to_string())]);
2974        assert!(sql.contains("DROP INDEX idx_name ON [users]"));
2975    }
2976
2977    #[test]
2978    fn test_sqlserver_get_dialect() {
2979        // get_dialect 应返回 SqlServerDialect,而不是 MySqlDialect 回退
2980        let dialect = get_dialect(DbType::SqlServer);
2981        assert!(dialect.is_ok(), "SqlServer dialect should be available");
2982        let dialect = dialect.unwrap();
2983        assert_eq!(dialect.db_type(), DbType::SqlServer);
2984        // 验证不是 MySqlDialect 的回退:SQL Server 使用方括号,MySQL 使用反引号
2985        assert_eq!(dialect.quote("users"), "[users]");
2986        // SQL Server 特有功能
2987        assert_eq!(dialect.last_insert_id_sql(), Some("SCOPE_IDENTITY()"));
2988        assert_eq!(dialect.auto_increment_keyword(), "IDENTITY(1,1)");
2989    }
2990
2991    #[test]
2992    fn test_clickhouse_get_dialect_unsupported() {
2993        // ClickHouse 现已支持独立方言(不再回退到 MySqlDialect)
2994        let dialect = get_dialect(DbType::ClickHouse);
2995        assert!(dialect.is_ok(), "ClickHouse should be supported");
2996        let dialect = dialect.unwrap();
2997        assert_eq!(dialect.db_type(), DbType::ClickHouse);
2998        // ClickHouse 使用 backquote 标识符(与 MySQL 一致)
2999        assert_eq!(dialect.quote("users"), "`users`");
3000        // ClickHouse 不支持 RETURNING
3001        assert!(!dialect.supports_returning());
3002        // ClickHouse 使用 LIMIT offset, limit 分页
3003        let sql = dialect.build_pagination("SELECT * FROM t", 2, 10);
3004        assert_eq!(sql, "SELECT * FROM t LIMIT 10, 10");
3005        // ClickHouse 自增列为空字符串
3006        assert_eq!(dialect.auto_increment_keyword(), "");
3007    }
3008
3009    #[test]
3010    fn test_get_dialect_all_supported_types() {
3011        // 所有声称为支持的方言应正确返回,不支持的应返回 Err
3012        assert!(get_dialect(DbType::MySQL).is_ok());
3013        assert!(get_dialect(DbType::PostgreSQL).is_ok());
3014        assert!(get_dialect(DbType::Sqlite).is_ok());
3015        assert!(get_dialect(DbType::Oracle).is_ok());
3016        assert!(get_dialect(DbType::SqlServer).is_ok());
3017        assert!(get_dialect(DbType::OceanBase).is_ok());
3018        assert!(get_dialect(DbType::ClickHouse).is_ok());
3019        // 国产数据库 & 兼容方言
3020        assert!(get_dialect(DbType::Dameng).is_ok());
3021        assert!(get_dialect(DbType::Kingbase).is_ok());
3022        assert!(get_dialect(DbType::Db2).is_ok());
3023        assert!(get_dialect(DbType::MariaDB).is_ok());
3024        assert!(get_dialect(DbType::TiDB).is_ok());
3025        assert!(get_dialect(DbType::PolarDB).is_ok());
3026        assert!(get_dialect(DbType::GaussDB).is_ok());
3027        assert!(get_dialect(DbType::GBase).is_ok());
3028        assert!(get_dialect(DbType::Sybase).is_ok());
3029        // 不支持标准 SQL 的
3030        assert!(get_dialect(DbType::Redis).is_err());
3031        assert!(get_dialect(DbType::MongoDB).is_err());
3032        assert!(get_dialect(DbType::VectorDb).is_err());
3033        assert!(get_dialect(DbType::PureJsDb).is_err());
3034    }
3035
3036    // ===== 国产数据库兼容方言测试 =====
3037
3038    #[test]
3039    fn test_mariadb_dialect() {
3040        let dialect = get_dialect(DbType::MariaDB).unwrap();
3041        assert_eq!(dialect.db_type(), DbType::MariaDB);
3042        // MariaDB 兼容 MySQL 语法:backquote 标识符 + 反斜杠转义
3043        assert_eq!(dialect.quote("users"), "`users`");
3044        assert_eq!(dialect.escape_string("it's"), "it\\'s");
3045        assert_eq!(dialect.auto_increment_keyword(), "AUTO_INCREMENT");
3046        // MySQL 家族不支持 RETURNING(MySQL 协议层限制)
3047        assert!(!dialect.supports_returning());
3048    }
3049
3050    #[test]
3051    fn test_tidb_dialect() {
3052        let dialect = get_dialect(DbType::TiDB).unwrap();
3053        assert_eq!(dialect.db_type(), DbType::TiDB);
3054        // TiDB 兼容 MySQL 语法
3055        assert_eq!(dialect.quote("users"), "`users`");
3056        assert_eq!(dialect.escape_string("it's"), "it\\'s");
3057        assert_eq!(dialect.auto_increment_keyword(), "AUTO_INCREMENT");
3058    }
3059
3060    #[test]
3061    fn test_dameng_dialect() {
3062        let dialect = get_dialect(DbType::Dameng).unwrap();
3063        assert_eq!(dialect.db_type(), DbType::Dameng);
3064        // 达梦兼容 Oracle 语法:双引号标识符 + 单引号双写
3065        assert_eq!(dialect.quote("users"), "\"users\"");
3066        assert_eq!(dialect.escape_string("it's"), "it''s");
3067        // Oracle 兼容:使用 IDENTITY 列
3068        assert_eq!(
3069            dialect.auto_increment_keyword(),
3070            "GENERATED BY DEFAULT AS IDENTITY"
3071        );
3072        // Oracle 兼容:支持 RETURNING
3073        assert!(dialect.supports_returning());
3074    }
3075
3076    #[test]
3077    fn test_kingbase_dialect() {
3078        let dialect = get_dialect(DbType::Kingbase).unwrap();
3079        assert_eq!(dialect.db_type(), DbType::Kingbase);
3080        // 人大金仓兼容 PostgreSQL 语法:双引号标识符 + 单引号双写
3081        assert_eq!(dialect.quote("users"), "\"users\"");
3082        assert_eq!(dialect.escape_string("it's"), "it''s");
3083        // PG 兼容:支持 RETURNING
3084        assert!(dialect.supports_returning());
3085        // PG 兼容:使用 GENERATED BY DEFAULT AS IDENTITY(PG 10+ 标准)
3086        assert_eq!(
3087            dialect.auto_increment_keyword(),
3088            "GENERATED BY DEFAULT AS IDENTITY"
3089        );
3090    }
3091
3092    #[test]
3093    fn test_polardb_dialect() {
3094        let dialect = get_dialect(DbType::PolarDB).unwrap();
3095        assert_eq!(dialect.db_type(), DbType::PolarDB);
3096        // PolarDB-PG 兼容 PostgreSQL
3097        assert_eq!(dialect.quote("users"), "\"users\"");
3098        assert!(dialect.supports_returning());
3099    }
3100
3101    #[test]
3102    fn test_gaussdb_dialect() {
3103        let dialect = get_dialect(DbType::GaussDB).unwrap();
3104        assert_eq!(dialect.db_type(), DbType::GaussDB);
3105        // GaussDB 兼容 PostgreSQL
3106        assert_eq!(dialect.quote("users"), "\"users\"");
3107        assert!(dialect.supports_returning());
3108    }
3109
3110    #[test]
3111    fn test_gbase_dialect() {
3112        let dialect = get_dialect(DbType::GBase).unwrap();
3113        assert_eq!(dialect.db_type(), DbType::GBase);
3114        // GBase 8s 兼容 T-SQL(使用方括号)
3115        assert_eq!(dialect.quote("users"), "[users]");
3116    }
3117
3118    #[test]
3119    fn test_sybase_dialect() {
3120        let dialect = get_dialect(DbType::Sybase).unwrap();
3121        assert_eq!(dialect.db_type(), DbType::Sybase);
3122        // Sybase ASE 兼容 T-SQL
3123        assert_eq!(dialect.quote("users"), "[users]");
3124    }
3125
3126    // ===== DB2 独立方言测试 =====
3127
3128    #[test]
3129    fn test_db2_dialect_basic() {
3130        let dialect = get_dialect(DbType::Db2).unwrap();
3131        assert_eq!(dialect.db_type(), DbType::Db2);
3132        // DB2 使用双引号标识符
3133        assert_eq!(dialect.quote("users"), "\"users\"");
3134        // DB2 单引号双写
3135        assert_eq!(dialect.escape_string("it's"), "it''s");
3136        // DB2 使用 IDENTITY
3137        assert_eq!(
3138            dialect.auto_increment_keyword(),
3139            "GENERATED ALWAYS AS IDENTITY"
3140        );
3141        // DB2 不支持 IF EXISTS / IF NOT EXISTS(11.5 之前)
3142        assert!(!dialect.supports_if_exists());
3143        assert!(!dialect.supports_if_not_exists());
3144        // DB2 不支持 RETURNING(使用 SELECT FROM FINAL TABLE 代替)
3145        assert!(!dialect.supports_returning());
3146    }
3147
3148    #[test]
3149    fn test_db2_pagination() {
3150        let dialect = Db2Dialect;
3151        // DB2 使用 OFFSET x ROWS FETCH NEXT y ROWS ONLY
3152        let sql = dialect.build_pagination("SELECT * FROM users", 2, 10);
3153        assert_eq!(
3154            sql,
3155            "SELECT * FROM users OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY"
3156        );
3157    }
3158
3159    #[test]
3160    fn test_db2_last_insert_id() {
3161        let dialect = Db2Dialect;
3162        // DB2 使用 IDENTITY_VAL_LOCAL() 获取最后插入的 IDENTITY
3163        assert_eq!(
3164            dialect.last_insert_id_sql(),
3165            Some("SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1")
3166        );
3167    }
3168
3169    #[test]
3170    fn test_db2_concat() {
3171        let dialect = Db2Dialect;
3172        // DB2 使用 || 拼接
3173        assert_eq!(dialect.concat(&["a", "b", "c"]), "a || b || c");
3174        assert_eq!(dialect.concat(&[]), "''");
3175    }
3176
3177    #[test]
3178    fn test_db2_create_table() {
3179        let dialect = Db2Dialect;
3180        let cols = vec![ColumnDef {
3181            name: "id".to_string(),
3182            sql_type: "BIGINT".to_string(),
3183            nullable: false,
3184            default: None,
3185            auto_increment: true,
3186            primary_key: true,
3187        }];
3188        let sql = dialect.build_create_table("users", &cols);
3189        assert!(sql.contains("\"id\" BIGINT"));
3190        assert!(sql.contains("GENERATED ALWAYS AS IDENTITY"));
3191        assert!(sql.contains("PRIMARY KEY"));
3192    }
3193
3194    #[test]
3195    fn test_db2_type_mapping() {
3196        // 验证通用类型到 DB2 类型的映射
3197        assert_eq!(map_to_db2_type("BIGINT"), "BIGINT");
3198        assert_eq!(map_to_db2_type("INT"), "INTEGER");
3199        assert_eq!(map_to_db2_type("INTEGER"), "INTEGER");
3200        assert_eq!(map_to_db2_type("TINYINT"), "SMALLINT");
3201        assert_eq!(map_to_db2_type("SMALLINT"), "SMALLINT");
3202        assert_eq!(map_to_db2_type("TEXT"), "CLOB(2G)");
3203        assert_eq!(map_to_db2_type("LONGTEXT"), "CLOB(2G)");
3204        assert_eq!(map_to_db2_type("BOOLEAN"), "SMALLINT");
3205        assert_eq!(map_to_db2_type("BOOL"), "SMALLINT");
3206        assert_eq!(map_to_db2_type("DATETIME"), "TIMESTAMP");
3207        assert_eq!(map_to_db2_type("TIMESTAMP"), "TIMESTAMP");
3208        assert_eq!(map_to_db2_type("DATE"), "DATE");
3209        assert_eq!(map_to_db2_type("VARCHAR(255)"), "VARCHAR(255)");
3210    }
3211
3212    // ===== ClickHouse 独立方言测试 =====
3213
3214    #[test]
3215    fn test_clickhouse_dialect_basic() {
3216        let dialect = get_dialect(DbType::ClickHouse).unwrap();
3217        assert_eq!(dialect.db_type(), DbType::ClickHouse);
3218        // ClickHouse 使用 backquote 标识符(与 MySQL 一致)
3219        assert_eq!(dialect.quote("users"), "`users`");
3220        // ClickHouse 反斜杠转义(与 MySQL 一致)
3221        assert_eq!(dialect.escape_string("it's"), "it\\'s");
3222        // ClickHouse 不支持 RETURNING
3223        assert!(!dialect.supports_returning());
3224        // ClickHouse 不支持自增列
3225        assert_eq!(dialect.auto_increment_keyword(), "");
3226        // ClickHouse 支持 IF EXISTS / IF NOT EXISTS
3227        assert!(dialect.supports_if_exists());
3228        assert!(dialect.supports_if_not_exists());
3229    }
3230
3231    #[test]
3232    fn test_clickhouse_type_mapping() {
3233        assert_eq!(map_to_clickhouse_type("BIGINT"), "Int64");
3234        assert_eq!(map_to_clickhouse_type("INT"), "Int32");
3235        assert_eq!(map_to_clickhouse_type("INTEGER"), "Int32");
3236        assert_eq!(map_to_clickhouse_type("TINYINT"), "Int16");
3237        assert_eq!(map_to_clickhouse_type("SMALLINT"), "Int16");
3238        assert_eq!(map_to_clickhouse_type("VARCHAR(255)"), "String");
3239        assert_eq!(map_to_clickhouse_type("TEXT"), "String");
3240        assert_eq!(map_to_clickhouse_type("BOOLEAN"), "UInt8");
3241        assert_eq!(map_to_clickhouse_type("BOOL"), "UInt8");
3242        assert_eq!(map_to_clickhouse_type("FLOAT"), "Float32");
3243        assert_eq!(map_to_clickhouse_type("DOUBLE"), "Float64");
3244        assert_eq!(map_to_clickhouse_type("DATETIME"), "DateTime");
3245        assert_eq!(map_to_clickhouse_type("TIMESTAMP"), "DateTime");
3246        assert_eq!(map_to_clickhouse_type("DATE"), "Date");
3247    }
3248
3249    #[test]
3250    fn test_clickhouse_create_table() {
3251        let dialect = ClickHouseDialect;
3252        let cols = vec![ColumnDef {
3253            name: "id".to_string(),
3254            sql_type: "BIGINT".to_string(),
3255            nullable: false,
3256            default: None,
3257            auto_increment: false, // ClickHouse 不支持自增
3258            primary_key: true,
3259        }];
3260        let sql = dialect.build_create_table("users", &cols);
3261        // 必须包含 ENGINE = MergeTree()
3262        assert!(
3263            sql.contains("ENGINE = MergeTree()"),
3264            "ClickHouse CREATE TABLE 必须指定 ENGINE: {}",
3265            sql
3266        );
3267        assert!(sql.contains("`id` Int64"));
3268        assert!(sql.contains("PRIMARY KEY"));
3269    }
3270
3271    #[test]
3272    fn test_clickhouse_json_extract() {
3273        let dialect = ClickHouseDialect;
3274        let sql = dialect.json_extract("data", "$.name");
3275        assert!(
3276            sql.contains("JSONExtractString"),
3277            "ClickHouse 应使用 JSONExtractString: {}",
3278            sql
3279        );
3280    }
3281
3282    #[test]
3283    fn test_clickhouse_concat() {
3284        let dialect = ClickHouseDialect;
3285        // ClickHouse 使用 concat() 函数
3286        assert_eq!(dialect.concat(&["a", "b", "c"]), "concat(a, b, c)");
3287        assert_eq!(dialect.concat(&[]), "''");
3288    }
3289
3290    // ===== DbType 国产数据库变体测试 =====
3291
3292    #[test]
3293    fn test_db_type_dameng_str() {
3294        assert_eq!(DbType::Dameng.as_str(), "dameng");
3295        assert_eq!(DbType::from_str("dameng"), Some(DbType::Dameng));
3296        assert_eq!(DbType::from_str("DM"), Some(DbType::Dameng));
3297        assert_eq!(DbType::from_str("dm8"), Some(DbType::Dameng));
3298        assert_eq!(DbType::Dameng.default_port(), 5236);
3299    }
3300
3301    #[test]
3302    fn test_db_type_kingbase_str() {
3303        assert_eq!(DbType::Kingbase.as_str(), "kingbase");
3304        assert_eq!(DbType::from_str("kingbase"), Some(DbType::Kingbase));
3305        assert_eq!(DbType::Kingbase.default_port(), 54321);
3306    }
3307
3308    #[test]
3309    fn test_db_type_db2_str() {
3310        assert_eq!(DbType::Db2.as_str(), "db2");
3311        assert_eq!(DbType::from_str("db2"), Some(DbType::Db2));
3312        assert_eq!(DbType::Db2.default_port(), 50000);
3313    }
3314
3315    #[test]
3316    fn test_db_type_mariadb_str() {
3317        assert_eq!(DbType::MariaDB.as_str(), "mariadb");
3318        assert_eq!(DbType::from_str("mariadb"), Some(DbType::MariaDB));
3319        assert_eq!(DbType::MariaDB.default_port(), 3306);
3320    }
3321
3322    #[test]
3323    fn test_db_type_tidb_str() {
3324        assert_eq!(DbType::TiDB.as_str(), "tidb");
3325        assert_eq!(DbType::from_str("tidb"), Some(DbType::TiDB));
3326        assert_eq!(DbType::TiDB.default_port(), 4000);
3327    }
3328
3329    #[test]
3330    fn test_db_type_polardb_str() {
3331        assert_eq!(DbType::PolarDB.as_str(), "polardb");
3332        assert_eq!(DbType::from_str("polardb"), Some(DbType::PolarDB));
3333        assert_eq!(DbType::PolarDB.default_port(), 5432);
3334    }
3335
3336    #[test]
3337    fn test_db_type_gaussdb_str() {
3338        assert_eq!(DbType::GaussDB.as_str(), "gaussdb");
3339        assert_eq!(DbType::from_str("gaussdb"), Some(DbType::GaussDB));
3340        assert_eq!(DbType::GaussDB.default_port(), 25308);
3341    }
3342
3343    #[test]
3344    fn test_db_type_gbase_str() {
3345        assert_eq!(DbType::GBase.as_str(), "gbase");
3346        assert_eq!(DbType::from_str("gbase"), Some(DbType::GBase));
3347        assert_eq!(DbType::GBase.default_port(), 9088);
3348    }
3349
3350    #[test]
3351    fn test_db_type_sybase_str() {
3352        assert_eq!(DbType::Sybase.as_str(), "sybase");
3353        assert_eq!(DbType::from_str("sybase"), Some(DbType::Sybase));
3354        assert_eq!(DbType::Sybase.default_port(), 5000);
3355    }
3356
3357    #[test]
3358    fn test_db_type_family_classification() {
3359        // MySQL 家族
3360        assert!(DbType::MySQL.is_mysql_family());
3361        assert!(DbType::MariaDB.is_mysql_family());
3362        assert!(DbType::TiDB.is_mysql_family());
3363        assert!(DbType::OceanBase.is_mysql_family());
3364        assert!(!DbType::PostgreSQL.is_mysql_family());
3365
3366        // PostgreSQL 家族
3367        assert!(DbType::PostgreSQL.is_postgres_family());
3368        assert!(DbType::Kingbase.is_postgres_family());
3369        assert!(DbType::GaussDB.is_postgres_family());
3370        assert!(!DbType::MySQL.is_postgres_family());
3371
3372        // Oracle 家族
3373        assert!(DbType::Oracle.is_oracle_family());
3374        assert!(DbType::Dameng.is_oracle_family());
3375        assert!(!DbType::MySQL.is_oracle_family());
3376    }
3377
3378    #[test]
3379    fn test_db_type_supports_stored_procedure_extended() {
3380        // 所有 SQL 数据库应支持存储过程
3381        assert!(DbType::Dameng.supports_stored_procedure());
3382        assert!(DbType::Kingbase.supports_stored_procedure());
3383        assert!(DbType::Db2.supports_stored_procedure());
3384        assert!(DbType::MariaDB.supports_stored_procedure());
3385        assert!(DbType::TiDB.supports_stored_procedure());
3386        assert!(DbType::PolarDB.supports_stored_procedure());
3387        assert!(DbType::GaussDB.supports_stored_procedure());
3388        assert!(DbType::GBase.supports_stored_procedure());
3389        assert!(DbType::Sybase.supports_stored_procedure());
3390    }
3391
3392    // ===== L-4 修复:表名/列名长度校验 =====
3393
3394    #[test]
3395    fn test_l4_max_identifier_len_constant() {
3396        // MAX_IDENTIFIER_LEN 应为 63(PostgreSQL 最严格值)
3397        assert_eq!(MAX_IDENTIFIER_LEN, 63);
3398    }
3399
3400    #[test]
3401    fn test_l4_quote_checked_valid_identifier() {
3402        let dialect = MySqlDialect;
3403        assert_eq!(dialect.quote_checked("users").unwrap(), "`users`");
3404        assert_eq!(dialect.quote_checked("user_id").unwrap(), "`user_id`");
3405        // 边界:恰好 63 字符
3406        let name_63 = "a".repeat(63);
3407        assert!(dialect.quote_checked(&name_63).is_ok());
3408    }
3409
3410    #[test]
3411    fn test_l4_quote_checked_rejects_too_long() {
3412        let dialect = MySqlDialect;
3413        let long_name = "a".repeat(64); // 64 > 63
3414        let result = dialect.quote_checked(&long_name);
3415        assert!(result.is_err());
3416        match result {
3417            Err(DbError::InvalidInput(msg)) => {
3418                assert!(
3419                    msg.contains("too long"),
3420                    "expected 'too long' error, got: {}",
3421                    msg
3422                );
3423            }
3424            _ => panic!("Expected DbError::InvalidInput"),
3425        }
3426    }
3427
3428    #[test]
3429    fn test_l4_quote_checked_rejects_empty() {
3430        let dialect = MySqlDialect;
3431        let result = dialect.quote_checked("");
3432        assert!(result.is_err());
3433    }
3434
3435    #[test]
3436    fn test_l4_quote_checked_rejects_sql_injection() {
3437        let dialect = MySqlDialect;
3438        // 含分号
3439        assert!(dialect.quote_checked("users; DROP TABLE users").is_err());
3440        // 含引号
3441        assert!(dialect.quote_checked("user'name").is_err());
3442        // 含空格
3443        assert!(dialect.quote_checked("user name").is_err());
3444        // 数字开头
3445        assert!(dialect.quote_checked("1users").is_err());
3446        // 含点号
3447        assert!(dialect.quote_checked("schema.table").is_err());
3448    }
3449
3450    #[test]
3451    fn test_l4_quote_checked_postgres() {
3452        let dialect = PostgreSqlDialect;
3453        assert_eq!(dialect.quote_checked("users").unwrap(), "\"users\"");
3454        assert!(dialect.quote_checked(&"a".repeat(64)).is_err());
3455    }
3456
3457    #[test]
3458    fn test_l4_quote_checked_sqlite() {
3459        let dialect = SqliteDialect;
3460        assert_eq!(dialect.quote_checked("users").unwrap(), "\"users\"");
3461        assert!(dialect.quote_checked(&"a".repeat(64)).is_err());
3462    }
3463
3464    #[test]
3465    fn test_l4_quote_checked_oracle() {
3466        let dialect = OracleDialect;
3467        assert_eq!(dialect.quote_checked("users").unwrap(), "\"users\"");
3468        assert!(dialect.quote_checked(&"a".repeat(64)).is_err());
3469    }
3470
3471    #[test]
3472    fn test_l4_quote_checked_sql_server() {
3473        let dialect = SqlServerDialect;
3474        assert_eq!(dialect.quote_checked("users").unwrap(), "[users]");
3475        assert!(dialect.quote_checked(&"a".repeat(64)).is_err());
3476    }
3477
3478    // ---- DuckDB 方言测试(TASK-033~036) ----
3479
3480    #[test]
3481    fn test_duckdb_quote() {
3482        let dialect = DuckDBDialect;
3483        assert_eq!(dialect.quote("users"), "\"users\"");
3484        assert_eq!(dialect.quote("user\"id"), "\"user\"\"id\"");
3485    }
3486
3487    #[test]
3488    fn test_duckdb_escape() {
3489        let dialect = DuckDBDialect;
3490        assert_eq!(dialect.escape_string("hello"), "hello");
3491        assert_eq!(dialect.escape_string("it's"), "it''s");
3492    }
3493
3494    #[test]
3495    fn test_duckdb_pagination() {
3496        let dialect = DuckDBDialect;
3497        let sql = dialect.build_pagination("SELECT * FROM users", 2, 10);
3498        assert_eq!(sql, "SELECT * FROM users LIMIT 10 OFFSET 10");
3499    }
3500
3501    #[test]
3502    fn test_duckdb_supports() {
3503        let dialect = DuckDBDialect;
3504        assert!(!dialect.supports_returning());
3505        assert!(!dialect.supports_lock_for_update());
3506        assert!(!dialect.supports_lock_shared());
3507        assert!(dialect.supports_if_exists());
3508        assert!(dialect.supports_if_not_exists());
3509    }
3510
3511    #[test]
3512    fn test_duckdb_insert_or_ignore() {
3513        let dialect = DuckDBDialect;
3514        let sql = dialect.build_insert_or_ignore_prefix("users");
3515        assert_eq!(sql, "INSERT OR IGNORE INTO \"users\"");
3516    }
3517
3518    #[test]
3519    fn test_duckdb_create_table() {
3520        let dialect = DuckDBDialect;
3521        let columns = vec![
3522            ColumnDef {
3523                name: "id".to_string(),
3524                sql_type: "BIGINT".to_string(),
3525                nullable: false,
3526                default: None,
3527                auto_increment: true,
3528                primary_key: true,
3529            },
3530            ColumnDef {
3531                name: "name".to_string(),
3532                sql_type: "VARCHAR(255)".to_string(),
3533                nullable: false,
3534                default: None,
3535                auto_increment: false,
3536                primary_key: false,
3537            },
3538        ];
3539        let sql = dialect.build_create_table("users", &columns);
3540        assert!(sql.contains("CREATE TABLE \"users\""));
3541        assert!(sql.contains("\"id\" BIGINT PRIMARY KEY"));
3542        assert!(sql.contains("\"name\" VARCHAR(255)"));
3543    }
3544
3545    #[test]
3546    fn test_duckdb_alter_table() {
3547        let dialect = DuckDBDialect;
3548        let changes = vec![TableChange::AddColumn(ColumnDef {
3549            name: "age".to_string(),
3550            sql_type: "INTEGER".to_string(),
3551            nullable: true,
3552            default: None,
3553            auto_increment: false,
3554            primary_key: false,
3555        })];
3556        let sql = dialect.build_alter_table("users", &changes);
3557        assert!(sql.contains("ALTER TABLE \"users\" ADD COLUMN \"age\" INTEGER"));
3558    }
3559
3560    #[test]
3561    fn test_duckdb_json_extract() {
3562        let dialect = DuckDBDialect;
3563        let sql = dialect.json_extract("data", "$.user.name");
3564        assert!(sql.contains("->"));
3565        assert!(sql.contains("user.name"));
3566    }
3567
3568    #[test]
3569    fn test_duckdb_concat() {
3570        let dialect = DuckDBDialect;
3571        let sql = dialect.concat(&["a", "b", "c"]);
3572        assert_eq!(sql, "a || b || c");
3573    }
3574
3575    #[test]
3576    fn test_duckdb_bool_to_int() {
3577        let dialect = DuckDBDialect;
3578        let sql = dialect.bool_to_int("active");
3579        assert!(sql.contains("CASE WHEN"));
3580        assert!(sql.contains("THEN 1"));
3581        assert!(sql.contains("ELSE 0"));
3582    }
3583
3584    #[test]
3585    fn test_get_dialect_duckdb() {
3586        let result = get_dialect(DbType::DuckDB);
3587        assert!(result.is_ok());
3588        let dialect = result.unwrap();
3589        assert_eq!(dialect.db_type(), DbType::DuckDB);
3590    }
3591}
3592
3593// ============================================================================
3594// v3.4.0 M3-T3:enum dispatch 优化(perf-enum-dispatch feature)
3595// ============================================================================
3596
3597/// 方言种类枚举(用于 match 分发替代 Box<dyn Dialect> vtable 查找)
3598///
3599/// 当 `perf-enum-dispatch` feature 启用时,可通过 `DialectKind` 直接 match 分发
3600/// 到具体方言实现,避免动态分发的 vtable 间接寻址开销。
3601#[cfg(feature = "perf-enum-dispatch")]
3602#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3603pub enum DialectKind {
3604    /// MySQL 方言
3605    MySQL,
3606    /// PostgreSQL 方言
3607    PostgreSQL,
3608    /// SQLite 方言
3609    SQLite,
3610    /// Oracle 方言
3611    Oracle,
3612    /// SQL Server 方言
3613    MSSQL,
3614}
3615
3616#[cfg(feature = "perf-enum-dispatch")]
3617impl DialectKind {
3618    /// 从 `DbType` 创建 `DialectKind`(不支持返回 None)
3619    pub fn from_db_type(db_type: DbType) -> Option<Self> {
3620        match db_type {
3621            DbType::MySQL | DbType::MariaDB | DbType::TiDB | DbType::OceanBase => Some(Self::MySQL),
3622            DbType::PostgreSQL | DbType::Kingbase | DbType::PolarDB | DbType::GaussDB => {
3623                Some(Self::PostgreSQL)
3624            }
3625            DbType::Sqlite => Some(Self::SQLite),
3626            DbType::Oracle | DbType::Dameng => Some(Self::Oracle),
3627            DbType::SqlServer | DbType::Sybase | DbType::GBase => Some(Self::MSSQL),
3628            _ => None,
3629        }
3630    }
3631
3632    /// 引用标识符(match 分发,避免 vtable 查找)
3633    pub fn quote(&self, identifier: &str) -> String {
3634        match self {
3635            Self::MySQL => MySqlDialect.quote(identifier),
3636            Self::PostgreSQL => PostgreSqlDialect.quote(identifier),
3637            Self::SQLite => SqliteDialect.quote(identifier),
3638            Self::Oracle => OracleDialect.quote(identifier),
3639            Self::MSSQL => SqlServerDialect.quote(identifier),
3640        }
3641    }
3642
3643    /// 转义字符串字面量(match 分发)
3644    pub fn escape_string(&self, s: &str) -> String {
3645        match self {
3646            Self::MySQL => MySqlDialect.escape_string(s),
3647            Self::PostgreSQL => PostgreSqlDialect.escape_string(s),
3648            Self::SQLite => SqliteDialect.escape_string(s),
3649            Self::Oracle => OracleDialect.escape_string(s),
3650            Self::MSSQL => SqlServerDialect.escape_string(s),
3651        }
3652    }
3653
3654    /// 返回对应的 `DbType`
3655    pub fn db_type(&self) -> DbType {
3656        match self {
3657            Self::MySQL => DbType::MySQL,
3658            Self::PostgreSQL => DbType::PostgreSQL,
3659            Self::SQLite => DbType::Sqlite,
3660            Self::Oracle => DbType::Oracle,
3661            Self::MSSQL => DbType::SqlServer,
3662        }
3663    }
3664
3665    /// 转为 `Box<dyn Dialect>`
3666    pub fn to_dialect(&self) -> Box<dyn Dialect> {
3667        match self {
3668            Self::MySQL => Box::new(MySqlDialect),
3669            Self::PostgreSQL => Box::new(PostgreSqlDialect),
3670            Self::SQLite => Box::new(SqliteDialect),
3671            Self::Oracle => Box::new(OracleDialect),
3672            Self::MSSQL => Box::new(SqlServerDialect),
3673        }
3674    }
3675}
3676
3677#[cfg(all(test, feature = "perf-enum-dispatch"))]
3678mod enum_dispatch_tests {
3679    use super::*;
3680
3681    #[test]
3682    fn test_dialect_kind_from_db_type() {
3683        assert_eq!(
3684            DialectKind::from_db_type(DbType::MySQL),
3685            Some(DialectKind::MySQL)
3686        );
3687        assert_eq!(
3688            DialectKind::from_db_type(DbType::PostgreSQL),
3689            Some(DialectKind::PostgreSQL)
3690        );
3691        assert_eq!(
3692            DialectKind::from_db_type(DbType::Sqlite),
3693            Some(DialectKind::SQLite)
3694        );
3695        assert_eq!(
3696            DialectKind::from_db_type(DbType::Oracle),
3697            Some(DialectKind::Oracle)
3698        );
3699        assert_eq!(
3700            DialectKind::from_db_type(DbType::SqlServer),
3701            Some(DialectKind::MSSQL)
3702        );
3703        assert_eq!(DialectKind::from_db_type(DbType::Redis), None);
3704    }
3705
3706    #[test]
3707    fn test_dialect_kind_quote_diff() {
3708        let kinds = [
3709            DialectKind::MySQL,
3710            DialectKind::PostgreSQL,
3711            DialectKind::SQLite,
3712            DialectKind::Oracle,
3713            DialectKind::MSSQL,
3714        ];
3715        for kind in &kinds {
3716            let enum_result = kind.quote("users");
3717            let box_result = kind.to_dialect().quote("users");
3718            assert_eq!(enum_result, box_result);
3719        }
3720    }
3721
3722    #[test]
3723    fn test_dialect_kind_escape_diff() {
3724        let kinds = [
3725            DialectKind::MySQL,
3726            DialectKind::PostgreSQL,
3727            DialectKind::SQLite,
3728            DialectKind::Oracle,
3729            DialectKind::MSSQL,
3730        ];
3731        for kind in &kinds {
3732            let enum_result = kind.escape_string("it's");
3733            let box_result = kind.to_dialect().escape_string("it's");
3734            assert_eq!(enum_result, box_result);
3735        }
3736    }
3737}