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
1389// 架构说明:build_create_table / build_alter_table / build_drop_table 在四个方言中
1390// 存在部分重复代码。当前选择保持各方言独立实现以确保 SQL 方言差异的显式性,
1391// 避免过度抽象导致维护复杂度上升。如需重构,可抽出公共构建器(参见 dialect 重构 RFC)。
1392
1393// ============================================================================
1394// 兼容方言(基于现有方言委派实现)
1395//
1396// 以下方言在 SQL 语法上与某个基础方言完全兼容,仅在 db_type() 上有区别。
1397// 使用宏减少重复代码,保持维护性。
1398// ============================================================================
1399
1400/// 将方言实现委派给基础方言的宏
1401///
1402/// `$wrapper`:新方言结构体名
1403/// `$base`:基础方言结构体名(如 MySqlDialect)
1404/// `$db_type`:返回的 DbType 变体
1405macro_rules! delegate_dialect_to {
1406    ($wrapper:ident, $base:ident, $db_type:expr) => {
1407        /// 兼容方言(委派给基础方言实现)
1408        #[derive(Debug, Clone)]
1409        pub struct $wrapper;
1410
1411        impl Dialect for $wrapper {
1412            fn clone_box(&self) -> Box<dyn Dialect> {
1413                Box::new($wrapper)
1414            }
1415
1416            fn db_type(&self) -> DbType {
1417                $db_type
1418            }
1419            fn quote(&self, identifier: &str) -> String {
1420                $base.quote(identifier)
1421            }
1422            fn escape_string(&self, s: &str) -> String {
1423                $base.escape_string(s)
1424            }
1425            fn supports_returning(&self) -> bool {
1426                $base.supports_returning()
1427            }
1428            fn build_pagination(&self, sql: &str, page: u64, limit: u64) -> String {
1429                $base.build_pagination(sql, page, limit)
1430            }
1431            fn json_type(&self) -> &'static str {
1432                $base.json_type()
1433            }
1434            fn json_extract(&self, column: &str, path: &str) -> String {
1435                $base.json_extract(column, path)
1436            }
1437            fn full_text_search(&self, columns: &[&str], keyword: &str) -> String {
1438                $base.full_text_search(columns, keyword)
1439            }
1440            fn bool_to_int(&self, expr: &str) -> String {
1441                $base.bool_to_int(expr)
1442            }
1443            fn concat(&self, parts: &[&str]) -> String {
1444                $base.concat(parts)
1445            }
1446            fn supports_if_exists(&self) -> bool {
1447                $base.supports_if_exists()
1448            }
1449            fn supports_if_not_exists(&self) -> bool {
1450                $base.supports_if_not_exists()
1451            }
1452            fn auto_increment_keyword(&self) -> &'static str {
1453                $base.auto_increment_keyword()
1454            }
1455            fn last_insert_id_sql(&self) -> Option<&'static str> {
1456                $base.last_insert_id_sql()
1457            }
1458            fn build_create_table(&self, table: &str, columns: &[ColumnDef]) -> String {
1459                $base.build_create_table(table, columns)
1460            }
1461            fn build_alter_table(&self, table: &str, changes: &[TableChange]) -> String {
1462                $base.build_alter_table(table, changes)
1463            }
1464            fn build_drop_table(&self, table: &str, if_exists: bool) -> String {
1465                $base.build_drop_table(table, if_exists)
1466            }
1467        }
1468    };
1469}
1470
1471// MariaDB:MySQL 兼容方言
1472delegate_dialect_to!(MariaDbDialect, MySqlDialect, DbType::MariaDB);
1473
1474// TiDB:MySQL 兼容分布式数据库
1475delegate_dialect_to!(TiDbDialect, MySqlDialect, DbType::TiDB);
1476
1477// KingbaseES:人大金仓,PostgreSQL 兼容方言
1478delegate_dialect_to!(KingbaseDialect, PostgreSqlDialect, DbType::Kingbase);
1479
1480// PolarDB:阿里云,PostgreSQL 兼容(PG 版本)
1481delegate_dialect_to!(PolarDbDialect, PostgreSqlDialect, DbType::PolarDB);
1482
1483// GaussDB:华为云,PostgreSQL 兼容分布式数据库
1484delegate_dialect_to!(GaussDbDialect, PostgreSqlDialect, DbType::GaussDB);
1485
1486// Dameng:达梦 DM8,Oracle 兼容方言
1487delegate_dialect_to!(DamengDialect, OracleDialect, DbType::Dameng);
1488
1489// Sybase ASE:与 SQL Server T-SQL 高度兼容
1490delegate_dialect_to!(SybaseDialect, SqlServerDialect, DbType::Sybase);
1491
1492// GBase 8s:南大通用,Informix 兼容方言,SQL 语法接近 T-SQL
1493delegate_dialect_to!(GBaseDialect, SqlServerDialect, DbType::GBase);
1494
1495// ============================================================================
1496// ClickHouse 方言(独立实现)
1497//
1498// ClickHouse 是列式 OLAP 数据库,有独特的类型系统和函数:
1499// - 使用 backtick 标识符(与 MySQL 一致)
1500// - 字符串字面量单引号转义为 \'(与 MySQL 一致)
1501// - 不支持事务、不支持 RETURNING
1502// - 分页使用 LIMIT offset, limit 语法(与 MySQL 一致)
1503// - 自增列不支持(使用 UUID 或物化列)
1504// - 类型系统:String/UInt64/Int64/Float64/DateTime 等
1505// ============================================================================
1506
1507/// ClickHouse 方言实现(列式 OLAP 数据库)
1508#[derive(Debug, Clone)]
1509pub struct ClickHouseDialect;
1510
1511impl Dialect for ClickHouseDialect {
1512    fn clone_box(&self) -> Box<dyn Dialect> {
1513        Box::new(ClickHouseDialect)
1514    }
1515
1516    fn db_type(&self) -> DbType {
1517        DbType::ClickHouse
1518    }
1519
1520    fn quote(&self, identifier: &str) -> String {
1521        // ClickHouse 使用 backquote 包裹标识符(与 MySQL 一致)
1522        format!("`{}`", identifier.replace('`', "``"))
1523    }
1524
1525    fn escape_string(&self, s: &str) -> String {
1526        // ClickHouse 使用反斜杠转义(与 MySQL 一致)
1527        let mut escaped = String::with_capacity(s.len() * 2);
1528        for c in s.chars() {
1529            match c {
1530                '\'' => escaped.push_str("\\'"),
1531                '\\' => escaped.push_str("\\\\"),
1532                '\n' => escaped.push_str("\\n"),
1533                '\r' => escaped.push_str("\\r"),
1534                '\t' => escaped.push_str("\\t"),
1535                _ => escaped.push(c),
1536            }
1537        }
1538        escaped
1539    }
1540
1541    fn supports_returning(&self) -> bool {
1542        // ClickHouse 不支持 RETURNING 子句
1543        false
1544    }
1545
1546    fn build_pagination(&self, sql: &str, page: u64, limit: u64) -> String {
1547        // ClickHouse 使用 LIMIT offset, limit 语法
1548        let offset = page.saturating_sub(1).saturating_mul(limit);
1549        format!("{} LIMIT {}, {}", sql, offset, limit)
1550    }
1551
1552    fn json_type(&self) -> &'static str {
1553        // ClickHouse 使用 String 存储 JSON,或使用 JSON 类型(实验性)
1554        "String"
1555    }
1556
1557    fn json_extract(&self, column: &str, path: &str) -> String {
1558        // ClickHouse 使用 JSONExtractString 函数
1559        let normalized = if path.starts_with('$') {
1560            path.to_string()
1561        } else {
1562            format!("$.{}", path)
1563        };
1564        format!(
1565            "JSONExtractString({}, '{}')",
1566            column,
1567            self.escape_string(&normalized)
1568        )
1569    }
1570
1571    fn full_text_search(&self, columns: &[&str], keyword: &str) -> String {
1572        // ClickHouse 使用 position() + like 进行全文检索(无原生 FTS)
1573        if columns.is_empty() {
1574            return "0".to_string();
1575        }
1576        let escaped = self.escape_string(keyword);
1577        let parts: Vec<String> = columns
1578            .iter()
1579            .map(|c| format!("position({}, '{}') > 0", c, escaped))
1580            .collect();
1581        parts.join(" OR ")
1582    }
1583
1584    fn bool_to_int(&self, expr: &str) -> String {
1585        // ClickHouse 支持 toUInt8 转换
1586        format!("toUInt8({})", expr)
1587    }
1588
1589    fn concat(&self, parts: &[&str]) -> String {
1590        // ClickHouse 使用 concat() 函数
1591        if parts.is_empty() {
1592            return "''".to_string();
1593        }
1594        format!("concat({})", parts.join(", "))
1595    }
1596
1597    fn supports_if_exists(&self) -> bool {
1598        true
1599    }
1600
1601    fn supports_if_not_exists(&self) -> bool {
1602        true
1603    }
1604
1605    fn auto_increment_keyword(&self) -> &'static str {
1606        // ClickHouse 不支持自增列,使用 UUID 默认值
1607        ""
1608    }
1609
1610    fn last_insert_id_sql(&self) -> Option<&'static str> {
1611        // ClickHouse 不支持 last_insert_id
1612        None
1613    }
1614
1615    fn build_create_table(&self, table: &str, columns: &[ColumnDef]) -> String {
1616        let cols: Vec<String> = columns
1617            .iter()
1618            .map(|col| {
1619                let ch_type = map_to_clickhouse_type(&col.sql_type);
1620                let mut sql = format!("{} {}", self.quote(&col.name), ch_type);
1621                if let Some(default) = &col.default {
1622                    sql.push_str(&format!(" DEFAULT {}", default));
1623                }
1624                if col.primary_key {
1625                    sql.push_str(" PRIMARY KEY");
1626                }
1627                sql
1628            })
1629            .collect();
1630
1631        // ClickHouse 必须指定 Engine,默认使用 MergeTree
1632        format!(
1633            "CREATE TABLE {} ({}) ENGINE = MergeTree()",
1634            self.quote(table),
1635            cols.join(", ")
1636        )
1637    }
1638
1639    fn build_alter_table(&self, table: &str, changes: &[TableChange]) -> String {
1640        let stmts: Vec<String> = changes
1641            .iter()
1642            .map(|change| match change {
1643                TableChange::AddColumn(col) => {
1644                    let ch_type = map_to_clickhouse_type(&col.sql_type);
1645                    format!(
1646                        "ALTER TABLE {} ADD COLUMN {} {}",
1647                        self.quote(table),
1648                        self.quote(&col.name),
1649                        ch_type
1650                    )
1651                }
1652                TableChange::DropColumn(name) => {
1653                    format!(
1654                        "ALTER TABLE {} DROP COLUMN {}",
1655                        self.quote(table),
1656                        self.quote(name)
1657                    )
1658                }
1659                TableChange::ModifyColumn(col) => {
1660                    let ch_type = map_to_clickhouse_type(&col.sql_type);
1661                    format!(
1662                        "ALTER TABLE {} MODIFY COLUMN {} {}",
1663                        self.quote(table),
1664                        self.quote(&col.name),
1665                        ch_type
1666                    )
1667                }
1668                TableChange::AddIndex(name, cols) => {
1669                    format!(
1670                        "ALTER TABLE {} ADD INDEX {} ({})",
1671                        self.quote(table),
1672                        name,
1673                        cols.join(", ")
1674                    )
1675                }
1676                TableChange::DropIndex(name) => {
1677                    format!("ALTER TABLE {} DROP INDEX {}", self.quote(table), name)
1678                }
1679                TableChange::AddForeignKey { .. } => {
1680                    // ClickHouse 不支持外键,跳过
1681                    String::new()
1682                }
1683            })
1684            .filter(|s| !s.is_empty())
1685            .collect();
1686
1687        stmts.join("; ")
1688    }
1689}
1690
1691/// 将通用 SQL 类型映射为 ClickHouse 类型
1692///
1693/// - BIGINT → Int64
1694/// - INT/INTEGER → Int32
1695/// - VARCHAR(n)/TEXT → String
1696/// - BOOLEAN/BOOL → UInt8
1697/// - FLOAT → Float32
1698/// - DOUBLE → Float64
1699/// - DATETIME/TIMESTAMP → DateTime
1700fn map_to_clickhouse_type(sql_type: &str) -> String {
1701    let upper = sql_type.to_uppercase();
1702    let trimmed = upper.trim();
1703
1704    if trimmed.starts_with("BIGINT") {
1705        "Int64".to_string()
1706    } else if matches!(trimmed, "INT" | "INTEGER") {
1707        "Int32".to_string()
1708    } else if matches!(trimmed, "TINYINT" | "SMALLINT") {
1709        "Int16".to_string()
1710    } else if trimmed.starts_with("VARCHAR")
1711        || trimmed.starts_with("CHAR")
1712        || matches!(trimmed, "TEXT" | "MEDIUMTEXT" | "LONGTEXT" | "TINYTEXT")
1713    {
1714        "String".to_string()
1715    } else if matches!(trimmed, "BOOLEAN" | "BOOL") {
1716        "UInt8".to_string()
1717    } else if matches!(trimmed, "FLOAT" | "REAL") {
1718        "Float32".to_string()
1719    } else if matches!(trimmed, "DOUBLE" | "DOUBLE PRECISION") {
1720        "Float64".to_string()
1721    } else if matches!(trimmed, "DATETIME" | "TIMESTAMP") {
1722        "DateTime".to_string()
1723    } else if matches!(trimmed, "DATE") {
1724        "Date".to_string()
1725    } else if trimmed.starts_with("DECIMAL") || trimmed.starts_with("NUMERIC") {
1726        "Decimal(38, 4)".to_string()
1727    } else {
1728        sql_type.to_string()
1729    }
1730}
1731
1732// ============================================================================
1733// DuckDB 方言(嵌入式 OLAP 数据库)
1734//
1735// DuckDB 语法特性:
1736// - 双引号标识符(标准 SQL 风格)
1737// - 字符串字面量单引号转义为 ''(标准 SQL 风格)
1738// - 不支持 RETURNING 子句
1739// - 分页使用 LIMIT x OFFSET y(与 PostgreSQL 一致)
1740// - 自增列使用 SERIAL 或 BIGINT PRIMARY KEY
1741// - 类型系统:BIGINT/INTEGER/VARCHAR/BOOLEAN/DOUBLE/TIMESTAMP/DATE 等
1742// - 支持 INSERT OR IGNORE(与 SQLite 一致)
1743// ============================================================================
1744
1745/// DuckDB 方言实现(嵌入式 OLAP 数据库)
1746#[derive(Debug, Clone)]
1747pub struct DuckDBDialect;
1748
1749impl Dialect for DuckDBDialect {
1750    fn clone_box(&self) -> Box<dyn Dialect> {
1751        Box::new(DuckDBDialect)
1752    }
1753
1754    fn db_type(&self) -> DbType {
1755        DbType::DuckDB
1756    }
1757
1758    fn quote(&self, identifier: &str) -> String {
1759        // DuckDB 使用双引号包裹标识符(标准 SQL 风格)
1760        format!("\"{}\"", identifier.replace('"', "\"\""))
1761    }
1762
1763    fn escape_string(&self, s: &str) -> String {
1764        // DuckDB 标准转义:单引号双写('O''Brien')
1765        let mut escaped = String::with_capacity(s.len() * 2);
1766        for c in s.chars() {
1767            match c {
1768                '\'' => escaped.push_str("''"),
1769                _ => escaped.push(c),
1770            }
1771        }
1772        escaped
1773    }
1774
1775    fn supports_returning(&self) -> bool {
1776        // DuckDB 不支持 RETURNING 子句
1777        false
1778    }
1779
1780    fn build_pagination(&self, sql: &str, page: u64, limit: u64) -> String {
1781        // DuckDB 使用 LIMIT x OFFSET y(与 PostgreSQL 一致)
1782        let offset = page.saturating_sub(1).saturating_mul(limit);
1783        format!("{} LIMIT {} OFFSET {}", sql, limit, offset)
1784    }
1785
1786    fn json_type(&self) -> &'static str {
1787        // DuckDB 使用 JSON 类型
1788        "JSON"
1789    }
1790
1791    fn json_extract(&self, column: &str, path: &str) -> String {
1792        // DuckDB 使用 -> 或 ->> 操作符(与 PostgreSQL 一致)
1793        let normalized = if path.starts_with('$') {
1794            path[2..].to_string()
1795        } else {
1796            path.to_string()
1797        };
1798        format!("{} -> '{}'", column, normalized)
1799    }
1800
1801    fn full_text_search(&self, columns: &[&str], keyword: &str) -> String {
1802        // DuckDB 无原生 FTS,降级使用 LIKE
1803        if columns.is_empty() {
1804            return "0".to_string();
1805        }
1806        let escaped = self.escape_string(keyword);
1807        let parts: Vec<String> = columns
1808            .iter()
1809            .map(|c| format!("{} LIKE '%{}%'", c, escaped))
1810            .collect();
1811        parts.join(" OR ")
1812    }
1813
1814    fn bool_to_int(&self, expr: &str) -> String {
1815        // DuckDB 支持 BOOLEAN 类型,但可使用 CASE WHEN 转换
1816        format!("(CASE WHEN {} THEN 1 ELSE 0 END)", expr)
1817    }
1818
1819    fn concat(&self, parts: &[&str]) -> String {
1820        // DuckDB 使用 || 操作符进行字符串拼接
1821        if parts.is_empty() {
1822            return "''".to_string();
1823        }
1824        parts.join(" || ")
1825    }
1826
1827    fn supports_if_exists(&self) -> bool {
1828        true
1829    }
1830
1831    fn supports_if_not_exists(&self) -> bool {
1832        true
1833    }
1834
1835    fn auto_increment_keyword(&self) -> &'static str {
1836        // DuckDB 不支持自增列关键字,使用 SERIAL 或 BIGINT PRIMARY KEY
1837        ""
1838    }
1839
1840    fn last_insert_id_sql(&self) -> Option<&'static str> {
1841        // DuckDB 不支持 last_insert_id
1842        None
1843    }
1844
1845    fn supports_lock_for_update(&self) -> bool {
1846        // DuckDB 不支持行锁(嵌入式数据库)
1847        false
1848    }
1849
1850    fn supports_lock_shared(&self) -> bool {
1851        // DuckDB 不支持共享锁
1852        false
1853    }
1854
1855    fn build_insert_or_ignore_prefix(&self, table: &str) -> String {
1856        // DuckDB 使用 INSERT OR IGNORE INTO(与 SQLite 一致)
1857        format!("INSERT OR IGNORE INTO {}", self.quote(table))
1858    }
1859
1860    fn build_create_table(&self, table: &str, columns: &[ColumnDef]) -> String {
1861        let cols: Vec<String> = columns
1862            .iter()
1863            .map(|col| {
1864                let mut sql = format!("{} {}", self.quote(&col.name), col.sql_type);
1865                if col.auto_increment {
1866                    // DuckDB 不支持 AUTO_INCREMENT,使用 BIGINT PRIMARY KEY
1867                    sql = format!("{} BIGINT PRIMARY KEY", self.quote(&col.name));
1868                }
1869                if let Some(default) = &col.default {
1870                    sql.push_str(&format!(" DEFAULT {}", default));
1871                }
1872                if col.primary_key && !col.auto_increment {
1873                    sql.push_str(" PRIMARY KEY");
1874                }
1875                sql
1876            })
1877            .collect();
1878
1879        format!(
1880            "CREATE TABLE {} ({})",
1881            self.quote(table),
1882            cols.join(", ")
1883        )
1884    }
1885
1886    fn build_alter_table(&self, table: &str, changes: &[TableChange]) -> String {
1887        let stmts: Vec<String> = changes
1888            .iter()
1889            .map(|change| match change {
1890                TableChange::AddColumn(col) => {
1891                    let mut sql = format!(
1892                        "ALTER TABLE {} ADD COLUMN {} {}",
1893                        self.quote(table),
1894                        self.quote(&col.name),
1895                        col.sql_type
1896                    );
1897                    if let Some(default) = &col.default {
1898                        sql.push_str(&format!(" DEFAULT {}", default));
1899                    }
1900                    sql
1901                }
1902                TableChange::DropColumn(name) => {
1903                    format!("ALTER TABLE {} DROP COLUMN {}", self.quote(table), self.quote(name))
1904                }
1905                TableChange::ModifyColumn(col) => {
1906                    format!(
1907                        "ALTER TABLE {} ALTER COLUMN {} SET DATA TYPE {}",
1908                        self.quote(table),
1909                        self.quote(&col.name),
1910                        col.sql_type
1911                    )
1912                }
1913                TableChange::AddIndex(name, _cols) => {
1914                    // DuckDB 支持 CREATE INDEX,但不支持 ALTER TABLE ADD INDEX
1915                    format!("CREATE INDEX {} ON {} (id)", self.quote(name), self.quote(table))
1916                }
1917                TableChange::DropIndex(name) => {
1918                    format!("DROP INDEX {}", self.quote(name))
1919                }
1920                TableChange::AddForeignKey { .. } => {
1921                    // DuckDB 不支持外键约束
1922                    String::new()
1923                }
1924            })
1925            .filter(|s| !s.is_empty())
1926            .collect();
1927
1928        stmts.join("; ")
1929    }
1930}
1931
1932// ============================================================================
1933// IBM DB2 方言(独立实现)
1934//
1935// DB2 LUW 语法特性:
1936// - 双引号标识符(标准 SQL 风格)
1937// - 字符串字面量单引号转义为 ''(标准 SQL 风格)
1938// - 支持 RETURNING(DB2 11.5+)
1939// - 分页使用 OFFSET x ROWS FETCH NEXT y ROWS ONLY(标准 SQL:2008)
1940// - 自增列使用 GENERATED ALWAYS AS IDENTITY
1941// - 类型系统:VARCHAR/INTEGER/BIGINT/TIMESTAMP/DECFLOAT 等
1942// ============================================================================
1943
1944/// IBM DB2 LUW 方言实现
1945#[derive(Debug, Clone)]
1946pub struct Db2Dialect;
1947
1948impl Dialect for Db2Dialect {
1949    fn clone_box(&self) -> Box<dyn Dialect> {
1950        Box::new(Db2Dialect)
1951    }
1952
1953    fn db_type(&self) -> DbType {
1954        DbType::Db2
1955    }
1956
1957    fn quote(&self, identifier: &str) -> String {
1958        // DB2 使用双引号包裹标识符(标准 SQL 风格)
1959        format!("\"{}\"", identifier.replace('"', "\"\""))
1960    }
1961
1962    fn escape_string(&self, s: &str) -> String {
1963        // DB2 标准转义:单引号双写('O''Brien')
1964        let mut escaped = String::with_capacity(s.len() * 2);
1965        for c in s.chars() {
1966            match c {
1967                '\'' => escaped.push_str("''"),
1968                _ => escaped.push(c),
1969            }
1970        }
1971        escaped
1972    }
1973
1974    fn supports_returning(&self) -> bool {
1975        // DB2 11.5+ 支持 RETURNING(实际使用 SELECT FROM FINAL TABLE 代替)
1976        false
1977    }
1978
1979    fn build_pagination(&self, sql: &str, page: u64, limit: u64) -> String {
1980        // DB2 使用 OFFSET ... ROWS FETCH NEXT ... ROWS ONLY(SQL:2008 标准)
1981        let offset = page.saturating_sub(1).saturating_mul(limit);
1982        format!(
1983            "{} OFFSET {} ROWS FETCH NEXT {} ROWS ONLY",
1984            sql, offset, limit
1985        )
1986    }
1987
1988    fn json_type(&self) -> &'static str {
1989        // DB2 11.5+ 原生支持 JSON 类型
1990        "JSON"
1991    }
1992
1993    fn json_extract(&self, column: &str, path: &str) -> String {
1994        // DB2 使用 JSON_VALUE 函数
1995        let normalized = if path.starts_with('$') {
1996            path.to_string()
1997        } else {
1998            format!("$.{}", path)
1999        };
2000        format!(
2001            "JSON_VALUE({}, '{}')",
2002            column,
2003            self.escape_string(&normalized)
2004        )
2005    }
2006
2007    fn full_text_search(&self, columns: &[&str], keyword: &str) -> String {
2008        // DB2 使用 CONTAINS 函数(需 DB2TEXT 索引)
2009        if columns.is_empty() {
2010            return "0".to_string();
2011        }
2012        let escaped = self.escape_string(keyword);
2013        let parts: Vec<String> = columns
2014            .iter()
2015            .map(|c| format!("CONTAINS({}, '{}') > 0", c, escaped))
2016            .collect();
2017        parts.join(" OR ")
2018    }
2019
2020    fn bool_to_int(&self, expr: &str) -> String {
2021        // DB2 没有原生 BOOL(11.5+ 有 BOOLEAN),使用 CASE WHEN 转换
2022        format!("(CASE WHEN {} THEN 1 ELSE 0 END)", expr)
2023    }
2024
2025    fn concat(&self, parts: &[&str]) -> String {
2026        // DB2 使用 || 操作符进行字符串拼接
2027        if parts.is_empty() {
2028            return "''".to_string();
2029        }
2030        parts.join(" || ")
2031    }
2032
2033    fn supports_if_exists(&self) -> bool {
2034        // DB2 不支持 DROP TABLE IF EXISTS(直到 11.5)
2035        false
2036    }
2037
2038    fn supports_if_not_exists(&self) -> bool {
2039        // DB2 不支持 CREATE TABLE IF NOT EXISTS
2040        false
2041    }
2042
2043    fn auto_increment_keyword(&self) -> &'static str {
2044        // DB2 使用 GENERATED ALWAYS AS IDENTITY
2045        "GENERATED ALWAYS AS IDENTITY"
2046    }
2047
2048    fn last_insert_id_sql(&self) -> Option<&'static str> {
2049        // DB2 使用 IDENTITY_VAL_LOCAL() 函数获取最后插入的 IDENTITY 值
2050        Some("SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1")
2051    }
2052
2053    fn build_create_table(&self, table: &str, columns: &[ColumnDef]) -> String {
2054        let cols: Vec<String> = columns
2055            .iter()
2056            .map(|col| {
2057                let db2_type = map_to_db2_type(&col.sql_type);
2058                let mut sql = format!("{} {}", self.quote(&col.name), db2_type);
2059                if !col.nullable && !col.auto_increment {
2060                    sql.push_str(" NOT NULL");
2061                }
2062                if let Some(default) = &col.default {
2063                    sql.push_str(&format!(" DEFAULT {}", default));
2064                }
2065                if col.auto_increment {
2066                    sql.push_str(&format!(" {}", self.auto_increment_keyword()));
2067                }
2068                if col.primary_key {
2069                    sql.push_str(" PRIMARY KEY");
2070                }
2071                sql
2072            })
2073            .collect();
2074
2075        format!("CREATE TABLE {} ({})", self.quote(table), cols.join(", "))
2076    }
2077
2078    fn build_alter_table(&self, table: &str, changes: &[TableChange]) -> String {
2079        let stmts: Vec<String> = changes
2080            .iter()
2081            .map(|change| match change {
2082                TableChange::AddColumn(col) => {
2083                    let db2_type = map_to_db2_type(&col.sql_type);
2084                    let mut sql = format!(
2085                        "ALTER TABLE {} ADD COLUMN {} {}",
2086                        self.quote(table),
2087                        self.quote(&col.name),
2088                        db2_type
2089                    );
2090                    if !col.nullable {
2091                        sql.push_str(" NOT NULL");
2092                    }
2093                    if let Some(default) = &col.default {
2094                        sql.push_str(&format!(" DEFAULT {}", default));
2095                    }
2096                    sql
2097                }
2098                TableChange::DropColumn(name) => {
2099                    format!(
2100                        "ALTER TABLE {} DROP COLUMN {}",
2101                        self.quote(table),
2102                        self.quote(name)
2103                    )
2104                }
2105                TableChange::ModifyColumn(col) => {
2106                    let db2_type = map_to_db2_type(&col.sql_type);
2107                    format!(
2108                        "ALTER TABLE {} ALTER COLUMN {} SET DATA TYPE {}",
2109                        self.quote(table),
2110                        self.quote(&col.name),
2111                        db2_type
2112                    )
2113                }
2114                TableChange::AddIndex(name, cols) => {
2115                    format!(
2116                        "CREATE INDEX {} ON {} ({})",
2117                        name,
2118                        self.quote(table),
2119                        cols.join(", ")
2120                    )
2121                }
2122                TableChange::DropIndex(name) => {
2123                    format!("DROP INDEX {}", name)
2124                }
2125                TableChange::AddForeignKey {
2126                    columns,
2127                    reference_table,
2128                    reference_columns,
2129                } => {
2130                    format!(
2131                        "ALTER TABLE {} ADD CONSTRAINT fk_{}_{} FOREIGN KEY ({}) REFERENCES {} ({})",
2132                        self.quote(table),
2133                        table,
2134                        columns.join("_"),
2135                        columns.iter().map(|c| self.quote(c)).collect::<Vec<_>>().join(", "),
2136                        self.quote(reference_table),
2137                        reference_columns.iter().map(|c| self.quote(c)).collect::<Vec<_>>().join(", ")
2138                    )
2139                }
2140            })
2141            .collect();
2142
2143        stmts.join("; ")
2144    }
2145
2146    fn build_drop_table(&self, table: &str, if_exists: bool) -> String {
2147        // DB2 不支持 IF EXISTS,但为兼容性保留参数
2148        let _ = if_exists;
2149        format!("DROP TABLE {}", self.quote(table))
2150    }
2151}
2152
2153/// 将通用 SQL 类型映射为 IBM DB2 类型
2154///
2155/// - BIGINT → BIGINT
2156/// - INT/INTEGER → INTEGER
2157/// - VARCHAR(n) → VARCHAR(n)
2158/// - TEXT 系列 → CLOB(2G)
2159/// - BOOLEAN/BOOL → SMALLINT(DB2 11.5+ 才有 BOOLEAN)
2160/// - DATETIME/TIMESTAMP → TIMESTAMP
2161fn map_to_db2_type(sql_type: &str) -> String {
2162    let upper = sql_type.to_uppercase();
2163    let trimmed = upper.trim();
2164
2165    if trimmed.starts_with("BIGINT") {
2166        "BIGINT".to_string()
2167    } else if matches!(trimmed, "INT" | "INTEGER") {
2168        "INTEGER".to_string()
2169    } else if matches!(trimmed, "TINYINT" | "SMALLINT") {
2170        "SMALLINT".to_string()
2171    } else if trimmed.starts_with("VARCHAR") || trimmed.starts_with("CHAR") {
2172        sql_type.to_string()
2173    } else if matches!(trimmed, "TEXT" | "MEDIUMTEXT" | "LONGTEXT" | "TINYTEXT") {
2174        "CLOB(2G)".to_string()
2175    } else if matches!(trimmed, "BOOLEAN" | "BOOL") {
2176        "SMALLINT".to_string()
2177    } else if matches!(trimmed, "FLOAT" | "REAL") {
2178        "REAL".to_string()
2179    } else if matches!(trimmed, "DOUBLE" | "DOUBLE PRECISION") {
2180        "DOUBLE".to_string()
2181    } else if matches!(trimmed, "DATETIME" | "TIMESTAMP") {
2182        "TIMESTAMP".to_string()
2183    } else if matches!(trimmed, "DATE") {
2184        "DATE".to_string()
2185    } else {
2186        // DECIMAL/NUMERIC 和其他未匹配类型保持原样
2187        sql_type.to_string()
2188    }
2189}
2190
2191/// 根据数据库类型获取对应的方言实例
2192///
2193/// L-5 修复:补充示例文档
2194///
2195/// 返回 `Box<dyn Dialect>`,可用于与 `QueryBuilder`、`Schema` 等组件配合。
2196/// 对于不支持 SQL 方言的数据库类型(如 Redis、MongoDB、向量库),返回
2197/// `DbError::Unsupported`。
2198///
2199/// # 示例
2200///
2201/// ```ignore
2202/// use sz_orm_core::db_type::DbType;
2203/// use sz_orm_core::dialect::{get_dialect, Dialect};
2204///
2205/// let dialect = get_dialect(DbType::MySQL).unwrap();
2206/// assert_eq!(dialect.quote("user"), "`user`");
2207/// assert_eq!(dialect.db_type(), DbType::MySQL);
2208///
2209/// // 不支持的类型返回错误
2210/// let err = get_dialect(DbType::Redis).unwrap_err();
2211/// assert!(matches!(err, sz_orm_core::DbError::Unsupported(_)));
2212/// ```
2213pub fn get_dialect(db_type: DbType) -> Result<Box<dyn Dialect>, DbError> {
2214    match db_type {
2215        DbType::MySQL => Ok(Box::new(MySqlDialect)),
2216        DbType::PostgreSQL => Ok(Box::new(PostgreSqlDialect)),
2217        DbType::Sqlite => Ok(Box::new(SqliteDialect)),
2218        DbType::Redis => Err(DbError::Unsupported(
2219            "Redis does not support standard SQL dialect".to_string(),
2220        )),
2221        DbType::MongoDB => Err(DbError::Unsupported(
2222            "MongoDB uses different query syntax".to_string(),
2223        )),
2224        DbType::ClickHouse => Ok(Box::new(ClickHouseDialect)),
2225        DbType::Oracle => Ok(Box::new(OracleDialect)),
2226        DbType::OceanBase => Ok(Box::new(MySqlDialect)),
2227        DbType::SqlServer => Ok(Box::new(SqlServerDialect)),
2228        DbType::VectorDb => Err(DbError::Unsupported(
2229            "Vector databases have specific APIs".to_string(),
2230        )),
2231        DbType::PureJsDb => Err(DbError::Unsupported(
2232            "PureJS database uses JavaScript".to_string(),
2233        )),
2234        // 国产数据库 & 兼容方言
2235        DbType::Dameng => Ok(Box::new(DamengDialect)),
2236        DbType::Kingbase => Ok(Box::new(KingbaseDialect)),
2237        DbType::Db2 => Ok(Box::new(Db2Dialect)),
2238        DbType::MariaDB => Ok(Box::new(MariaDbDialect)),
2239        DbType::TiDB => Ok(Box::new(TiDbDialect)),
2240        DbType::PolarDB => Ok(Box::new(PolarDbDialect)),
2241        DbType::GaussDB => Ok(Box::new(GaussDbDialect)),
2242        DbType::GBase => Ok(Box::new(GBaseDialect)),
2243        DbType::Sybase => Ok(Box::new(SybaseDialect)),
2244        DbType::DuckDB => Ok(Box::new(DuckDBDialect)),
2245    }
2246}
2247
2248impl fmt::Display for dyn Dialect {
2249    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2250        write!(f, "Dialect({})", self.db_type())
2251    }
2252}
2253
2254#[cfg(test)]
2255mod tests {
2256    use super::*;
2257
2258    #[test]
2259    fn test_mysql_quote() {
2260        let dialect = MySqlDialect;
2261        assert_eq!(dialect.quote("users"), "`users`");
2262        assert_eq!(dialect.quote("user`id"), "`user``id`");
2263    }
2264
2265    #[test]
2266    fn test_mysql_escape() {
2267        let dialect = MySqlDialect;
2268        assert_eq!(dialect.escape_string("hello"), "hello");
2269        assert_eq!(dialect.escape_string("it's"), "it\\'s");
2270        assert_eq!(dialect.escape_string("line\nbreak"), "line\\nbreak");
2271    }
2272
2273    #[test]
2274    fn test_mysql_pagination() {
2275        let dialect = MySqlDialect;
2276        let sql = dialect.build_pagination("SELECT * FROM users", 2, 10);
2277        assert_eq!(sql, "SELECT * FROM users LIMIT 10 OFFSET 10");
2278    }
2279
2280    #[test]
2281    fn test_postgres_quote() {
2282        let dialect = PostgreSqlDialect;
2283        assert_eq!(dialect.quote("users"), "\"users\"");
2284        assert_eq!(dialect.quote("user\"id"), "\"user\"\"id\"");
2285    }
2286
2287    #[test]
2288    fn test_postgres_pagination() {
2289        let dialect = PostgreSqlDialect;
2290        let sql = dialect.build_pagination("SELECT * FROM users", 3, 20);
2291        assert_eq!(sql, "SELECT * FROM users LIMIT 20 OFFSET 40");
2292    }
2293
2294    #[test]
2295    fn test_postgres_returning() {
2296        let dialect = PostgreSqlDialect;
2297        assert!(dialect.supports_returning());
2298    }
2299
2300    #[test]
2301    fn test_sqlite_quote() {
2302        let dialect = SqliteDialect;
2303        assert_eq!(dialect.quote("users"), "\"users\"");
2304        assert_eq!(dialect.quote("user\"id"), "\"user\"\"id\"");
2305    }
2306
2307    #[test]
2308    fn test_sqlite_escape() {
2309        let dialect = SqliteDialect;
2310        assert_eq!(dialect.escape_string("hello"), "hello");
2311        assert_eq!(dialect.escape_string("it's"), "it''s");
2312    }
2313
2314    #[test]
2315    fn test_get_dialect() {
2316        let dialect = get_dialect(DbType::MySQL);
2317        assert!(dialect.is_ok());
2318
2319        let dialect = get_dialect(DbType::Redis);
2320        assert!(dialect.is_err());
2321    }
2322
2323    #[test]
2324    fn test_bool_to_int() {
2325        let mysql = MySqlDialect;
2326        assert_eq!(mysql.bool_to_int("active"), "IF(active, 1, 0)");
2327
2328        let pg = PostgreSqlDialect;
2329        assert_eq!(
2330            pg.bool_to_int("active"),
2331            "(CASE WHEN active THEN 1 ELSE 0 END)"
2332        );
2333    }
2334
2335    #[test]
2336    fn test_json_extract_with_path() {
2337        let mysql = MySqlDialect;
2338        let sql = mysql.json_extract("data", "$.user.name");
2339        assert!(sql.contains("$.user.name"));
2340        assert!(sql.contains("JSON_EXTRACT"));
2341
2342        let pg = PostgreSqlDialect;
2343        let sql = pg.json_extract("data", "user.name");
2344        assert!(sql.contains("#>>"));
2345
2346        let sqlite = SqliteDialect;
2347        let sql = sqlite.json_extract("data", "$.user.name");
2348        assert!(sql.contains("$.user.name"));
2349        assert!(sql.contains("json_extract"));
2350    }
2351
2352    #[test]
2353    fn test_sqlite_full_text_search() {
2354        let sqlite = SqliteDialect;
2355        let sql = sqlite.full_text_search(&["title", "content"], "hello");
2356        // SQLite 接口只传入列名,无 FTS 表名,因此降级使用 LIKE
2357        assert!(sql.contains("LIKE"));
2358        assert!(sql.contains("title LIKE '%hello%'"));
2359        assert!(sql.contains("content LIKE '%hello%'"));
2360        assert!(sql.contains(" OR "));
2361
2362        // 空列列表返回 "0"(短路避免空 IN/OR)
2363        assert_eq!(sqlite.full_text_search(&[], "hello"), "0");
2364
2365        // 含单引号的关键字需正确转义(SQLite 双单引号)
2366        let sql = sqlite.full_text_search(&["title"], "it's");
2367        assert!(sql.contains("title LIKE '%it''s%'"));
2368    }
2369
2370    #[test]
2371    fn test_alter_table_modify_column() {
2372        let mysql = MySqlDialect;
2373        let col = ColumnDef {
2374            name: "name".to_string(),
2375            sql_type: "VARCHAR(255)".to_string(),
2376            nullable: false,
2377            default: None,
2378            auto_increment: false,
2379            primary_key: false,
2380        };
2381        let sql = mysql.build_alter_table("users", &[TableChange::ModifyColumn(col)]);
2382        assert!(sql.contains("MODIFY COLUMN"));
2383
2384        let pg = PostgreSqlDialect;
2385        let col = ColumnDef {
2386            name: "name".to_string(),
2387            sql_type: "VARCHAR(255)".to_string(),
2388            nullable: false,
2389            default: None,
2390            auto_increment: false,
2391            primary_key: false,
2392        };
2393        let sql = pg.build_alter_table("users", &[TableChange::ModifyColumn(col)]);
2394        assert!(sql.contains("ALTER COLUMN"));
2395        assert!(sql.contains("TYPE"));
2396    }
2397
2398    #[test]
2399    fn test_alter_table_add_foreign_key() {
2400        let mysql = MySqlDialect;
2401        let sql = mysql.build_alter_table(
2402            "orders",
2403            &[TableChange::AddForeignKey {
2404                columns: vec!["user_id".to_string()],
2405                reference_table: "users".to_string(),
2406                reference_columns: vec!["id".to_string()],
2407            }],
2408        );
2409        assert!(sql.contains("FOREIGN KEY"));
2410        assert!(sql.contains("REFERENCES"));
2411
2412        let sqlite = SqliteDialect;
2413        let sql = sqlite.build_alter_table(
2414            "orders",
2415            &[TableChange::AddForeignKey {
2416                columns: vec!["user_id".to_string()],
2417                reference_table: "users".to_string(),
2418                reference_columns: vec!["id".to_string()],
2419            }],
2420        );
2421        // SQLite 不支持,应返回注释
2422        assert!(sql.starts_with("--"));
2423    }
2424
2425    #[test]
2426    fn test_sqlite_alter_table_add_column() {
2427        let sqlite = SqliteDialect;
2428        let col = ColumnDef {
2429            name: "email".to_string(),
2430            sql_type: "TEXT".to_string(),
2431            nullable: true,
2432            default: None,
2433            auto_increment: false,
2434            primary_key: false,
2435        };
2436        let sql = sqlite.build_alter_table("users", &[TableChange::AddColumn(col)]);
2437        assert!(sql.contains("ADD COLUMN"));
2438        assert!(sql.contains("email"));
2439    }
2440
2441    // ===================== Oracle 方言测试 =====================
2442
2443    #[test]
2444    fn test_oracle_quote_and_escape() {
2445        let dialect = OracleDialect;
2446        // 标识符使用双引号包裹,内部双引号双写
2447        assert_eq!(dialect.quote("users"), "\"users\"");
2448        assert_eq!(dialect.quote("user\"id"), "\"user\"\"id\"");
2449        assert_eq!(dialect.quote("column_name"), "\"column_name\"");
2450
2451        // 字符串字面量转义:单引号双写,反斜杠不转义
2452        assert_eq!(dialect.escape_string("hello"), "hello");
2453        assert_eq!(dialect.escape_string("it's"), "it''s");
2454        assert_eq!(dialect.escape_string("O'Brien"), "O''Brien");
2455        assert_eq!(dialect.escape_string("a'b'c"), "a''b''c");
2456        // 反斜杠按原样保留(Oracle 标准行为)
2457        assert_eq!(dialect.escape_string("path\\to"), "path\\to");
2458    }
2459
2460    #[test]
2461    fn test_oracle_pagination() {
2462        let dialect = OracleDialect;
2463        // page=1 为第一页(offset=0),与其他方言保持一致
2464        let sql = dialect.build_pagination("SELECT * FROM users", 1, 10);
2465        assert_eq!(
2466            sql,
2467            "SELECT * FROM users OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY"
2468        );
2469        // page=3, limit=20 → offset=40
2470        let sql = dialect.build_pagination("SELECT * FROM users", 3, 20);
2471        assert_eq!(
2472            sql,
2473            "SELECT * FROM users OFFSET 40 ROWS FETCH NEXT 20 ROWS ONLY"
2474        );
2475        // page=0(边界)→ offset=0
2476        let sql = dialect.build_pagination("SELECT * FROM users", 0, 10);
2477        assert_eq!(
2478            sql,
2479            "SELECT * FROM users OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY"
2480        );
2481    }
2482
2483    #[test]
2484    fn test_oracle_json_extract() {
2485        let dialect = OracleDialect;
2486        // 标准 $.path 格式
2487        let sql = dialect.json_extract("data", "$.user.name");
2488        assert!(sql.contains("JSON_VALUE"));
2489        assert!(sql.contains("$.user.name"));
2490        assert!(sql.starts_with("JSON_VALUE(data, '$.user.name')"));
2491
2492        // 自动补全 $. 前缀
2493        let sql = dialect.json_extract("data", "user.name");
2494        assert!(sql.contains("$.user.name"));
2495        assert!(sql.contains("JSON_VALUE"));
2496
2497        // 单引号转义
2498        let sql = dialect.json_extract("data", "$.key's");
2499        assert!(sql.contains("$.key''s"));
2500    }
2501
2502    #[test]
2503    fn test_oracle_create_table() {
2504        let dialect = OracleDialect;
2505        let columns = vec![
2506            ColumnDef {
2507                name: "id".to_string(),
2508                sql_type: "BIGINT".to_string(),
2509                nullable: false,
2510                default: None,
2511                auto_increment: true,
2512                primary_key: true,
2513            },
2514            ColumnDef {
2515                name: "name".to_string(),
2516                sql_type: "VARCHAR(255)".to_string(),
2517                nullable: false,
2518                default: None,
2519                auto_increment: false,
2520                primary_key: false,
2521            },
2522            ColumnDef {
2523                name: "bio".to_string(),
2524                sql_type: "TEXT".to_string(),
2525                nullable: true,
2526                default: None,
2527                auto_increment: false,
2528                primary_key: false,
2529            },
2530            ColumnDef {
2531                name: "is_active".to_string(),
2532                sql_type: "BOOLEAN".to_string(),
2533                nullable: false,
2534                default: Some("1".to_string()),
2535                auto_increment: false,
2536                primary_key: false,
2537            },
2538        ];
2539        let sql = dialect.build_create_table("users", &columns);
2540        // Oracle 类型映射
2541        assert!(
2542            sql.contains("NUMBER(19)"),
2543            "BIGINT should map to NUMBER(19): {}",
2544            sql
2545        );
2546        assert!(
2547            sql.contains("VARCHAR2(255)"),
2548            "VARCHAR should map to VARCHAR2: {}",
2549            sql
2550        );
2551        assert!(sql.contains("CLOB"), "TEXT should map to CLOB: {}", sql);
2552        assert!(
2553            sql.contains("NUMBER(1)"),
2554            "BOOLEAN should map to NUMBER(1): {}",
2555            sql
2556        );
2557        // 自增与主键
2558        assert!(sql.contains("GENERATED BY DEFAULT AS IDENTITY"));
2559        assert!(sql.contains("PRIMARY KEY"));
2560        assert!(sql.contains("NOT NULL"));
2561        assert!(sql.contains("DEFAULT 1"));
2562        // 标识符使用双引号
2563        assert!(sql.contains("\"users\""));
2564        assert!(sql.contains("\"id\""));
2565    }
2566
2567    #[test]
2568    fn test_oracle_bool_to_int_and_concat() {
2569        let dialect = OracleDialect;
2570        // bool_to_int 使用 CASE WHEN
2571        assert_eq!(
2572            dialect.bool_to_int("active"),
2573            "(CASE WHEN active THEN 1 ELSE 0 END)"
2574        );
2575        assert_eq!(
2576            dialect.bool_to_int("x > 0"),
2577            "(CASE WHEN x > 0 THEN 1 ELSE 0 END)"
2578        );
2579        // concat 使用 || 操作符
2580        assert_eq!(dialect.concat(&["a", "b", "c"]), "a || b || c");
2581        assert_eq!(
2582            dialect.concat(&["first_name", "last_name"]),
2583            "first_name || last_name"
2584        );
2585        // 空列表返回 NULL
2586        assert_eq!(dialect.concat(&[]), "NULL");
2587    }
2588
2589    #[test]
2590    fn test_oracle_misc_dialect_methods() {
2591        let dialect = OracleDialect;
2592        // db_type
2593        assert_eq!(dialect.db_type(), DbType::Oracle);
2594        // supports_returning: Oracle 12c+ 支持
2595        assert!(dialect.supports_returning());
2596        // supports_if_exists / supports_if_not_exists: Oracle 23ai 支持
2597        assert!(dialect.supports_if_exists());
2598        assert!(dialect.supports_if_not_exists());
2599        // auto_increment_keyword
2600        assert_eq!(
2601            dialect.auto_increment_keyword(),
2602            "GENERATED BY DEFAULT AS IDENTITY"
2603        );
2604        // last_insert_id_sql: Oracle 不支持独立可执行的获取最后插入 ID 语句
2605        // RETURNING ... INTO 是 PL/SQL 子句,必须附加在 INSERT 之后
2606        assert_eq!(dialect.last_insert_id_sql(), None);
2607        // json_type
2608        assert_eq!(dialect.json_type(), "JSON");
2609    }
2610
2611    #[test]
2612    fn test_oracle_get_dialect() {
2613        // get_dialect 应返回 OracleDialect 而非 MySqlDialect
2614        let dialect = get_dialect(DbType::Oracle);
2615        assert!(dialect.is_ok(), "Oracle dialect should be available");
2616        let dialect = dialect.unwrap();
2617        assert_eq!(dialect.db_type(), DbType::Oracle);
2618        // 验证不是 MySqlDialect 的回退:Oracle 使用双引号,MySQL 使用反引号
2619        assert_eq!(dialect.quote("users"), "\"users\"");
2620        // 验证 Oracle 特有功能
2621        assert!(dialect.supports_returning());
2622        // Oracle 没有 standalone 的 last_insert_id SQL
2623        assert_eq!(dialect.last_insert_id_sql(), None);
2624    }
2625
2626    #[test]
2627    fn test_oracle_drop_table() {
2628        let dialect = OracleDialect;
2629        // IF EXISTS
2630        let sql = dialect.build_drop_table("users", true);
2631        assert_eq!(sql, "DROP TABLE IF EXISTS \"users\"");
2632        // 不带 IF EXISTS
2633        let sql = dialect.build_drop_table("users", false);
2634        assert_eq!(sql, "DROP TABLE \"users\"");
2635    }
2636
2637    #[test]
2638    fn test_oracle_alter_table() {
2639        let dialect = OracleDialect;
2640        // MODIFY COLUMN: Oracle 使用 MODIFY(不带 COLUMN 关键字)
2641        let col = ColumnDef {
2642            name: "name".to_string(),
2643            sql_type: "VARCHAR(255)".to_string(),
2644            nullable: false,
2645            default: None,
2646            auto_increment: false,
2647            primary_key: false,
2648        };
2649        let sql = dialect.build_alter_table("users", &[TableChange::ModifyColumn(col)]);
2650        assert!(sql.contains("MODIFY"));
2651        assert!(sql.contains("VARCHAR2(255)"));
2652        assert!(!sql.contains("MODIFY COLUMN")); // Oracle 不使用 COLUMN 关键字
2653
2654        // ADD COLUMN
2655        let col = ColumnDef {
2656            name: "email".to_string(),
2657            sql_type: "VARCHAR(255)".to_string(),
2658            nullable: true,
2659            default: None,
2660            auto_increment: false,
2661            primary_key: false,
2662        };
2663        let sql = dialect.build_alter_table("users", &[TableChange::AddColumn(col)]);
2664        assert!(sql.contains("ADD \"email\""));
2665        assert!(sql.contains("VARCHAR2(255)"));
2666
2667        // DROP COLUMN
2668        let sql =
2669            dialect.build_alter_table("users", &[TableChange::DropColumn("email".to_string())]);
2670        assert!(sql.contains("DROP COLUMN"));
2671        assert!(sql.contains("\"email\""));
2672    }
2673
2674    // ===================== SQLite concat 修复测试 =====================
2675
2676    #[test]
2677    fn test_sqlite_concat_handles_null() {
2678        let sqlite = SqliteDialect;
2679        // 修复前:COALESCE(a || b) 在 a 为 NULL 时整体已 NULL,COALESCE 失效
2680        // 修复后:每个参数 COALESCE 为空串,保证拼接结果非 NULL
2681        let sql = sqlite.concat(&["a", "b"]);
2682        assert_eq!(sql, "COALESCE(a, '') || COALESCE(b, '')");
2683        // 单参数
2684        let sql = sqlite.concat(&["a"]);
2685        assert_eq!(sql, "COALESCE(a, '')");
2686        // 空列表
2687        assert_eq!(sqlite.concat(&[]), "NULL");
2688    }
2689
2690    // ===================== SQL Server 方言测试 =====================
2691
2692    #[test]
2693    fn test_sqlserver_quote_and_escape() {
2694        let dialect = SqlServerDialect;
2695        // 标识符使用 [name] 包裹,内部 ] 双写
2696        assert_eq!(dialect.quote("users"), "[users]");
2697        assert_eq!(dialect.quote("col]name"), "[col]]name]");
2698        // 字符串字面量:单引号双写,反斜杠不转义
2699        assert_eq!(dialect.escape_string("hello"), "hello");
2700        assert_eq!(dialect.escape_string("it's"), "it''s");
2701        assert_eq!(dialect.escape_string("O'Brien"), "O''Brien");
2702        assert_eq!(dialect.escape_string("path\\to"), "path\\to");
2703    }
2704
2705    #[test]
2706    fn test_sqlserver_pagination() {
2707        let dialect = SqlServerDialect;
2708        // SQL Server 2012+ 使用 OFFSET ... ROWS FETCH NEXT ... ROWS ONLY
2709        let sql = dialect.build_pagination("SELECT * FROM users", 1, 10);
2710        assert_eq!(
2711            sql,
2712            "SELECT * FROM users OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY"
2713        );
2714        let sql = dialect.build_pagination("SELECT * FROM users", 3, 20);
2715        assert_eq!(
2716            sql,
2717            "SELECT * FROM users OFFSET 40 ROWS FETCH NEXT 20 ROWS ONLY"
2718        );
2719        // page=0 边界
2720        let sql = dialect.build_pagination("SELECT * FROM users", 0, 10);
2721        assert_eq!(
2722            sql,
2723            "SELECT * FROM users OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY"
2724        );
2725    }
2726
2727    #[test]
2728    fn test_sqlserver_misc_dialect_methods() {
2729        let dialect = SqlServerDialect;
2730        assert_eq!(dialect.db_type(), DbType::SqlServer);
2731        // supports_returning: SQL Server 使用 OUTPUT,语义等价
2732        assert!(dialect.supports_returning());
2733        // supports_if_exists / supports_if_not_exists: SQL Server 2016+
2734        assert!(dialect.supports_if_exists());
2735        assert!(dialect.supports_if_not_exists());
2736        // auto_increment_keyword
2737        assert_eq!(dialect.auto_increment_keyword(), "IDENTITY(1,1)");
2738        // last_insert_id_sql
2739        assert_eq!(dialect.last_insert_id_sql(), Some("SCOPE_IDENTITY()"));
2740        // json_type
2741        assert_eq!(dialect.json_type(), "NVARCHAR(MAX)");
2742    }
2743
2744    #[test]
2745    fn test_sqlserver_json_extract() {
2746        let dialect = SqlServerDialect;
2747        let sql = dialect.json_extract("data", "$.user.name");
2748        assert!(sql.starts_with("JSON_VALUE(data, '$.user.name')"));
2749        // 自动补 $. 前缀
2750        let sql = dialect.json_extract("data", "user.name");
2751        assert!(sql.contains("$.user.name"));
2752        assert!(sql.contains("JSON_VALUE"));
2753        // 单引号转义
2754        let sql = dialect.json_extract("data", "$.key's");
2755        assert!(sql.contains("$.key''s"));
2756    }
2757
2758    #[test]
2759    fn test_sqlserver_full_text_search() {
2760        let dialect = SqlServerDialect;
2761        let sql = dialect.full_text_search(&["title", "content"], "hello");
2762        assert!(sql.starts_with("CONTAINS(title, content, 'hello')"));
2763        // 空列列表
2764        assert_eq!(dialect.full_text_search(&[], "hello"), "0");
2765        // 单引号转义
2766        let sql = dialect.full_text_search(&["title"], "it's");
2767        assert!(sql.contains("it''s"));
2768    }
2769
2770    #[test]
2771    fn test_sqlserver_bool_to_int_and_concat() {
2772        let dialect = SqlServerDialect;
2773        assert_eq!(
2774            dialect.bool_to_int("active"),
2775            "(CASE WHEN active THEN 1 ELSE 0 END)"
2776        );
2777        assert_eq!(dialect.concat(&["a", "b", "c"]), "CONCAT(a, b, c)");
2778        assert_eq!(dialect.concat(&[]), "NULL");
2779    }
2780
2781    #[test]
2782    fn test_sqlserver_create_table() {
2783        let dialect = SqlServerDialect;
2784        let columns = vec![
2785            ColumnDef {
2786                name: "id".to_string(),
2787                sql_type: "BIGINT".to_string(),
2788                nullable: false,
2789                default: None,
2790                auto_increment: true,
2791                primary_key: true,
2792            },
2793            ColumnDef {
2794                name: "name".to_string(),
2795                sql_type: "VARCHAR(255)".to_string(),
2796                nullable: false,
2797                default: None,
2798                auto_increment: false,
2799                primary_key: false,
2800            },
2801            ColumnDef {
2802                name: "bio".to_string(),
2803                sql_type: "TEXT".to_string(),
2804                nullable: true,
2805                default: None,
2806                auto_increment: false,
2807                primary_key: false,
2808            },
2809            ColumnDef {
2810                name: "is_active".to_string(),
2811                sql_type: "BOOLEAN".to_string(),
2812                nullable: false,
2813                default: Some("1".to_string()),
2814                auto_increment: false,
2815                primary_key: false,
2816            },
2817        ];
2818        let sql = dialect.build_create_table("users", &columns);
2819        // 标识符使用方括号
2820        assert!(sql.contains("[users]"));
2821        assert!(sql.contains("[id]"));
2822        // 类型映射
2823        assert!(sql.contains("IDENTITY(1,1)"));
2824        assert!(
2825            sql.contains("NVARCHAR(255)"),
2826            "VARCHAR should map to NVARCHAR: {}",
2827            sql
2828        );
2829        assert!(
2830            sql.contains("NVARCHAR(MAX)"),
2831            "TEXT should map to NVARCHAR(MAX): {}",
2832            sql
2833        );
2834        assert!(sql.contains("BIT"), "BOOLEAN should map to BIT: {}", sql);
2835        assert!(sql.contains("PRIMARY KEY"));
2836        assert!(sql.contains("NOT NULL"));
2837        assert!(sql.contains("DEFAULT 1"));
2838    }
2839
2840    #[test]
2841    fn test_sqlserver_drop_table() {
2842        let dialect = SqlServerDialect;
2843        assert_eq!(
2844            dialect.build_drop_table("users", true),
2845            "DROP TABLE IF EXISTS [users]"
2846        );
2847        assert_eq!(
2848            dialect.build_drop_table("users", false),
2849            "DROP TABLE [users]"
2850        );
2851    }
2852
2853    #[test]
2854    fn test_sqlserver_alter_table() {
2855        let dialect = SqlServerDialect;
2856        // MODIFY COLUMN: SQL Server 使用 ALTER COLUMN(不是 MODIFY)
2857        let col = ColumnDef {
2858            name: "name".to_string(),
2859            sql_type: "VARCHAR(255)".to_string(),
2860            nullable: false,
2861            default: None,
2862            auto_increment: false,
2863            primary_key: false,
2864        };
2865        let sql = dialect.build_alter_table("users", &[TableChange::ModifyColumn(col)]);
2866        assert!(sql.contains("ALTER COLUMN"));
2867        assert!(sql.contains("NVARCHAR(255)"));
2868        assert!(!sql.contains("MODIFY"));
2869
2870        // ADD COLUMN
2871        let col = ColumnDef {
2872            name: "email".to_string(),
2873            sql_type: "VARCHAR(255)".to_string(),
2874            nullable: true,
2875            default: None,
2876            auto_increment: false,
2877            primary_key: false,
2878        };
2879        let sql = dialect.build_alter_table("users", &[TableChange::AddColumn(col)]);
2880        assert!(sql.contains("ADD [email]"));
2881        assert!(sql.contains("NVARCHAR(255)"));
2882
2883        // DROP COLUMN
2884        let sql =
2885            dialect.build_alter_table("users", &[TableChange::DropColumn("email".to_string())]);
2886        assert!(sql.contains("DROP COLUMN"));
2887        assert!(sql.contains("[email]"));
2888
2889        // DROP INDEX 必须指定表名
2890        let sql =
2891            dialect.build_alter_table("users", &[TableChange::DropIndex("idx_name".to_string())]);
2892        assert!(sql.contains("DROP INDEX idx_name ON [users]"));
2893    }
2894
2895    #[test]
2896    fn test_sqlserver_get_dialect() {
2897        // get_dialect 应返回 SqlServerDialect,而不是 MySqlDialect 回退
2898        let dialect = get_dialect(DbType::SqlServer);
2899        assert!(dialect.is_ok(), "SqlServer dialect should be available");
2900        let dialect = dialect.unwrap();
2901        assert_eq!(dialect.db_type(), DbType::SqlServer);
2902        // 验证不是 MySqlDialect 的回退:SQL Server 使用方括号,MySQL 使用反引号
2903        assert_eq!(dialect.quote("users"), "[users]");
2904        // SQL Server 特有功能
2905        assert_eq!(dialect.last_insert_id_sql(), Some("SCOPE_IDENTITY()"));
2906        assert_eq!(dialect.auto_increment_keyword(), "IDENTITY(1,1)");
2907    }
2908
2909    #[test]
2910    fn test_clickhouse_get_dialect_unsupported() {
2911        // ClickHouse 现已支持独立方言(不再回退到 MySqlDialect)
2912        let dialect = get_dialect(DbType::ClickHouse);
2913        assert!(dialect.is_ok(), "ClickHouse should be supported");
2914        let dialect = dialect.unwrap();
2915        assert_eq!(dialect.db_type(), DbType::ClickHouse);
2916        // ClickHouse 使用 backquote 标识符(与 MySQL 一致)
2917        assert_eq!(dialect.quote("users"), "`users`");
2918        // ClickHouse 不支持 RETURNING
2919        assert!(!dialect.supports_returning());
2920        // ClickHouse 使用 LIMIT offset, limit 分页
2921        let sql = dialect.build_pagination("SELECT * FROM t", 2, 10);
2922        assert_eq!(sql, "SELECT * FROM t LIMIT 10, 10");
2923        // ClickHouse 自增列为空字符串
2924        assert_eq!(dialect.auto_increment_keyword(), "");
2925    }
2926
2927    #[test]
2928    fn test_get_dialect_all_supported_types() {
2929        // 所有声称为支持的方言应正确返回,不支持的应返回 Err
2930        assert!(get_dialect(DbType::MySQL).is_ok());
2931        assert!(get_dialect(DbType::PostgreSQL).is_ok());
2932        assert!(get_dialect(DbType::Sqlite).is_ok());
2933        assert!(get_dialect(DbType::Oracle).is_ok());
2934        assert!(get_dialect(DbType::SqlServer).is_ok());
2935        assert!(get_dialect(DbType::OceanBase).is_ok());
2936        assert!(get_dialect(DbType::ClickHouse).is_ok());
2937        // 国产数据库 & 兼容方言
2938        assert!(get_dialect(DbType::Dameng).is_ok());
2939        assert!(get_dialect(DbType::Kingbase).is_ok());
2940        assert!(get_dialect(DbType::Db2).is_ok());
2941        assert!(get_dialect(DbType::MariaDB).is_ok());
2942        assert!(get_dialect(DbType::TiDB).is_ok());
2943        assert!(get_dialect(DbType::PolarDB).is_ok());
2944        assert!(get_dialect(DbType::GaussDB).is_ok());
2945        assert!(get_dialect(DbType::GBase).is_ok());
2946        assert!(get_dialect(DbType::Sybase).is_ok());
2947        // 不支持标准 SQL 的
2948        assert!(get_dialect(DbType::Redis).is_err());
2949        assert!(get_dialect(DbType::MongoDB).is_err());
2950        assert!(get_dialect(DbType::VectorDb).is_err());
2951        assert!(get_dialect(DbType::PureJsDb).is_err());
2952    }
2953
2954    // ===== 国产数据库兼容方言测试 =====
2955
2956    #[test]
2957    fn test_mariadb_dialect() {
2958        let dialect = get_dialect(DbType::MariaDB).unwrap();
2959        assert_eq!(dialect.db_type(), DbType::MariaDB);
2960        // MariaDB 兼容 MySQL 语法:backquote 标识符 + 反斜杠转义
2961        assert_eq!(dialect.quote("users"), "`users`");
2962        assert_eq!(dialect.escape_string("it's"), "it\\'s");
2963        assert_eq!(dialect.auto_increment_keyword(), "AUTO_INCREMENT");
2964        // MySQL 家族不支持 RETURNING(MySQL 协议层限制)
2965        assert!(!dialect.supports_returning());
2966    }
2967
2968    #[test]
2969    fn test_tidb_dialect() {
2970        let dialect = get_dialect(DbType::TiDB).unwrap();
2971        assert_eq!(dialect.db_type(), DbType::TiDB);
2972        // TiDB 兼容 MySQL 语法
2973        assert_eq!(dialect.quote("users"), "`users`");
2974        assert_eq!(dialect.escape_string("it's"), "it\\'s");
2975        assert_eq!(dialect.auto_increment_keyword(), "AUTO_INCREMENT");
2976    }
2977
2978    #[test]
2979    fn test_dameng_dialect() {
2980        let dialect = get_dialect(DbType::Dameng).unwrap();
2981        assert_eq!(dialect.db_type(), DbType::Dameng);
2982        // 达梦兼容 Oracle 语法:双引号标识符 + 单引号双写
2983        assert_eq!(dialect.quote("users"), "\"users\"");
2984        assert_eq!(dialect.escape_string("it's"), "it''s");
2985        // Oracle 兼容:使用 IDENTITY 列
2986        assert_eq!(
2987            dialect.auto_increment_keyword(),
2988            "GENERATED BY DEFAULT AS IDENTITY"
2989        );
2990        // Oracle 兼容:支持 RETURNING
2991        assert!(dialect.supports_returning());
2992    }
2993
2994    #[test]
2995    fn test_kingbase_dialect() {
2996        let dialect = get_dialect(DbType::Kingbase).unwrap();
2997        assert_eq!(dialect.db_type(), DbType::Kingbase);
2998        // 人大金仓兼容 PostgreSQL 语法:双引号标识符 + 单引号双写
2999        assert_eq!(dialect.quote("users"), "\"users\"");
3000        assert_eq!(dialect.escape_string("it's"), "it''s");
3001        // PG 兼容:支持 RETURNING
3002        assert!(dialect.supports_returning());
3003        // PG 兼容:使用 GENERATED BY DEFAULT AS IDENTITY(PG 10+ 标准)
3004        assert_eq!(
3005            dialect.auto_increment_keyword(),
3006            "GENERATED BY DEFAULT AS IDENTITY"
3007        );
3008    }
3009
3010    #[test]
3011    fn test_polardb_dialect() {
3012        let dialect = get_dialect(DbType::PolarDB).unwrap();
3013        assert_eq!(dialect.db_type(), DbType::PolarDB);
3014        // PolarDB-PG 兼容 PostgreSQL
3015        assert_eq!(dialect.quote("users"), "\"users\"");
3016        assert!(dialect.supports_returning());
3017    }
3018
3019    #[test]
3020    fn test_gaussdb_dialect() {
3021        let dialect = get_dialect(DbType::GaussDB).unwrap();
3022        assert_eq!(dialect.db_type(), DbType::GaussDB);
3023        // GaussDB 兼容 PostgreSQL
3024        assert_eq!(dialect.quote("users"), "\"users\"");
3025        assert!(dialect.supports_returning());
3026    }
3027
3028    #[test]
3029    fn test_gbase_dialect() {
3030        let dialect = get_dialect(DbType::GBase).unwrap();
3031        assert_eq!(dialect.db_type(), DbType::GBase);
3032        // GBase 8s 兼容 T-SQL(使用方括号)
3033        assert_eq!(dialect.quote("users"), "[users]");
3034    }
3035
3036    #[test]
3037    fn test_sybase_dialect() {
3038        let dialect = get_dialect(DbType::Sybase).unwrap();
3039        assert_eq!(dialect.db_type(), DbType::Sybase);
3040        // Sybase ASE 兼容 T-SQL
3041        assert_eq!(dialect.quote("users"), "[users]");
3042    }
3043
3044    // ===== DB2 独立方言测试 =====
3045
3046    #[test]
3047    fn test_db2_dialect_basic() {
3048        let dialect = get_dialect(DbType::Db2).unwrap();
3049        assert_eq!(dialect.db_type(), DbType::Db2);
3050        // DB2 使用双引号标识符
3051        assert_eq!(dialect.quote("users"), "\"users\"");
3052        // DB2 单引号双写
3053        assert_eq!(dialect.escape_string("it's"), "it''s");
3054        // DB2 使用 IDENTITY
3055        assert_eq!(
3056            dialect.auto_increment_keyword(),
3057            "GENERATED ALWAYS AS IDENTITY"
3058        );
3059        // DB2 不支持 IF EXISTS / IF NOT EXISTS(11.5 之前)
3060        assert!(!dialect.supports_if_exists());
3061        assert!(!dialect.supports_if_not_exists());
3062        // DB2 不支持 RETURNING(使用 SELECT FROM FINAL TABLE 代替)
3063        assert!(!dialect.supports_returning());
3064    }
3065
3066    #[test]
3067    fn test_db2_pagination() {
3068        let dialect = Db2Dialect;
3069        // DB2 使用 OFFSET x ROWS FETCH NEXT y ROWS ONLY
3070        let sql = dialect.build_pagination("SELECT * FROM users", 2, 10);
3071        assert_eq!(
3072            sql,
3073            "SELECT * FROM users OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY"
3074        );
3075    }
3076
3077    #[test]
3078    fn test_db2_last_insert_id() {
3079        let dialect = Db2Dialect;
3080        // DB2 使用 IDENTITY_VAL_LOCAL() 获取最后插入的 IDENTITY
3081        assert_eq!(
3082            dialect.last_insert_id_sql(),
3083            Some("SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1")
3084        );
3085    }
3086
3087    #[test]
3088    fn test_db2_concat() {
3089        let dialect = Db2Dialect;
3090        // DB2 使用 || 拼接
3091        assert_eq!(dialect.concat(&["a", "b", "c"]), "a || b || c");
3092        assert_eq!(dialect.concat(&[]), "''");
3093    }
3094
3095    #[test]
3096    fn test_db2_create_table() {
3097        let dialect = Db2Dialect;
3098        let cols = vec![ColumnDef {
3099            name: "id".to_string(),
3100            sql_type: "BIGINT".to_string(),
3101            nullable: false,
3102            default: None,
3103            auto_increment: true,
3104            primary_key: true,
3105        }];
3106        let sql = dialect.build_create_table("users", &cols);
3107        assert!(sql.contains("\"id\" BIGINT"));
3108        assert!(sql.contains("GENERATED ALWAYS AS IDENTITY"));
3109        assert!(sql.contains("PRIMARY KEY"));
3110    }
3111
3112    #[test]
3113    fn test_db2_type_mapping() {
3114        // 验证通用类型到 DB2 类型的映射
3115        assert_eq!(map_to_db2_type("BIGINT"), "BIGINT");
3116        assert_eq!(map_to_db2_type("INT"), "INTEGER");
3117        assert_eq!(map_to_db2_type("INTEGER"), "INTEGER");
3118        assert_eq!(map_to_db2_type("TINYINT"), "SMALLINT");
3119        assert_eq!(map_to_db2_type("SMALLINT"), "SMALLINT");
3120        assert_eq!(map_to_db2_type("TEXT"), "CLOB(2G)");
3121        assert_eq!(map_to_db2_type("LONGTEXT"), "CLOB(2G)");
3122        assert_eq!(map_to_db2_type("BOOLEAN"), "SMALLINT");
3123        assert_eq!(map_to_db2_type("BOOL"), "SMALLINT");
3124        assert_eq!(map_to_db2_type("DATETIME"), "TIMESTAMP");
3125        assert_eq!(map_to_db2_type("TIMESTAMP"), "TIMESTAMP");
3126        assert_eq!(map_to_db2_type("DATE"), "DATE");
3127        assert_eq!(map_to_db2_type("VARCHAR(255)"), "VARCHAR(255)");
3128    }
3129
3130    // ===== ClickHouse 独立方言测试 =====
3131
3132    #[test]
3133    fn test_clickhouse_dialect_basic() {
3134        let dialect = get_dialect(DbType::ClickHouse).unwrap();
3135        assert_eq!(dialect.db_type(), DbType::ClickHouse);
3136        // ClickHouse 使用 backquote 标识符(与 MySQL 一致)
3137        assert_eq!(dialect.quote("users"), "`users`");
3138        // ClickHouse 反斜杠转义(与 MySQL 一致)
3139        assert_eq!(dialect.escape_string("it's"), "it\\'s");
3140        // ClickHouse 不支持 RETURNING
3141        assert!(!dialect.supports_returning());
3142        // ClickHouse 不支持自增列
3143        assert_eq!(dialect.auto_increment_keyword(), "");
3144        // ClickHouse 支持 IF EXISTS / IF NOT EXISTS
3145        assert!(dialect.supports_if_exists());
3146        assert!(dialect.supports_if_not_exists());
3147    }
3148
3149    #[test]
3150    fn test_clickhouse_type_mapping() {
3151        assert_eq!(map_to_clickhouse_type("BIGINT"), "Int64");
3152        assert_eq!(map_to_clickhouse_type("INT"), "Int32");
3153        assert_eq!(map_to_clickhouse_type("INTEGER"), "Int32");
3154        assert_eq!(map_to_clickhouse_type("TINYINT"), "Int16");
3155        assert_eq!(map_to_clickhouse_type("SMALLINT"), "Int16");
3156        assert_eq!(map_to_clickhouse_type("VARCHAR(255)"), "String");
3157        assert_eq!(map_to_clickhouse_type("TEXT"), "String");
3158        assert_eq!(map_to_clickhouse_type("BOOLEAN"), "UInt8");
3159        assert_eq!(map_to_clickhouse_type("BOOL"), "UInt8");
3160        assert_eq!(map_to_clickhouse_type("FLOAT"), "Float32");
3161        assert_eq!(map_to_clickhouse_type("DOUBLE"), "Float64");
3162        assert_eq!(map_to_clickhouse_type("DATETIME"), "DateTime");
3163        assert_eq!(map_to_clickhouse_type("TIMESTAMP"), "DateTime");
3164        assert_eq!(map_to_clickhouse_type("DATE"), "Date");
3165    }
3166
3167    #[test]
3168    fn test_clickhouse_create_table() {
3169        let dialect = ClickHouseDialect;
3170        let cols = vec![ColumnDef {
3171            name: "id".to_string(),
3172            sql_type: "BIGINT".to_string(),
3173            nullable: false,
3174            default: None,
3175            auto_increment: false, // ClickHouse 不支持自增
3176            primary_key: true,
3177        }];
3178        let sql = dialect.build_create_table("users", &cols);
3179        // 必须包含 ENGINE = MergeTree()
3180        assert!(
3181            sql.contains("ENGINE = MergeTree()"),
3182            "ClickHouse CREATE TABLE 必须指定 ENGINE: {}",
3183            sql
3184        );
3185        assert!(sql.contains("`id` Int64"));
3186        assert!(sql.contains("PRIMARY KEY"));
3187    }
3188
3189    #[test]
3190    fn test_clickhouse_json_extract() {
3191        let dialect = ClickHouseDialect;
3192        let sql = dialect.json_extract("data", "$.name");
3193        assert!(
3194            sql.contains("JSONExtractString"),
3195            "ClickHouse 应使用 JSONExtractString: {}",
3196            sql
3197        );
3198    }
3199
3200    #[test]
3201    fn test_clickhouse_concat() {
3202        let dialect = ClickHouseDialect;
3203        // ClickHouse 使用 concat() 函数
3204        assert_eq!(dialect.concat(&["a", "b", "c"]), "concat(a, b, c)");
3205        assert_eq!(dialect.concat(&[]), "''");
3206    }
3207
3208    // ===== DbType 国产数据库变体测试 =====
3209
3210    #[test]
3211    fn test_db_type_dameng_str() {
3212        assert_eq!(DbType::Dameng.as_str(), "dameng");
3213        assert_eq!(DbType::from_str("dameng"), Some(DbType::Dameng));
3214        assert_eq!(DbType::from_str("DM"), Some(DbType::Dameng));
3215        assert_eq!(DbType::from_str("dm8"), Some(DbType::Dameng));
3216        assert_eq!(DbType::Dameng.default_port(), 5236);
3217    }
3218
3219    #[test]
3220    fn test_db_type_kingbase_str() {
3221        assert_eq!(DbType::Kingbase.as_str(), "kingbase");
3222        assert_eq!(DbType::from_str("kingbase"), Some(DbType::Kingbase));
3223        assert_eq!(DbType::Kingbase.default_port(), 54321);
3224    }
3225
3226    #[test]
3227    fn test_db_type_db2_str() {
3228        assert_eq!(DbType::Db2.as_str(), "db2");
3229        assert_eq!(DbType::from_str("db2"), Some(DbType::Db2));
3230        assert_eq!(DbType::Db2.default_port(), 50000);
3231    }
3232
3233    #[test]
3234    fn test_db_type_mariadb_str() {
3235        assert_eq!(DbType::MariaDB.as_str(), "mariadb");
3236        assert_eq!(DbType::from_str("mariadb"), Some(DbType::MariaDB));
3237        assert_eq!(DbType::MariaDB.default_port(), 3306);
3238    }
3239
3240    #[test]
3241    fn test_db_type_tidb_str() {
3242        assert_eq!(DbType::TiDB.as_str(), "tidb");
3243        assert_eq!(DbType::from_str("tidb"), Some(DbType::TiDB));
3244        assert_eq!(DbType::TiDB.default_port(), 4000);
3245    }
3246
3247    #[test]
3248    fn test_db_type_polardb_str() {
3249        assert_eq!(DbType::PolarDB.as_str(), "polardb");
3250        assert_eq!(DbType::from_str("polardb"), Some(DbType::PolarDB));
3251        assert_eq!(DbType::PolarDB.default_port(), 5432);
3252    }
3253
3254    #[test]
3255    fn test_db_type_gaussdb_str() {
3256        assert_eq!(DbType::GaussDB.as_str(), "gaussdb");
3257        assert_eq!(DbType::from_str("gaussdb"), Some(DbType::GaussDB));
3258        assert_eq!(DbType::GaussDB.default_port(), 25308);
3259    }
3260
3261    #[test]
3262    fn test_db_type_gbase_str() {
3263        assert_eq!(DbType::GBase.as_str(), "gbase");
3264        assert_eq!(DbType::from_str("gbase"), Some(DbType::GBase));
3265        assert_eq!(DbType::GBase.default_port(), 9088);
3266    }
3267
3268    #[test]
3269    fn test_db_type_sybase_str() {
3270        assert_eq!(DbType::Sybase.as_str(), "sybase");
3271        assert_eq!(DbType::from_str("sybase"), Some(DbType::Sybase));
3272        assert_eq!(DbType::Sybase.default_port(), 5000);
3273    }
3274
3275    #[test]
3276    fn test_db_type_family_classification() {
3277        // MySQL 家族
3278        assert!(DbType::MySQL.is_mysql_family());
3279        assert!(DbType::MariaDB.is_mysql_family());
3280        assert!(DbType::TiDB.is_mysql_family());
3281        assert!(DbType::OceanBase.is_mysql_family());
3282        assert!(!DbType::PostgreSQL.is_mysql_family());
3283
3284        // PostgreSQL 家族
3285        assert!(DbType::PostgreSQL.is_postgres_family());
3286        assert!(DbType::Kingbase.is_postgres_family());
3287        assert!(DbType::GaussDB.is_postgres_family());
3288        assert!(!DbType::MySQL.is_postgres_family());
3289
3290        // Oracle 家族
3291        assert!(DbType::Oracle.is_oracle_family());
3292        assert!(DbType::Dameng.is_oracle_family());
3293        assert!(!DbType::MySQL.is_oracle_family());
3294    }
3295
3296    #[test]
3297    fn test_db_type_supports_stored_procedure_extended() {
3298        // 所有 SQL 数据库应支持存储过程
3299        assert!(DbType::Dameng.supports_stored_procedure());
3300        assert!(DbType::Kingbase.supports_stored_procedure());
3301        assert!(DbType::Db2.supports_stored_procedure());
3302        assert!(DbType::MariaDB.supports_stored_procedure());
3303        assert!(DbType::TiDB.supports_stored_procedure());
3304        assert!(DbType::PolarDB.supports_stored_procedure());
3305        assert!(DbType::GaussDB.supports_stored_procedure());
3306        assert!(DbType::GBase.supports_stored_procedure());
3307        assert!(DbType::Sybase.supports_stored_procedure());
3308    }
3309
3310    // ===== L-4 修复:表名/列名长度校验 =====
3311
3312    #[test]
3313    fn test_l4_max_identifier_len_constant() {
3314        // MAX_IDENTIFIER_LEN 应为 63(PostgreSQL 最严格值)
3315        assert_eq!(MAX_IDENTIFIER_LEN, 63);
3316    }
3317
3318    #[test]
3319    fn test_l4_quote_checked_valid_identifier() {
3320        let dialect = MySqlDialect;
3321        assert_eq!(dialect.quote_checked("users").unwrap(), "`users`");
3322        assert_eq!(dialect.quote_checked("user_id").unwrap(), "`user_id`");
3323        // 边界:恰好 63 字符
3324        let name_63 = "a".repeat(63);
3325        assert!(dialect.quote_checked(&name_63).is_ok());
3326    }
3327
3328    #[test]
3329    fn test_l4_quote_checked_rejects_too_long() {
3330        let dialect = MySqlDialect;
3331        let long_name = "a".repeat(64); // 64 > 63
3332        let result = dialect.quote_checked(&long_name);
3333        assert!(result.is_err());
3334        match result {
3335            Err(DbError::InvalidInput(msg)) => {
3336                assert!(
3337                    msg.contains("too long"),
3338                    "expected 'too long' error, got: {}",
3339                    msg
3340                );
3341            }
3342            _ => panic!("Expected DbError::InvalidInput"),
3343        }
3344    }
3345
3346    #[test]
3347    fn test_l4_quote_checked_rejects_empty() {
3348        let dialect = MySqlDialect;
3349        let result = dialect.quote_checked("");
3350        assert!(result.is_err());
3351    }
3352
3353    #[test]
3354    fn test_l4_quote_checked_rejects_sql_injection() {
3355        let dialect = MySqlDialect;
3356        // 含分号
3357        assert!(dialect.quote_checked("users; DROP TABLE users").is_err());
3358        // 含引号
3359        assert!(dialect.quote_checked("user'name").is_err());
3360        // 含空格
3361        assert!(dialect.quote_checked("user name").is_err());
3362        // 数字开头
3363        assert!(dialect.quote_checked("1users").is_err());
3364        // 含点号
3365        assert!(dialect.quote_checked("schema.table").is_err());
3366    }
3367
3368    #[test]
3369    fn test_l4_quote_checked_postgres() {
3370        let dialect = PostgreSqlDialect;
3371        assert_eq!(dialect.quote_checked("users").unwrap(), "\"users\"");
3372        assert!(dialect.quote_checked(&"a".repeat(64)).is_err());
3373    }
3374
3375    #[test]
3376    fn test_l4_quote_checked_sqlite() {
3377        let dialect = SqliteDialect;
3378        assert_eq!(dialect.quote_checked("users").unwrap(), "\"users\"");
3379        assert!(dialect.quote_checked(&"a".repeat(64)).is_err());
3380    }
3381
3382    #[test]
3383    fn test_l4_quote_checked_oracle() {
3384        let dialect = OracleDialect;
3385        assert_eq!(dialect.quote_checked("users").unwrap(), "\"users\"");
3386        assert!(dialect.quote_checked(&"a".repeat(64)).is_err());
3387    }
3388
3389    #[test]
3390    fn test_l4_quote_checked_sql_server() {
3391        let dialect = SqlServerDialect;
3392        assert_eq!(dialect.quote_checked("users").unwrap(), "[users]");
3393        assert!(dialect.quote_checked(&"a".repeat(64)).is_err());
3394    }
3395
3396    // ---- DuckDB 方言测试(TASK-033~036) ----
3397
3398    #[test]
3399    fn test_duckdb_quote() {
3400        let dialect = DuckDBDialect;
3401        assert_eq!(dialect.quote("users"), "\"users\"");
3402        assert_eq!(dialect.quote("user\"id"), "\"user\"\"id\"");
3403    }
3404
3405    #[test]
3406    fn test_duckdb_escape() {
3407        let dialect = DuckDBDialect;
3408        assert_eq!(dialect.escape_string("hello"), "hello");
3409        assert_eq!(dialect.escape_string("it's"), "it''s");
3410    }
3411
3412    #[test]
3413    fn test_duckdb_pagination() {
3414        let dialect = DuckDBDialect;
3415        let sql = dialect.build_pagination("SELECT * FROM users", 2, 10);
3416        assert_eq!(sql, "SELECT * FROM users LIMIT 10 OFFSET 10");
3417    }
3418
3419    #[test]
3420    fn test_duckdb_supports() {
3421        let dialect = DuckDBDialect;
3422        assert!(!dialect.supports_returning());
3423        assert!(!dialect.supports_lock_for_update());
3424        assert!(!dialect.supports_lock_shared());
3425        assert!(dialect.supports_if_exists());
3426        assert!(dialect.supports_if_not_exists());
3427    }
3428
3429    #[test]
3430    fn test_duckdb_insert_or_ignore() {
3431        let dialect = DuckDBDialect;
3432        let sql = dialect.build_insert_or_ignore_prefix("users");
3433        assert_eq!(sql, "INSERT OR IGNORE INTO \"users\"");
3434    }
3435
3436    #[test]
3437    fn test_duckdb_create_table() {
3438        let dialect = DuckDBDialect;
3439        let columns = vec![
3440            ColumnDef {
3441                name: "id".to_string(),
3442                sql_type: "BIGINT".to_string(),
3443                nullable: false,
3444                default: None,
3445                auto_increment: true,
3446                primary_key: true,
3447            },
3448            ColumnDef {
3449                name: "name".to_string(),
3450                sql_type: "VARCHAR(255)".to_string(),
3451                nullable: false,
3452                default: None,
3453                auto_increment: false,
3454                primary_key: false,
3455            },
3456        ];
3457        let sql = dialect.build_create_table("users", &columns);
3458        assert!(sql.contains("CREATE TABLE \"users\""));
3459        assert!(sql.contains("\"id\" BIGINT PRIMARY KEY"));
3460        assert!(sql.contains("\"name\" VARCHAR(255)"));
3461    }
3462
3463    #[test]
3464    fn test_duckdb_alter_table() {
3465        let dialect = DuckDBDialect;
3466        let changes = vec![
3467            TableChange::AddColumn(ColumnDef {
3468                name: "age".to_string(),
3469                sql_type: "INTEGER".to_string(),
3470                nullable: true,
3471                default: None,
3472                auto_increment: false,
3473                primary_key: false,
3474            }),
3475        ];
3476        let sql = dialect.build_alter_table("users", &changes);
3477        assert!(sql.contains("ALTER TABLE \"users\" ADD COLUMN \"age\" INTEGER"));
3478    }
3479
3480    #[test]
3481    fn test_duckdb_json_extract() {
3482        let dialect = DuckDBDialect;
3483        let sql = dialect.json_extract("data", "$.user.name");
3484        assert!(sql.contains("->"));
3485        assert!(sql.contains("user.name"));
3486    }
3487
3488    #[test]
3489    fn test_duckdb_concat() {
3490        let dialect = DuckDBDialect;
3491        let sql = dialect.concat(&["a", "b", "c"]);
3492        assert_eq!(sql, "a || b || c");
3493    }
3494
3495    #[test]
3496    fn test_duckdb_bool_to_int() {
3497        let dialect = DuckDBDialect;
3498        let sql = dialect.bool_to_int("active");
3499        assert!(sql.contains("CASE WHEN"));
3500        assert!(sql.contains("THEN 1"));
3501        assert!(sql.contains("ELSE 0"));
3502    }
3503
3504    #[test]
3505    fn test_get_dialect_duckdb() {
3506        let result = get_dialect(DbType::DuckDB);
3507        assert!(result.is_ok());
3508        let dialect = result.unwrap();
3509        assert_eq!(dialect.db_type(), DbType::DuckDB);
3510    }
3511}