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