Skip to main content

sz_orm_core/
dialect.rs

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