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 if trimmed.starts_with("SERIAL") {
2851        sql_type.to_string()
2852    } else {
2853        sql_type.to_string()
2854    }
2855}
2856
2857/// SAP HANA 方言实现(v3.7.0 SQL 生成层;v4.9.0 TASK-003 已集成真实驱动 hdbconnect_async v0.32.0)
2858///
2859/// SAP HANA 内存数据库方言,支持计算列 + CE 函数。
2860/// 真实驱动桥接见 sz-orm-sqlx/src/saphana_adapter.rs(feature dialect-saphana-driver)。
2861#[cfg(feature = "dialect-saphana")]
2862#[derive(Debug, Clone)]
2863pub struct SapHanaDialect;
2864
2865#[cfg(feature = "dialect-saphana")]
2866impl Dialect for SapHanaDialect {
2867    fn clone_box(&self) -> Box<dyn Dialect> {
2868        Box::new(SapHanaDialect)
2869    }
2870
2871    fn db_type(&self) -> DbType {
2872        DbType::SapHana
2873    }
2874
2875    fn quote(&self, identifier: &str) -> String {
2876        format!("\"{}\"", identifier.replace('"', "\"\""))
2877    }
2878
2879    fn escape_string(&self, s: &str) -> String {
2880        let mut escaped = String::with_capacity(s.len() * 2);
2881        for c in s.chars() {
2882            match c {
2883                '\'' => escaped.push_str("''"),
2884                _ => escaped.push(c),
2885            }
2886        }
2887        escaped
2888    }
2889
2890    fn supports_returning(&self) -> bool {
2891        false
2892    }
2893
2894    fn build_pagination(&self, sql: &str, page: u64, limit: u64) -> String {
2895        let offset = page.saturating_sub(1).saturating_mul(limit);
2896        format!("{} LIMIT {} OFFSET {}", sql, limit, offset)
2897    }
2898
2899    fn json_type(&self) -> &'static str {
2900        "NCLOB"
2901    }
2902
2903    fn json_extract(&self, column: &str, path: &str) -> String {
2904        let normalized = if path.starts_with('$') {
2905            path.to_string()
2906        } else {
2907            format!("$.{}", path)
2908        };
2909        format!(
2910            "JSON_VALUE({}, '{}')",
2911            column,
2912            self.escape_string(&normalized)
2913        )
2914    }
2915
2916    fn full_text_search(&self, columns: &[&str], keyword: &str) -> String {
2917        if columns.is_empty() {
2918            return "0".to_string();
2919        }
2920        let escaped = self.escape_string(keyword);
2921        let parts: Vec<String> = columns
2922            .iter()
2923            .map(|c| format!("CONTAINS({}, '{}') > 0", c, escaped))
2924            .collect();
2925        parts.join(" OR ")
2926    }
2927
2928    fn bool_to_int(&self, expr: &str) -> String {
2929        format!("(CASE WHEN {} THEN 1 ELSE 0 END)", expr)
2930    }
2931
2932    fn concat(&self, parts: &[&str]) -> String {
2933        if parts.is_empty() {
2934            return "''".to_string();
2935        }
2936        parts.join(" || ")
2937    }
2938
2939    fn supports_if_exists(&self) -> bool {
2940        true
2941    }
2942
2943    fn supports_if_not_exists(&self) -> bool {
2944        true
2945    }
2946
2947    fn auto_increment_keyword(&self) -> &'static str {
2948        "GENERATED BY DEFAULT AS IDENTITY"
2949    }
2950
2951    fn last_insert_id_sql(&self) -> Option<&'static str> {
2952        None
2953    }
2954
2955    fn build_create_table(&self, table: &str, columns: &[ColumnDef]) -> String {
2956        let cols: Vec<String> = columns
2957            .iter()
2958            .map(|col| {
2959                let hana_type = map_to_saphana_type(&col.sql_type);
2960                let mut sql = format!("{} {}", self.quote(&col.name), hana_type);
2961                if !col.nullable && !col.auto_increment {
2962                    sql.push_str(" NOT NULL");
2963                }
2964                if let Some(default) = &col.default {
2965                    sql.push_str(&format!(" DEFAULT {}", default));
2966                }
2967                if col.auto_increment {
2968                    sql.push_str(&format!(" {}", self.auto_increment_keyword()));
2969                }
2970                if col.primary_key {
2971                    sql.push_str(" PRIMARY KEY");
2972                }
2973                sql
2974            })
2975            .collect();
2976
2977        format!(
2978            "CREATE COLUMN TABLE {} ({})",
2979            self.quote(table),
2980            cols.join(", ")
2981        )
2982    }
2983
2984    fn build_alter_table(&self, table: &str, changes: &[TableChange]) -> String {
2985        let stmts: Vec<String> = changes
2986            .iter()
2987            .map(|change| match change {
2988                TableChange::AddColumn(col) => {
2989                    let hana_type = map_to_saphana_type(&col.sql_type);
2990                    let mut sql = format!(
2991                        "ALTER TABLE {} ADD ({})",
2992                        self.quote(table),
2993                        format!("{} {}", self.quote(&col.name), hana_type)
2994                    );
2995                    if !col.nullable {
2996                        sql.push_str(" NOT NULL");
2997                    }
2998                    if let Some(default) = &col.default {
2999                        sql.push_str(&format!(" DEFAULT {}", default));
3000                    }
3001                    sql
3002                }
3003                TableChange::DropColumn(name) => {
3004                    format!(
3005                        "ALTER TABLE {} DROP ({})",
3006                        self.quote(table),
3007                        self.quote(name)
3008                    )
3009                }
3010                TableChange::ModifyColumn(col) => {
3011                    let hana_type = map_to_saphana_type(&col.sql_type);
3012                    format!(
3013                        "ALTER TABLE {} ALTER ({})",
3014                        self.quote(table),
3015                        format!("{} {}", self.quote(&col.name), 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") || trimmed.starts_with("NVARCHAR") {
3070        sql_type.to_string()
3071    } else if trimmed.starts_with("CHAR") || trimmed.starts_with("NCHAR") {
3072        sql_type.to_string()
3073    } else if matches!(trimmed, "TEXT" | "CLOB") {
3074        "NCLOB".to_string()
3075    } else if matches!(trimmed, "BOOLEAN" | "BOOL") {
3076        "BOOLEAN".to_string()
3077    } else if matches!(trimmed, "FLOAT" | "REAL") {
3078        "REAL".to_string()
3079    } else if matches!(trimmed, "DOUBLE" | "DOUBLE PRECISION") {
3080        "DOUBLE".to_string()
3081    } else if matches!(trimmed, "DATETIME" | "TIMESTAMP") {
3082        "TIMESTAMP".to_string()
3083    } else if matches!(trimmed, "DATE") {
3084        "DATE".to_string()
3085    } else {
3086        sql_type.to_string()
3087    }
3088}
3089
3090/// Firebird 方言实现(v3.7.0,SQL generation only: 仅 SQL 生成,无真实驱动连接)
3091///
3092/// Firebird 数据库方言,支持 GENERATOR/SEQUENCE + EXECUTE BLOCK。
3093/// 无真 DB 驱动(主流 rsfbclient v0.27.0 同步,异步候选不成熟),仅生成 SQL 语法。
3094#[cfg(feature = "dialect-firebird")]
3095#[derive(Debug, Clone)]
3096pub struct FirebirdDialect;
3097
3098#[cfg(feature = "dialect-firebird")]
3099impl Dialect for FirebirdDialect {
3100    fn clone_box(&self) -> Box<dyn Dialect> {
3101        Box::new(FirebirdDialect)
3102    }
3103
3104    fn db_type(&self) -> DbType {
3105        DbType::Firebird
3106    }
3107
3108    fn quote(&self, identifier: &str) -> String {
3109        format!("\"{}\"", identifier.replace('"', "\"\""))
3110    }
3111
3112    fn escape_string(&self, s: &str) -> String {
3113        let mut escaped = String::with_capacity(s.len() * 2);
3114        for c in s.chars() {
3115            match c {
3116                '\'' => escaped.push_str("''"),
3117                _ => escaped.push(c),
3118            }
3119        }
3120        escaped
3121    }
3122
3123    fn supports_returning(&self) -> bool {
3124        true
3125    }
3126
3127    fn build_pagination(&self, sql: &str, page: u64, limit: u64) -> String {
3128        let offset = page.saturating_sub(1).saturating_mul(limit);
3129        let to = offset + limit;
3130        format!("{} ROWS {} TO {}", sql, offset + 1, to)
3131    }
3132
3133    fn json_type(&self) -> &'static str {
3134        "BLOB SUB_TYPE TEXT"
3135    }
3136
3137    fn json_extract(&self, column: &str, path: &str) -> String {
3138        let normalized = if path.starts_with('$') {
3139            path.to_string()
3140        } else {
3141            format!("$.{}", path)
3142        };
3143        format!(
3144            "JSON_GET({}, '{}')",
3145            column,
3146            self.escape_string(&normalized)
3147        )
3148    }
3149
3150    fn full_text_search(&self, columns: &[&str], keyword: &str) -> String {
3151        if columns.is_empty() {
3152            return "0".to_string();
3153        }
3154        let escaped = self.escape_string(keyword);
3155        let parts: Vec<String> = columns
3156            .iter()
3157            .map(|c| format!("CONTAINING({}, '{}')", c, escaped))
3158            .collect();
3159        parts.join(" OR ")
3160    }
3161
3162    fn bool_to_int(&self, expr: &str) -> String {
3163        format!("(CASE WHEN {} THEN 1 ELSE 0 END)", expr)
3164    }
3165
3166    fn concat(&self, parts: &[&str]) -> String {
3167        if parts.is_empty() {
3168            return "''".to_string();
3169        }
3170        parts.join(" || ")
3171    }
3172
3173    fn supports_if_exists(&self) -> bool {
3174        true
3175    }
3176
3177    fn supports_if_not_exists(&self) -> bool {
3178        true
3179    }
3180
3181    fn auto_increment_keyword(&self) -> &'static str {
3182        "GENERATED BY DEFAULT AS IDENTITY"
3183    }
3184
3185    fn last_insert_id_sql(&self) -> Option<&'static str> {
3186        Some("SELECT GEN_ID(SQLITE_SEQUENCE, 0) FROM RDB$DATABASE")
3187    }
3188
3189    fn build_create_table(&self, table: &str, columns: &[ColumnDef]) -> String {
3190        let cols: Vec<String> = columns
3191            .iter()
3192            .map(|col| {
3193                let fb_type = map_to_firebird_type(&col.sql_type);
3194                let mut sql = format!("{} {}", self.quote(&col.name), fb_type);
3195                if !col.nullable && !col.auto_increment {
3196                    sql.push_str(" NOT NULL");
3197                }
3198                if let Some(default) = &col.default {
3199                    sql.push_str(&format!(" DEFAULT {}", default));
3200                }
3201                if col.auto_increment {
3202                    sql.push_str(&format!(" {}", self.auto_increment_keyword()));
3203                }
3204                if col.primary_key {
3205                    sql.push_str(" PRIMARY KEY");
3206                }
3207                sql
3208            })
3209            .collect();
3210
3211        format!("CREATE TABLE {} ({})", self.quote(table), cols.join(", "))
3212    }
3213
3214    fn build_alter_table(&self, table: &str, changes: &[TableChange]) -> String {
3215        let stmts: Vec<String> = changes
3216            .iter()
3217            .map(|change| match change {
3218                TableChange::AddColumn(col) => {
3219                    let fb_type = map_to_firebird_type(&col.sql_type);
3220                    let mut sql = format!(
3221                        "ALTER TABLE {} ADD {} {}",
3222                        self.quote(table),
3223                        self.quote(&col.name),
3224                        fb_type
3225                    );
3226                    if !col.nullable {
3227                        sql.push_str(" NOT NULL");
3228                    }
3229                    if let Some(default) = &col.default {
3230                        sql.push_str(&format!(" DEFAULT {}", default));
3231                    }
3232                    sql
3233                }
3234                TableChange::DropColumn(name) => {
3235                    format!(
3236                        "ALTER TABLE {} DROP {}",
3237                        self.quote(table),
3238                        self.quote(name)
3239                    )
3240                }
3241                TableChange::ModifyColumn(col) => {
3242                    let fb_type = map_to_firebird_type(&col.sql_type);
3243                    format!(
3244                        "ALTER TABLE {} ALTER COLUMN {} TYPE {}",
3245                        self.quote(table),
3246                        self.quote(&col.name),
3247                        fb_type
3248                    )
3249                }
3250                TableChange::AddIndex(name, cols) => {
3251                    format!(
3252                        "CREATE INDEX {} ON {} ({})",
3253                        name,
3254                        self.quote(table),
3255                        cols.join(", ")
3256                    )
3257                }
3258                TableChange::DropIndex(name) => {
3259                    format!("DROP INDEX {}", name)
3260                }
3261                TableChange::AddForeignKey {
3262                    columns,
3263                    reference_table,
3264                    reference_columns,
3265                } => {
3266                    format!(
3267                        "ALTER TABLE {} ADD CONSTRAINT FOREIGN KEY ({}) REFERENCES {} ({})",
3268                        self.quote(table),
3269                        columns
3270                            .iter()
3271                            .map(|c| self.quote(c))
3272                            .collect::<Vec<_>>()
3273                            .join(", "),
3274                        self.quote(reference_table),
3275                        reference_columns
3276                            .iter()
3277                            .map(|c| self.quote(c))
3278                            .collect::<Vec<_>>()
3279                            .join(", ")
3280                    )
3281                }
3282            })
3283            .collect();
3284
3285        stmts.join("; ")
3286    }
3287}
3288
3289/// 将通用 SQL 类型映射为 Firebird 类型
3290#[cfg(feature = "dialect-firebird")]
3291fn map_to_firebird_type(sql_type: &str) -> String {
3292    let upper = sql_type.to_uppercase();
3293    let trimmed = upper.trim();
3294
3295    if trimmed.starts_with("BIGINT") {
3296        "BIGINT".to_string()
3297    } else if matches!(trimmed, "INT" | "INTEGER") {
3298        "INTEGER".to_string()
3299    } else if matches!(trimmed, "SMALLINT") {
3300        "SMALLINT".to_string()
3301    } else if trimmed.starts_with("VARCHAR") || trimmed.starts_with("CHAR") {
3302        sql_type.to_string()
3303    } else if matches!(trimmed, "TEXT" | "BLOB") {
3304        "BLOB SUB_TYPE TEXT".to_string()
3305    } else if matches!(trimmed, "BOOLEAN" | "BOOL") {
3306        "BOOLEAN".to_string()
3307    } else if matches!(trimmed, "FLOAT" | "REAL") {
3308        "FLOAT".to_string()
3309    } else if matches!(trimmed, "DOUBLE" | "DOUBLE PRECISION") {
3310        "DOUBLE PRECISION".to_string()
3311    } else if matches!(trimmed, "DATETIME" | "TIMESTAMP") {
3312        "TIMESTAMP".to_string()
3313    } else if matches!(trimmed, "DATE") {
3314        "DATE".to_string()
3315    } else {
3316        sql_type.to_string()
3317    }
3318}
3319
3320#[cfg(test)]
3321mod tests {
3322    use super::*;
3323
3324    #[test]
3325    fn test_mysql_quote() {
3326        let dialect = MySqlDialect;
3327        assert_eq!(dialect.quote("users"), "`users`");
3328        assert_eq!(dialect.quote("user`id"), "`user``id`");
3329    }
3330
3331    #[test]
3332    fn test_mysql_escape() {
3333        let dialect = MySqlDialect;
3334        assert_eq!(dialect.escape_string("hello"), "hello");
3335        assert_eq!(dialect.escape_string("it's"), "it\\'s");
3336        assert_eq!(dialect.escape_string("line\nbreak"), "line\\nbreak");
3337    }
3338
3339    #[test]
3340    fn test_mysql_pagination() {
3341        let dialect = MySqlDialect;
3342        let sql = dialect.build_pagination("SELECT * FROM users", 2, 10);
3343        assert_eq!(sql, "SELECT * FROM users LIMIT 10 OFFSET 10");
3344    }
3345
3346    #[test]
3347    fn test_postgres_quote() {
3348        let dialect = PostgreSqlDialect;
3349        assert_eq!(dialect.quote("users"), "\"users\"");
3350        assert_eq!(dialect.quote("user\"id"), "\"user\"\"id\"");
3351    }
3352
3353    #[test]
3354    fn test_postgres_pagination() {
3355        let dialect = PostgreSqlDialect;
3356        let sql = dialect.build_pagination("SELECT * FROM users", 3, 20);
3357        assert_eq!(sql, "SELECT * FROM users LIMIT 20 OFFSET 40");
3358    }
3359
3360    #[test]
3361    fn test_postgres_returning() {
3362        let dialect = PostgreSqlDialect;
3363        assert!(dialect.supports_returning());
3364    }
3365
3366    #[test]
3367    fn test_sqlite_quote() {
3368        let dialect = SqliteDialect;
3369        assert_eq!(dialect.quote("users"), "\"users\"");
3370        assert_eq!(dialect.quote("user\"id"), "\"user\"\"id\"");
3371    }
3372
3373    #[test]
3374    fn test_sqlite_escape() {
3375        let dialect = SqliteDialect;
3376        assert_eq!(dialect.escape_string("hello"), "hello");
3377        assert_eq!(dialect.escape_string("it's"), "it''s");
3378    }
3379
3380    #[test]
3381    fn test_get_dialect() {
3382        let dialect = get_dialect(DbType::MySQL);
3383        assert!(dialect.is_ok());
3384
3385        let dialect = get_dialect(DbType::Redis);
3386        assert!(dialect.is_err());
3387    }
3388
3389    #[test]
3390    fn test_bool_to_int() {
3391        let mysql = MySqlDialect;
3392        assert_eq!(mysql.bool_to_int("active"), "IF(active, 1, 0)");
3393
3394        let pg = PostgreSqlDialect;
3395        assert_eq!(
3396            pg.bool_to_int("active"),
3397            "(CASE WHEN active THEN 1 ELSE 0 END)"
3398        );
3399    }
3400
3401    #[test]
3402    fn test_json_extract_with_path() {
3403        let mysql = MySqlDialect;
3404        let sql = mysql.json_extract("data", "$.user.name");
3405        assert!(sql.contains("$.user.name"));
3406        assert!(sql.contains("JSON_EXTRACT"));
3407
3408        let pg = PostgreSqlDialect;
3409        let sql = pg.json_extract("data", "user.name");
3410        assert!(sql.contains("#>>"));
3411
3412        let sqlite = SqliteDialect;
3413        let sql = sqlite.json_extract("data", "$.user.name");
3414        assert!(sql.contains("$.user.name"));
3415        assert!(sql.contains("json_extract"));
3416    }
3417
3418    #[test]
3419    fn test_sqlite_full_text_search() {
3420        let sqlite = SqliteDialect;
3421        let sql = sqlite.full_text_search(&["title", "content"], "hello");
3422        // SQLite 接口只传入列名,无 FTS 表名,因此降级使用 LIKE
3423        assert!(sql.contains("LIKE"));
3424        assert!(sql.contains("title LIKE '%hello%'"));
3425        assert!(sql.contains("content LIKE '%hello%'"));
3426        assert!(sql.contains(" OR "));
3427
3428        // 空列列表返回 "0"(短路避免空 IN/OR)
3429        assert_eq!(sqlite.full_text_search(&[], "hello"), "0");
3430
3431        // 含单引号的关键字需正确转义(SQLite 双单引号)
3432        let sql = sqlite.full_text_search(&["title"], "it's");
3433        assert!(sql.contains("title LIKE '%it''s%'"));
3434    }
3435
3436    #[test]
3437    fn test_alter_table_modify_column() {
3438        let mysql = MySqlDialect;
3439        let col = ColumnDef {
3440            name: "name".to_string(),
3441            sql_type: "VARCHAR(255)".to_string(),
3442            nullable: false,
3443            default: None,
3444            auto_increment: false,
3445            primary_key: false,
3446        };
3447        let sql = mysql.build_alter_table("users", &[TableChange::ModifyColumn(col)]);
3448        assert!(sql.contains("MODIFY COLUMN"));
3449
3450        let pg = PostgreSqlDialect;
3451        let col = ColumnDef {
3452            name: "name".to_string(),
3453            sql_type: "VARCHAR(255)".to_string(),
3454            nullable: false,
3455            default: None,
3456            auto_increment: false,
3457            primary_key: false,
3458        };
3459        let sql = pg.build_alter_table("users", &[TableChange::ModifyColumn(col)]);
3460        assert!(sql.contains("ALTER COLUMN"));
3461        assert!(sql.contains("TYPE"));
3462    }
3463
3464    #[test]
3465    fn test_alter_table_add_foreign_key() {
3466        let mysql = MySqlDialect;
3467        let sql = mysql.build_alter_table(
3468            "orders",
3469            &[TableChange::AddForeignKey {
3470                columns: vec!["user_id".to_string()],
3471                reference_table: "users".to_string(),
3472                reference_columns: vec!["id".to_string()],
3473            }],
3474        );
3475        assert!(sql.contains("FOREIGN KEY"));
3476        assert!(sql.contains("REFERENCES"));
3477
3478        let sqlite = SqliteDialect;
3479        let sql = sqlite.build_alter_table(
3480            "orders",
3481            &[TableChange::AddForeignKey {
3482                columns: vec!["user_id".to_string()],
3483                reference_table: "users".to_string(),
3484                reference_columns: vec!["id".to_string()],
3485            }],
3486        );
3487        // SQLite 不支持,应返回注释
3488        assert!(sql.starts_with("--"));
3489    }
3490
3491    #[test]
3492    fn test_sqlite_alter_table_add_column() {
3493        let sqlite = SqliteDialect;
3494        let col = ColumnDef {
3495            name: "email".to_string(),
3496            sql_type: "TEXT".to_string(),
3497            nullable: true,
3498            default: None,
3499            auto_increment: false,
3500            primary_key: false,
3501        };
3502        let sql = sqlite.build_alter_table("users", &[TableChange::AddColumn(col)]);
3503        assert!(sql.contains("ADD COLUMN"));
3504        assert!(sql.contains("email"));
3505    }
3506
3507    // ===================== Oracle 方言测试 =====================
3508
3509    #[test]
3510    fn test_oracle_quote_and_escape() {
3511        let dialect = OracleDialect;
3512        // 标识符使用双引号包裹,内部双引号双写
3513        assert_eq!(dialect.quote("users"), "\"users\"");
3514        assert_eq!(dialect.quote("user\"id"), "\"user\"\"id\"");
3515        assert_eq!(dialect.quote("column_name"), "\"column_name\"");
3516
3517        // 字符串字面量转义:单引号双写,反斜杠不转义
3518        assert_eq!(dialect.escape_string("hello"), "hello");
3519        assert_eq!(dialect.escape_string("it's"), "it''s");
3520        assert_eq!(dialect.escape_string("O'Brien"), "O''Brien");
3521        assert_eq!(dialect.escape_string("a'b'c"), "a''b''c");
3522        // 反斜杠按原样保留(Oracle 标准行为)
3523        assert_eq!(dialect.escape_string("path\\to"), "path\\to");
3524    }
3525
3526    #[test]
3527    fn test_oracle_pagination() {
3528        let dialect = OracleDialect;
3529        // page=1 为第一页(offset=0),与其他方言保持一致
3530        let sql = dialect.build_pagination("SELECT * FROM users", 1, 10);
3531        assert_eq!(
3532            sql,
3533            "SELECT * FROM users OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY"
3534        );
3535        // page=3, limit=20 → offset=40
3536        let sql = dialect.build_pagination("SELECT * FROM users", 3, 20);
3537        assert_eq!(
3538            sql,
3539            "SELECT * FROM users OFFSET 40 ROWS FETCH NEXT 20 ROWS ONLY"
3540        );
3541        // page=0(边界)→ offset=0
3542        let sql = dialect.build_pagination("SELECT * FROM users", 0, 10);
3543        assert_eq!(
3544            sql,
3545            "SELECT * FROM users OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY"
3546        );
3547    }
3548
3549    #[test]
3550    fn test_oracle_json_extract() {
3551        let dialect = OracleDialect;
3552        // 标准 $.path 格式
3553        let sql = dialect.json_extract("data", "$.user.name");
3554        assert!(sql.contains("JSON_VALUE"));
3555        assert!(sql.contains("$.user.name"));
3556        assert!(sql.starts_with("JSON_VALUE(data, '$.user.name')"));
3557
3558        // 自动补全 $. 前缀
3559        let sql = dialect.json_extract("data", "user.name");
3560        assert!(sql.contains("$.user.name"));
3561        assert!(sql.contains("JSON_VALUE"));
3562
3563        // 单引号转义
3564        let sql = dialect.json_extract("data", "$.key's");
3565        assert!(sql.contains("$.key''s"));
3566    }
3567
3568    #[test]
3569    fn test_oracle_create_table() {
3570        let dialect = OracleDialect;
3571        let columns = vec![
3572            ColumnDef {
3573                name: "id".to_string(),
3574                sql_type: "BIGINT".to_string(),
3575                nullable: false,
3576                default: None,
3577                auto_increment: true,
3578                primary_key: true,
3579            },
3580            ColumnDef {
3581                name: "name".to_string(),
3582                sql_type: "VARCHAR(255)".to_string(),
3583                nullable: false,
3584                default: None,
3585                auto_increment: false,
3586                primary_key: false,
3587            },
3588            ColumnDef {
3589                name: "bio".to_string(),
3590                sql_type: "TEXT".to_string(),
3591                nullable: true,
3592                default: None,
3593                auto_increment: false,
3594                primary_key: false,
3595            },
3596            ColumnDef {
3597                name: "is_active".to_string(),
3598                sql_type: "BOOLEAN".to_string(),
3599                nullable: false,
3600                default: Some("1".to_string()),
3601                auto_increment: false,
3602                primary_key: false,
3603            },
3604        ];
3605        let sql = dialect.build_create_table("users", &columns);
3606        // Oracle 类型映射
3607        assert!(
3608            sql.contains("NUMBER(19)"),
3609            "BIGINT should map to NUMBER(19): {}",
3610            sql
3611        );
3612        assert!(
3613            sql.contains("VARCHAR2(255)"),
3614            "VARCHAR should map to VARCHAR2: {}",
3615            sql
3616        );
3617        assert!(sql.contains("CLOB"), "TEXT should map to CLOB: {}", sql);
3618        assert!(
3619            sql.contains("NUMBER(1)"),
3620            "BOOLEAN should map to NUMBER(1): {}",
3621            sql
3622        );
3623        // 自增与主键
3624        assert!(sql.contains("GENERATED BY DEFAULT AS IDENTITY"));
3625        assert!(sql.contains("PRIMARY KEY"));
3626        assert!(sql.contains("NOT NULL"));
3627        assert!(sql.contains("DEFAULT 1"));
3628        // 标识符使用双引号
3629        assert!(sql.contains("\"users\""));
3630        assert!(sql.contains("\"id\""));
3631    }
3632
3633    #[test]
3634    fn test_oracle_bool_to_int_and_concat() {
3635        let dialect = OracleDialect;
3636        // bool_to_int 使用 CASE WHEN
3637        assert_eq!(
3638            dialect.bool_to_int("active"),
3639            "(CASE WHEN active THEN 1 ELSE 0 END)"
3640        );
3641        assert_eq!(
3642            dialect.bool_to_int("x > 0"),
3643            "(CASE WHEN x > 0 THEN 1 ELSE 0 END)"
3644        );
3645        // concat 使用 || 操作符
3646        assert_eq!(dialect.concat(&["a", "b", "c"]), "a || b || c");
3647        assert_eq!(
3648            dialect.concat(&["first_name", "last_name"]),
3649            "first_name || last_name"
3650        );
3651        // 空列表返回 NULL
3652        assert_eq!(dialect.concat(&[]), "NULL");
3653    }
3654
3655    #[test]
3656    fn test_oracle_misc_dialect_methods() {
3657        let dialect = OracleDialect;
3658        // db_type
3659        assert_eq!(dialect.db_type(), DbType::Oracle);
3660        // supports_returning: Oracle 12c+ 支持
3661        assert!(dialect.supports_returning());
3662        // supports_if_exists / supports_if_not_exists: Oracle 23ai 支持
3663        assert!(dialect.supports_if_exists());
3664        assert!(dialect.supports_if_not_exists());
3665        // auto_increment_keyword
3666        assert_eq!(
3667            dialect.auto_increment_keyword(),
3668            "GENERATED BY DEFAULT AS IDENTITY"
3669        );
3670        // last_insert_id_sql: Oracle 不支持独立可执行的获取最后插入 ID 语句
3671        // RETURNING ... INTO 是 PL/SQL 子句,必须附加在 INSERT 之后
3672        assert_eq!(dialect.last_insert_id_sql(), None);
3673        // json_type
3674        assert_eq!(dialect.json_type(), "JSON");
3675    }
3676
3677    #[test]
3678    fn test_oracle_get_dialect() {
3679        // get_dialect 应返回 OracleDialect 而非 MySqlDialect
3680        let dialect = get_dialect(DbType::Oracle);
3681        assert!(dialect.is_ok(), "Oracle dialect should be available");
3682        let dialect = dialect.unwrap();
3683        assert_eq!(dialect.db_type(), DbType::Oracle);
3684        // 验证不是 MySqlDialect 的回退:Oracle 使用双引号,MySQL 使用反引号
3685        assert_eq!(dialect.quote("users"), "\"users\"");
3686        // 验证 Oracle 特有功能
3687        assert!(dialect.supports_returning());
3688        // Oracle 没有 standalone 的 last_insert_id SQL
3689        assert_eq!(dialect.last_insert_id_sql(), None);
3690    }
3691
3692    #[test]
3693    fn test_oracle_drop_table() {
3694        let dialect = OracleDialect;
3695        // IF EXISTS
3696        let sql = dialect.build_drop_table("users", true);
3697        assert_eq!(sql, "DROP TABLE IF EXISTS \"users\"");
3698        // 不带 IF EXISTS
3699        let sql = dialect.build_drop_table("users", false);
3700        assert_eq!(sql, "DROP TABLE \"users\"");
3701    }
3702
3703    #[test]
3704    fn test_oracle_alter_table() {
3705        let dialect = OracleDialect;
3706        // MODIFY COLUMN: Oracle 使用 MODIFY(不带 COLUMN 关键字)
3707        let col = ColumnDef {
3708            name: "name".to_string(),
3709            sql_type: "VARCHAR(255)".to_string(),
3710            nullable: false,
3711            default: None,
3712            auto_increment: false,
3713            primary_key: false,
3714        };
3715        let sql = dialect.build_alter_table("users", &[TableChange::ModifyColumn(col)]);
3716        assert!(sql.contains("MODIFY"));
3717        assert!(sql.contains("VARCHAR2(255)"));
3718        assert!(!sql.contains("MODIFY COLUMN")); // Oracle 不使用 COLUMN 关键字
3719
3720        // ADD COLUMN
3721        let col = ColumnDef {
3722            name: "email".to_string(),
3723            sql_type: "VARCHAR(255)".to_string(),
3724            nullable: true,
3725            default: None,
3726            auto_increment: false,
3727            primary_key: false,
3728        };
3729        let sql = dialect.build_alter_table("users", &[TableChange::AddColumn(col)]);
3730        assert!(sql.contains("ADD \"email\""));
3731        assert!(sql.contains("VARCHAR2(255)"));
3732
3733        // DROP COLUMN
3734        let sql =
3735            dialect.build_alter_table("users", &[TableChange::DropColumn("email".to_string())]);
3736        assert!(sql.contains("DROP COLUMN"));
3737        assert!(sql.contains("\"email\""));
3738    }
3739
3740    // ===================== SQLite concat 修复测试 =====================
3741
3742    #[test]
3743    fn test_sqlite_concat_handles_null() {
3744        let sqlite = SqliteDialect;
3745        // 修复前:COALESCE(a || b) 在 a 为 NULL 时整体已 NULL,COALESCE 失效
3746        // 修复后:每个参数 COALESCE 为空串,保证拼接结果非 NULL
3747        let sql = sqlite.concat(&["a", "b"]);
3748        assert_eq!(sql, "COALESCE(a, '') || COALESCE(b, '')");
3749        // 单参数
3750        let sql = sqlite.concat(&["a"]);
3751        assert_eq!(sql, "COALESCE(a, '')");
3752        // 空列表
3753        assert_eq!(sqlite.concat(&[]), "NULL");
3754    }
3755
3756    // ===================== SQL Server 方言测试 =====================
3757
3758    #[test]
3759    fn test_sqlserver_quote_and_escape() {
3760        let dialect = SqlServerDialect;
3761        // 标识符使用 [name] 包裹,内部 ] 双写
3762        assert_eq!(dialect.quote("users"), "[users]");
3763        assert_eq!(dialect.quote("col]name"), "[col]]name]");
3764        // 字符串字面量:单引号双写,反斜杠不转义
3765        assert_eq!(dialect.escape_string("hello"), "hello");
3766        assert_eq!(dialect.escape_string("it's"), "it''s");
3767        assert_eq!(dialect.escape_string("O'Brien"), "O''Brien");
3768        assert_eq!(dialect.escape_string("path\\to"), "path\\to");
3769    }
3770
3771    #[test]
3772    fn test_sqlserver_pagination() {
3773        let dialect = SqlServerDialect;
3774        // SQL Server 2012+ 使用 OFFSET ... ROWS FETCH NEXT ... ROWS ONLY
3775        let sql = dialect.build_pagination("SELECT * FROM users", 1, 10);
3776        assert_eq!(
3777            sql,
3778            "SELECT * FROM users OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY"
3779        );
3780        let sql = dialect.build_pagination("SELECT * FROM users", 3, 20);
3781        assert_eq!(
3782            sql,
3783            "SELECT * FROM users OFFSET 40 ROWS FETCH NEXT 20 ROWS ONLY"
3784        );
3785        // page=0 边界
3786        let sql = dialect.build_pagination("SELECT * FROM users", 0, 10);
3787        assert_eq!(
3788            sql,
3789            "SELECT * FROM users OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY"
3790        );
3791    }
3792
3793    #[test]
3794    fn test_sqlserver_misc_dialect_methods() {
3795        let dialect = SqlServerDialect;
3796        assert_eq!(dialect.db_type(), DbType::SqlServer);
3797        // supports_returning: SQL Server 使用 OUTPUT,语义等价
3798        assert!(dialect.supports_returning());
3799        // supports_if_exists / supports_if_not_exists: SQL Server 2016+
3800        assert!(dialect.supports_if_exists());
3801        assert!(dialect.supports_if_not_exists());
3802        // auto_increment_keyword
3803        assert_eq!(dialect.auto_increment_keyword(), "IDENTITY(1,1)");
3804        // last_insert_id_sql
3805        assert_eq!(dialect.last_insert_id_sql(), Some("SCOPE_IDENTITY()"));
3806        // json_type
3807        assert_eq!(dialect.json_type(), "NVARCHAR(MAX)");
3808    }
3809
3810    #[test]
3811    fn test_sqlserver_insert_or_ignore_fallback_to_plain_insert() {
3812        let dialect = SqlServerDialect;
3813        let sql = dialect.build_insert_or_ignore_prefix("users");
3814        // SQL Server 不支持 INSERT OR IGNORE,回退为普通 INSERT(不生成非法 SQL)
3815        assert_eq!(sql, "INSERT INTO [users]");
3816        assert!(!sql.contains("OR IGNORE"));
3817    }
3818
3819    #[test]
3820    fn test_sqlserver_json_extract() {
3821        let dialect = SqlServerDialect;
3822        let sql = dialect.json_extract("data", "$.user.name");
3823        assert!(sql.starts_with("JSON_VALUE(data, '$.user.name')"));
3824        // 自动补 $. 前缀
3825        let sql = dialect.json_extract("data", "user.name");
3826        assert!(sql.contains("$.user.name"));
3827        assert!(sql.contains("JSON_VALUE"));
3828        // 单引号转义
3829        let sql = dialect.json_extract("data", "$.key's");
3830        assert!(sql.contains("$.key''s"));
3831    }
3832
3833    #[test]
3834    fn test_sqlserver_full_text_search() {
3835        let dialect = SqlServerDialect;
3836        let sql = dialect.full_text_search(&["title", "content"], "hello");
3837        assert!(sql.starts_with("CONTAINS(title, content, 'hello')"));
3838        // 空列列表
3839        assert_eq!(dialect.full_text_search(&[], "hello"), "0");
3840        // 单引号转义
3841        let sql = dialect.full_text_search(&["title"], "it's");
3842        assert!(sql.contains("it''s"));
3843    }
3844
3845    #[test]
3846    fn test_sqlserver_bool_to_int_and_concat() {
3847        let dialect = SqlServerDialect;
3848        assert_eq!(
3849            dialect.bool_to_int("active"),
3850            "(CASE WHEN active THEN 1 ELSE 0 END)"
3851        );
3852        assert_eq!(dialect.concat(&["a", "b", "c"]), "CONCAT(a, b, c)");
3853        assert_eq!(dialect.concat(&[]), "NULL");
3854    }
3855
3856    #[test]
3857    fn test_sqlserver_create_table() {
3858        let dialect = SqlServerDialect;
3859        let columns = vec![
3860            ColumnDef {
3861                name: "id".to_string(),
3862                sql_type: "BIGINT".to_string(),
3863                nullable: false,
3864                default: None,
3865                auto_increment: true,
3866                primary_key: true,
3867            },
3868            ColumnDef {
3869                name: "name".to_string(),
3870                sql_type: "VARCHAR(255)".to_string(),
3871                nullable: false,
3872                default: None,
3873                auto_increment: false,
3874                primary_key: false,
3875            },
3876            ColumnDef {
3877                name: "bio".to_string(),
3878                sql_type: "TEXT".to_string(),
3879                nullable: true,
3880                default: None,
3881                auto_increment: false,
3882                primary_key: false,
3883            },
3884            ColumnDef {
3885                name: "is_active".to_string(),
3886                sql_type: "BOOLEAN".to_string(),
3887                nullable: false,
3888                default: Some("1".to_string()),
3889                auto_increment: false,
3890                primary_key: false,
3891            },
3892        ];
3893        let sql = dialect.build_create_table("users", &columns);
3894        // 标识符使用方括号
3895        assert!(sql.contains("[users]"));
3896        assert!(sql.contains("[id]"));
3897        // 类型映射
3898        assert!(sql.contains("IDENTITY(1,1)"));
3899        assert!(
3900            sql.contains("NVARCHAR(255)"),
3901            "VARCHAR should map to NVARCHAR: {}",
3902            sql
3903        );
3904        assert!(
3905            sql.contains("NVARCHAR(MAX)"),
3906            "TEXT should map to NVARCHAR(MAX): {}",
3907            sql
3908        );
3909        assert!(sql.contains("BIT"), "BOOLEAN should map to BIT: {}", sql);
3910        assert!(sql.contains("PRIMARY KEY"));
3911        assert!(sql.contains("NOT NULL"));
3912        assert!(sql.contains("DEFAULT 1"));
3913    }
3914
3915    #[test]
3916    fn test_sqlserver_drop_table() {
3917        let dialect = SqlServerDialect;
3918        assert_eq!(
3919            dialect.build_drop_table("users", true),
3920            "DROP TABLE IF EXISTS [users]"
3921        );
3922        assert_eq!(
3923            dialect.build_drop_table("users", false),
3924            "DROP TABLE [users]"
3925        );
3926    }
3927
3928    #[test]
3929    fn test_sqlserver_alter_table() {
3930        let dialect = SqlServerDialect;
3931        // MODIFY COLUMN: SQL Server 使用 ALTER COLUMN(不是 MODIFY)
3932        let col = ColumnDef {
3933            name: "name".to_string(),
3934            sql_type: "VARCHAR(255)".to_string(),
3935            nullable: false,
3936            default: None,
3937            auto_increment: false,
3938            primary_key: false,
3939        };
3940        let sql = dialect.build_alter_table("users", &[TableChange::ModifyColumn(col)]);
3941        assert!(sql.contains("ALTER COLUMN"));
3942        assert!(sql.contains("NVARCHAR(255)"));
3943        assert!(!sql.contains("MODIFY"));
3944
3945        // ADD COLUMN
3946        let col = ColumnDef {
3947            name: "email".to_string(),
3948            sql_type: "VARCHAR(255)".to_string(),
3949            nullable: true,
3950            default: None,
3951            auto_increment: false,
3952            primary_key: false,
3953        };
3954        let sql = dialect.build_alter_table("users", &[TableChange::AddColumn(col)]);
3955        assert!(sql.contains("ADD [email]"));
3956        assert!(sql.contains("NVARCHAR(255)"));
3957
3958        // DROP COLUMN
3959        let sql =
3960            dialect.build_alter_table("users", &[TableChange::DropColumn("email".to_string())]);
3961        assert!(sql.contains("DROP COLUMN"));
3962        assert!(sql.contains("[email]"));
3963
3964        // DROP INDEX 必须指定表名
3965        let sql =
3966            dialect.build_alter_table("users", &[TableChange::DropIndex("idx_name".to_string())]);
3967        assert!(sql.contains("DROP INDEX idx_name ON [users]"));
3968    }
3969
3970    #[test]
3971    fn test_sqlserver_get_dialect() {
3972        // get_dialect 应返回 SqlServerDialect,而不是 MySqlDialect 回退
3973        let dialect = get_dialect(DbType::SqlServer);
3974        assert!(dialect.is_ok(), "SqlServer dialect should be available");
3975        let dialect = dialect.unwrap();
3976        assert_eq!(dialect.db_type(), DbType::SqlServer);
3977        // 验证不是 MySqlDialect 的回退:SQL Server 使用方括号,MySQL 使用反引号
3978        assert_eq!(dialect.quote("users"), "[users]");
3979        // SQL Server 特有功能
3980        assert_eq!(dialect.last_insert_id_sql(), Some("SCOPE_IDENTITY()"));
3981        assert_eq!(dialect.auto_increment_keyword(), "IDENTITY(1,1)");
3982    }
3983
3984    #[test]
3985    fn test_clickhouse_get_dialect_unsupported() {
3986        // ClickHouse 现已支持独立方言(不再回退到 MySqlDialect)
3987        let dialect = get_dialect(DbType::ClickHouse);
3988        assert!(dialect.is_ok(), "ClickHouse should be supported");
3989        let dialect = dialect.unwrap();
3990        assert_eq!(dialect.db_type(), DbType::ClickHouse);
3991        // ClickHouse 使用 backquote 标识符(与 MySQL 一致)
3992        assert_eq!(dialect.quote("users"), "`users`");
3993        // ClickHouse 不支持 RETURNING
3994        assert!(!dialect.supports_returning());
3995        // ClickHouse 使用 LIMIT offset, limit 分页
3996        let sql = dialect.build_pagination("SELECT * FROM t", 2, 10);
3997        assert_eq!(sql, "SELECT * FROM t LIMIT 10, 10");
3998        // ClickHouse 自增列为空字符串
3999        assert_eq!(dialect.auto_increment_keyword(), "");
4000    }
4001
4002    #[test]
4003    fn test_get_dialect_all_supported_types() {
4004        // 所有声称为支持的方言应正确返回,不支持的应返回 Err
4005        assert!(get_dialect(DbType::MySQL).is_ok());
4006        assert!(get_dialect(DbType::PostgreSQL).is_ok());
4007        assert!(get_dialect(DbType::Sqlite).is_ok());
4008        assert!(get_dialect(DbType::Oracle).is_ok());
4009        assert!(get_dialect(DbType::SqlServer).is_ok());
4010        assert!(get_dialect(DbType::OceanBase).is_ok());
4011        assert!(get_dialect(DbType::ClickHouse).is_ok());
4012        // 国产数据库 & 兼容方言
4013        assert!(get_dialect(DbType::Dameng).is_ok());
4014        assert!(get_dialect(DbType::Kingbase).is_ok());
4015        assert!(get_dialect(DbType::Db2).is_ok());
4016        assert!(get_dialect(DbType::MariaDB).is_ok());
4017        assert!(get_dialect(DbType::TiDB).is_ok());
4018        assert!(get_dialect(DbType::PolarDB).is_ok());
4019        assert!(get_dialect(DbType::GaussDB).is_ok());
4020        assert!(get_dialect(DbType::GBase).is_ok());
4021        assert!(get_dialect(DbType::Sybase).is_ok());
4022        // 不支持标准 SQL 的
4023        assert!(get_dialect(DbType::Redis).is_err());
4024        assert!(get_dialect(DbType::MongoDB).is_err());
4025        assert!(get_dialect(DbType::VectorDb).is_err());
4026        assert!(get_dialect(DbType::PureJsDb).is_err());
4027    }
4028
4029    // ===== 国产数据库兼容方言测试 =====
4030
4031    #[test]
4032    fn test_mariadb_dialect() {
4033        let dialect = get_dialect(DbType::MariaDB).unwrap();
4034        assert_eq!(dialect.db_type(), DbType::MariaDB);
4035        // MariaDB 兼容 MySQL 语法:backquote 标识符 + 反斜杠转义
4036        assert_eq!(dialect.quote("users"), "`users`");
4037        assert_eq!(dialect.escape_string("it's"), "it\\'s");
4038        assert_eq!(dialect.auto_increment_keyword(), "AUTO_INCREMENT");
4039        // MySQL 家族不支持 RETURNING(MySQL 协议层限制)
4040        assert!(!dialect.supports_returning());
4041    }
4042
4043    #[test]
4044    fn test_tidb_dialect() {
4045        let dialect = get_dialect(DbType::TiDB).unwrap();
4046        assert_eq!(dialect.db_type(), DbType::TiDB);
4047        // TiDB 兼容 MySQL 语法
4048        assert_eq!(dialect.quote("users"), "`users`");
4049        assert_eq!(dialect.escape_string("it's"), "it\\'s");
4050        assert_eq!(dialect.auto_increment_keyword(), "AUTO_INCREMENT");
4051    }
4052
4053    #[test]
4054    fn test_dameng_dialect() {
4055        let dialect = get_dialect(DbType::Dameng).unwrap();
4056        assert_eq!(dialect.db_type(), DbType::Dameng);
4057        // 达梦兼容 Oracle 语法:双引号标识符 + 单引号双写
4058        assert_eq!(dialect.quote("users"), "\"users\"");
4059        assert_eq!(dialect.escape_string("it's"), "it''s");
4060        // Oracle 兼容:使用 IDENTITY 列
4061        assert_eq!(
4062            dialect.auto_increment_keyword(),
4063            "GENERATED BY DEFAULT AS IDENTITY"
4064        );
4065        // Oracle 兼容:支持 RETURNING
4066        assert!(dialect.supports_returning());
4067    }
4068
4069    #[test]
4070    fn test_kingbase_dialect() {
4071        let dialect = get_dialect(DbType::Kingbase).unwrap();
4072        assert_eq!(dialect.db_type(), DbType::Kingbase);
4073        // 人大金仓兼容 PostgreSQL 语法:双引号标识符 + 单引号双写
4074        assert_eq!(dialect.quote("users"), "\"users\"");
4075        assert_eq!(dialect.escape_string("it's"), "it''s");
4076        // PG 兼容:支持 RETURNING
4077        assert!(dialect.supports_returning());
4078        // PG 兼容:使用 GENERATED BY DEFAULT AS IDENTITY(PG 10+ 标准)
4079        assert_eq!(
4080            dialect.auto_increment_keyword(),
4081            "GENERATED BY DEFAULT AS IDENTITY"
4082        );
4083    }
4084
4085    #[test]
4086    fn test_polardb_dialect() {
4087        let dialect = get_dialect(DbType::PolarDB).unwrap();
4088        assert_eq!(dialect.db_type(), DbType::PolarDB);
4089        // PolarDB-PG 兼容 PostgreSQL
4090        assert_eq!(dialect.quote("users"), "\"users\"");
4091        assert!(dialect.supports_returning());
4092    }
4093
4094    #[test]
4095    fn test_gaussdb_dialect() {
4096        let dialect = get_dialect(DbType::GaussDB).unwrap();
4097        assert_eq!(dialect.db_type(), DbType::GaussDB);
4098        // GaussDB 兼容 PostgreSQL
4099        assert_eq!(dialect.quote("users"), "\"users\"");
4100        assert!(dialect.supports_returning());
4101    }
4102
4103    #[test]
4104    fn test_gbase_dialect() {
4105        let dialect = get_dialect(DbType::GBase).unwrap();
4106        assert_eq!(dialect.db_type(), DbType::GBase);
4107        // GBase 8s 兼容 T-SQL(使用方括号)
4108        assert_eq!(dialect.quote("users"), "[users]");
4109    }
4110
4111    #[test]
4112    fn test_sybase_dialect() {
4113        let dialect = get_dialect(DbType::Sybase).unwrap();
4114        assert_eq!(dialect.db_type(), DbType::Sybase);
4115        // Sybase ASE 兼容 T-SQL
4116        assert_eq!(dialect.quote("users"), "[users]");
4117    }
4118
4119    // ===== DB2 独立方言测试 =====
4120
4121    #[test]
4122    fn test_db2_dialect_basic() {
4123        let dialect = get_dialect(DbType::Db2).unwrap();
4124        assert_eq!(dialect.db_type(), DbType::Db2);
4125        // DB2 使用双引号标识符
4126        assert_eq!(dialect.quote("users"), "\"users\"");
4127        // DB2 单引号双写
4128        assert_eq!(dialect.escape_string("it's"), "it''s");
4129        // DB2 使用 IDENTITY
4130        assert_eq!(
4131            dialect.auto_increment_keyword(),
4132            "GENERATED ALWAYS AS IDENTITY"
4133        );
4134        // DB2 不支持 IF EXISTS / IF NOT EXISTS(11.5 之前)
4135        assert!(!dialect.supports_if_exists());
4136        assert!(!dialect.supports_if_not_exists());
4137        // DB2 不支持 RETURNING(使用 SELECT FROM FINAL TABLE 代替)
4138        assert!(!dialect.supports_returning());
4139    }
4140
4141    #[test]
4142    fn test_db2_pagination() {
4143        let dialect = Db2Dialect;
4144        // DB2 使用 OFFSET x ROWS FETCH NEXT y ROWS ONLY
4145        let sql = dialect.build_pagination("SELECT * FROM users", 2, 10);
4146        assert_eq!(
4147            sql,
4148            "SELECT * FROM users OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY"
4149        );
4150    }
4151
4152    #[test]
4153    fn test_db2_last_insert_id() {
4154        let dialect = Db2Dialect;
4155        // DB2 使用 IDENTITY_VAL_LOCAL() 获取最后插入的 IDENTITY
4156        assert_eq!(
4157            dialect.last_insert_id_sql(),
4158            Some("SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1")
4159        );
4160    }
4161
4162    #[test]
4163    fn test_db2_concat() {
4164        let dialect = Db2Dialect;
4165        // DB2 使用 || 拼接
4166        assert_eq!(dialect.concat(&["a", "b", "c"]), "a || b || c");
4167        assert_eq!(dialect.concat(&[]), "''");
4168    }
4169
4170    #[test]
4171    fn test_db2_create_table() {
4172        let dialect = Db2Dialect;
4173        let cols = vec![ColumnDef {
4174            name: "id".to_string(),
4175            sql_type: "BIGINT".to_string(),
4176            nullable: false,
4177            default: None,
4178            auto_increment: true,
4179            primary_key: true,
4180        }];
4181        let sql = dialect.build_create_table("users", &cols);
4182        assert!(sql.contains("\"id\" BIGINT"));
4183        assert!(sql.contains("GENERATED ALWAYS AS IDENTITY"));
4184        assert!(sql.contains("PRIMARY KEY"));
4185    }
4186
4187    #[test]
4188    fn test_db2_type_mapping() {
4189        // 验证通用类型到 DB2 类型的映射
4190        assert_eq!(map_to_db2_type("BIGINT"), "BIGINT");
4191        assert_eq!(map_to_db2_type("INT"), "INTEGER");
4192        assert_eq!(map_to_db2_type("INTEGER"), "INTEGER");
4193        assert_eq!(map_to_db2_type("TINYINT"), "SMALLINT");
4194        assert_eq!(map_to_db2_type("SMALLINT"), "SMALLINT");
4195        assert_eq!(map_to_db2_type("TEXT"), "CLOB(2G)");
4196        assert_eq!(map_to_db2_type("LONGTEXT"), "CLOB(2G)");
4197        assert_eq!(map_to_db2_type("BOOLEAN"), "SMALLINT");
4198        assert_eq!(map_to_db2_type("BOOL"), "SMALLINT");
4199        assert_eq!(map_to_db2_type("DATETIME"), "TIMESTAMP");
4200        assert_eq!(map_to_db2_type("TIMESTAMP"), "TIMESTAMP");
4201        assert_eq!(map_to_db2_type("DATE"), "DATE");
4202        assert_eq!(map_to_db2_type("VARCHAR(255)"), "VARCHAR(255)");
4203    }
4204
4205    // ===== ClickHouse 独立方言测试 =====
4206
4207    #[test]
4208    fn test_clickhouse_dialect_basic() {
4209        let dialect = get_dialect(DbType::ClickHouse).unwrap();
4210        assert_eq!(dialect.db_type(), DbType::ClickHouse);
4211        // ClickHouse 使用 backquote 标识符(与 MySQL 一致)
4212        assert_eq!(dialect.quote("users"), "`users`");
4213        // ClickHouse 反斜杠转义(与 MySQL 一致)
4214        assert_eq!(dialect.escape_string("it's"), "it\\'s");
4215        // ClickHouse 不支持 RETURNING
4216        assert!(!dialect.supports_returning());
4217        // ClickHouse 不支持自增列
4218        assert_eq!(dialect.auto_increment_keyword(), "");
4219        // ClickHouse 支持 IF EXISTS / IF NOT EXISTS
4220        assert!(dialect.supports_if_exists());
4221        assert!(dialect.supports_if_not_exists());
4222    }
4223
4224    #[test]
4225    fn test_clickhouse_type_mapping() {
4226        assert_eq!(map_to_clickhouse_type("BIGINT"), "Int64");
4227        assert_eq!(map_to_clickhouse_type("INT"), "Int32");
4228        assert_eq!(map_to_clickhouse_type("INTEGER"), "Int32");
4229        assert_eq!(map_to_clickhouse_type("TINYINT"), "Int16");
4230        assert_eq!(map_to_clickhouse_type("SMALLINT"), "Int16");
4231        assert_eq!(map_to_clickhouse_type("VARCHAR(255)"), "String");
4232        assert_eq!(map_to_clickhouse_type("TEXT"), "String");
4233        assert_eq!(map_to_clickhouse_type("BOOLEAN"), "UInt8");
4234        assert_eq!(map_to_clickhouse_type("BOOL"), "UInt8");
4235        assert_eq!(map_to_clickhouse_type("FLOAT"), "Float32");
4236        assert_eq!(map_to_clickhouse_type("DOUBLE"), "Float64");
4237        assert_eq!(map_to_clickhouse_type("DATETIME"), "DateTime");
4238        assert_eq!(map_to_clickhouse_type("TIMESTAMP"), "DateTime");
4239        assert_eq!(map_to_clickhouse_type("DATE"), "Date");
4240    }
4241
4242    #[test]
4243    fn test_clickhouse_create_table() {
4244        let dialect = ClickHouseDialect;
4245        let cols = vec![ColumnDef {
4246            name: "id".to_string(),
4247            sql_type: "BIGINT".to_string(),
4248            nullable: false,
4249            default: None,
4250            auto_increment: false, // ClickHouse 不支持自增
4251            primary_key: true,
4252        }];
4253        let sql = dialect.build_create_table("users", &cols);
4254        // 必须包含 ENGINE = MergeTree()
4255        assert!(
4256            sql.contains("ENGINE = MergeTree()"),
4257            "ClickHouse CREATE TABLE 必须指定 ENGINE: {}",
4258            sql
4259        );
4260        assert!(sql.contains("`id` Int64"));
4261        assert!(sql.contains("PRIMARY KEY"));
4262    }
4263
4264    #[test]
4265    fn test_clickhouse_json_extract() {
4266        let dialect = ClickHouseDialect;
4267        let sql = dialect.json_extract("data", "$.name");
4268        assert!(
4269            sql.contains("JSONExtractString"),
4270            "ClickHouse 应使用 JSONExtractString: {}",
4271            sql
4272        );
4273    }
4274
4275    #[test]
4276    fn test_clickhouse_concat() {
4277        let dialect = ClickHouseDialect;
4278        // ClickHouse 使用 concat() 函数
4279        assert_eq!(dialect.concat(&["a", "b", "c"]), "concat(a, b, c)");
4280        assert_eq!(dialect.concat(&[]), "''");
4281    }
4282
4283    // ===== DbType 国产数据库变体测试 =====
4284
4285    #[test]
4286    fn test_db_type_dameng_str() {
4287        assert_eq!(DbType::Dameng.as_str(), "dameng");
4288        assert_eq!(DbType::from_str("dameng"), Some(DbType::Dameng));
4289        assert_eq!(DbType::from_str("DM"), Some(DbType::Dameng));
4290        assert_eq!(DbType::from_str("dm8"), Some(DbType::Dameng));
4291        assert_eq!(DbType::Dameng.default_port(), 5236);
4292    }
4293
4294    #[test]
4295    fn test_db_type_kingbase_str() {
4296        assert_eq!(DbType::Kingbase.as_str(), "kingbase");
4297        assert_eq!(DbType::from_str("kingbase"), Some(DbType::Kingbase));
4298        assert_eq!(DbType::Kingbase.default_port(), 54321);
4299    }
4300
4301    #[test]
4302    fn test_db_type_db2_str() {
4303        assert_eq!(DbType::Db2.as_str(), "db2");
4304        assert_eq!(DbType::from_str("db2"), Some(DbType::Db2));
4305        assert_eq!(DbType::Db2.default_port(), 50000);
4306    }
4307
4308    #[test]
4309    fn test_db_type_mariadb_str() {
4310        assert_eq!(DbType::MariaDB.as_str(), "mariadb");
4311        assert_eq!(DbType::from_str("mariadb"), Some(DbType::MariaDB));
4312        assert_eq!(DbType::MariaDB.default_port(), 3306);
4313    }
4314
4315    #[test]
4316    fn test_db_type_tidb_str() {
4317        assert_eq!(DbType::TiDB.as_str(), "tidb");
4318        assert_eq!(DbType::from_str("tidb"), Some(DbType::TiDB));
4319        assert_eq!(DbType::TiDB.default_port(), 4000);
4320    }
4321
4322    #[test]
4323    fn test_db_type_polardb_str() {
4324        assert_eq!(DbType::PolarDB.as_str(), "polardb");
4325        assert_eq!(DbType::from_str("polardb"), Some(DbType::PolarDB));
4326        assert_eq!(DbType::PolarDB.default_port(), 5432);
4327    }
4328
4329    #[test]
4330    fn test_db_type_gaussdb_str() {
4331        assert_eq!(DbType::GaussDB.as_str(), "gaussdb");
4332        assert_eq!(DbType::from_str("gaussdb"), Some(DbType::GaussDB));
4333        assert_eq!(DbType::GaussDB.default_port(), 25308);
4334    }
4335
4336    #[test]
4337    fn test_db_type_gbase_str() {
4338        assert_eq!(DbType::GBase.as_str(), "gbase");
4339        assert_eq!(DbType::from_str("gbase"), Some(DbType::GBase));
4340        assert_eq!(DbType::GBase.default_port(), 9088);
4341    }
4342
4343    #[test]
4344    fn test_db_type_sybase_str() {
4345        assert_eq!(DbType::Sybase.as_str(), "sybase");
4346        assert_eq!(DbType::from_str("sybase"), Some(DbType::Sybase));
4347        assert_eq!(DbType::Sybase.default_port(), 5000);
4348    }
4349
4350    #[test]
4351    fn test_db_type_family_classification() {
4352        // MySQL 家族
4353        assert!(DbType::MySQL.is_mysql_family());
4354        assert!(DbType::MariaDB.is_mysql_family());
4355        assert!(DbType::TiDB.is_mysql_family());
4356        assert!(DbType::OceanBase.is_mysql_family());
4357        assert!(!DbType::PostgreSQL.is_mysql_family());
4358
4359        // PostgreSQL 家族
4360        assert!(DbType::PostgreSQL.is_postgres_family());
4361        assert!(DbType::Kingbase.is_postgres_family());
4362        assert!(DbType::GaussDB.is_postgres_family());
4363        assert!(!DbType::MySQL.is_postgres_family());
4364
4365        // Oracle 家族
4366        assert!(DbType::Oracle.is_oracle_family());
4367        assert!(DbType::Dameng.is_oracle_family());
4368        assert!(!DbType::MySQL.is_oracle_family());
4369    }
4370
4371    #[test]
4372    fn test_db_type_supports_stored_procedure_extended() {
4373        // 所有 SQL 数据库应支持存储过程
4374        assert!(DbType::Dameng.supports_stored_procedure());
4375        assert!(DbType::Kingbase.supports_stored_procedure());
4376        assert!(DbType::Db2.supports_stored_procedure());
4377        assert!(DbType::MariaDB.supports_stored_procedure());
4378        assert!(DbType::TiDB.supports_stored_procedure());
4379        assert!(DbType::PolarDB.supports_stored_procedure());
4380        assert!(DbType::GaussDB.supports_stored_procedure());
4381        assert!(DbType::GBase.supports_stored_procedure());
4382        assert!(DbType::Sybase.supports_stored_procedure());
4383    }
4384
4385    // ===== L-4 修复:表名/列名长度校验 =====
4386
4387    #[test]
4388    fn test_l4_max_identifier_len_constant() {
4389        // MAX_IDENTIFIER_LEN 应为 63(PostgreSQL 最严格值)
4390        assert_eq!(MAX_IDENTIFIER_LEN, 63);
4391    }
4392
4393    #[test]
4394    fn test_l4_quote_checked_valid_identifier() {
4395        let dialect = MySqlDialect;
4396        assert_eq!(dialect.quote_checked("users").unwrap(), "`users`");
4397        assert_eq!(dialect.quote_checked("user_id").unwrap(), "`user_id`");
4398        // 边界:恰好 63 字符
4399        let name_63 = "a".repeat(63);
4400        assert!(dialect.quote_checked(&name_63).is_ok());
4401    }
4402
4403    #[test]
4404    fn test_l4_quote_checked_rejects_too_long() {
4405        let dialect = MySqlDialect;
4406        let long_name = "a".repeat(64); // 64 > 63
4407        let result = dialect.quote_checked(&long_name);
4408        assert!(result.is_err());
4409        match result {
4410            Err(DbError::InvalidInput(msg)) => {
4411                assert!(
4412                    msg.contains("too long"),
4413                    "expected 'too long' error, got: {}",
4414                    msg
4415                );
4416            }
4417            _ => panic!("Expected DbError::InvalidInput"),
4418        }
4419    }
4420
4421    #[test]
4422    fn test_l4_quote_checked_rejects_empty() {
4423        let dialect = MySqlDialect;
4424        let result = dialect.quote_checked("");
4425        assert!(result.is_err());
4426    }
4427
4428    #[test]
4429    fn test_l4_quote_checked_rejects_sql_injection() {
4430        let dialect = MySqlDialect;
4431        // 含分号
4432        assert!(dialect.quote_checked("users; DROP TABLE users").is_err());
4433        // 含引号
4434        assert!(dialect.quote_checked("user'name").is_err());
4435        // 含空格
4436        assert!(dialect.quote_checked("user name").is_err());
4437        // 数字开头
4438        assert!(dialect.quote_checked("1users").is_err());
4439        // 含点号
4440        assert!(dialect.quote_checked("schema.table").is_err());
4441    }
4442
4443    #[test]
4444    fn test_l4_quote_checked_postgres() {
4445        let dialect = PostgreSqlDialect;
4446        assert_eq!(dialect.quote_checked("users").unwrap(), "\"users\"");
4447        assert!(dialect.quote_checked(&"a".repeat(64)).is_err());
4448    }
4449
4450    #[test]
4451    fn test_l4_quote_checked_sqlite() {
4452        let dialect = SqliteDialect;
4453        assert_eq!(dialect.quote_checked("users").unwrap(), "\"users\"");
4454        assert!(dialect.quote_checked(&"a".repeat(64)).is_err());
4455    }
4456
4457    #[test]
4458    fn test_l4_quote_checked_oracle() {
4459        let dialect = OracleDialect;
4460        assert_eq!(dialect.quote_checked("users").unwrap(), "\"users\"");
4461        assert!(dialect.quote_checked(&"a".repeat(64)).is_err());
4462    }
4463
4464    #[test]
4465    fn test_l4_quote_checked_sql_server() {
4466        let dialect = SqlServerDialect;
4467        assert_eq!(dialect.quote_checked("users").unwrap(), "[users]");
4468        assert!(dialect.quote_checked(&"a".repeat(64)).is_err());
4469    }
4470
4471    // ---- DuckDB 方言测试(TASK-033~036) ----
4472
4473    #[test]
4474    fn test_duckdb_quote() {
4475        let dialect = DuckDBDialect;
4476        assert_eq!(dialect.quote("users"), "\"users\"");
4477        assert_eq!(dialect.quote("user\"id"), "\"user\"\"id\"");
4478    }
4479
4480    #[test]
4481    fn test_duckdb_escape() {
4482        let dialect = DuckDBDialect;
4483        assert_eq!(dialect.escape_string("hello"), "hello");
4484        assert_eq!(dialect.escape_string("it's"), "it''s");
4485    }
4486
4487    #[test]
4488    fn test_duckdb_pagination() {
4489        let dialect = DuckDBDialect;
4490        let sql = dialect.build_pagination("SELECT * FROM users", 2, 10);
4491        assert_eq!(sql, "SELECT * FROM users LIMIT 10 OFFSET 10");
4492    }
4493
4494    #[test]
4495    fn test_duckdb_supports() {
4496        let dialect = DuckDBDialect;
4497        assert!(!dialect.supports_returning());
4498        assert!(!dialect.supports_lock_for_update());
4499        assert!(!dialect.supports_lock_shared());
4500        assert!(dialect.supports_if_exists());
4501        assert!(dialect.supports_if_not_exists());
4502    }
4503
4504    #[test]
4505    fn test_duckdb_insert_or_ignore() {
4506        let dialect = DuckDBDialect;
4507        let sql = dialect.build_insert_or_ignore_prefix("users");
4508        assert_eq!(sql, "INSERT OR IGNORE INTO \"users\"");
4509    }
4510
4511    #[test]
4512    fn test_duckdb_create_table() {
4513        let dialect = DuckDBDialect;
4514        let columns = vec![
4515            ColumnDef {
4516                name: "id".to_string(),
4517                sql_type: "BIGINT".to_string(),
4518                nullable: false,
4519                default: None,
4520                auto_increment: true,
4521                primary_key: true,
4522            },
4523            ColumnDef {
4524                name: "name".to_string(),
4525                sql_type: "VARCHAR(255)".to_string(),
4526                nullable: false,
4527                default: None,
4528                auto_increment: false,
4529                primary_key: false,
4530            },
4531        ];
4532        let sql = dialect.build_create_table("users", &columns);
4533        assert!(sql.contains("CREATE TABLE \"users\""));
4534        assert!(sql.contains("\"id\" BIGINT PRIMARY KEY"));
4535        assert!(sql.contains("\"name\" VARCHAR(255)"));
4536    }
4537
4538    #[test]
4539    fn test_duckdb_alter_table() {
4540        let dialect = DuckDBDialect;
4541        let changes = vec![TableChange::AddColumn(ColumnDef {
4542            name: "age".to_string(),
4543            sql_type: "INTEGER".to_string(),
4544            nullable: true,
4545            default: None,
4546            auto_increment: false,
4547            primary_key: false,
4548        })];
4549        let sql = dialect.build_alter_table("users", &changes);
4550        assert!(sql.contains("ALTER TABLE \"users\" ADD COLUMN \"age\" INTEGER"));
4551    }
4552
4553    #[test]
4554    fn test_duckdb_json_extract() {
4555        let dialect = DuckDBDialect;
4556        let sql = dialect.json_extract("data", "$.user.name");
4557        assert!(sql.contains("->"));
4558        assert!(sql.contains("user.name"));
4559    }
4560
4561    #[test]
4562    fn test_duckdb_concat() {
4563        let dialect = DuckDBDialect;
4564        let sql = dialect.concat(&["a", "b", "c"]);
4565        assert_eq!(sql, "a || b || c");
4566    }
4567
4568    #[test]
4569    fn test_duckdb_bool_to_int() {
4570        let dialect = DuckDBDialect;
4571        let sql = dialect.bool_to_int("active");
4572        assert!(sql.contains("CASE WHEN"));
4573        assert!(sql.contains("THEN 1"));
4574        assert!(sql.contains("ELSE 0"));
4575    }
4576
4577    #[test]
4578    fn test_get_dialect_duckdb() {
4579        let result = get_dialect(DbType::DuckDB);
4580        assert!(result.is_ok());
4581        let dialect = result.unwrap();
4582        assert_eq!(dialect.db_type(), DbType::DuckDB);
4583    }
4584}
4585
4586// ============================================================================
4587// v3.4.0 M3-T3:enum dispatch 优化(perf-enum-dispatch feature)
4588// ============================================================================
4589
4590/// 方言种类枚举(用于 match 分发替代 `Box<dyn Dialect>` vtable 查找)
4591///
4592/// 当 `perf-enum-dispatch` feature 启用时,可通过 `DialectKind` 直接 match 分发
4593/// 到具体方言实现,避免动态分发的 vtable 间接寻址开销。
4594#[cfg(feature = "perf-enum-dispatch")]
4595#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4596pub enum DialectKind {
4597    /// MySQL 方言
4598    MySQL,
4599    /// PostgreSQL 方言
4600    PostgreSQL,
4601    /// SQLite 方言
4602    SQLite,
4603    /// Oracle 方言
4604    Oracle,
4605    /// SQL Server 方言
4606    MSSQL,
4607}
4608
4609#[cfg(feature = "perf-enum-dispatch")]
4610impl DialectKind {
4611    /// 从 `DbType` 创建 `DialectKind`(不支持返回 None)
4612    pub fn from_db_type(db_type: DbType) -> Option<Self> {
4613        match db_type {
4614            DbType::MySQL | DbType::MariaDB | DbType::TiDB | DbType::OceanBase => Some(Self::MySQL),
4615            DbType::PostgreSQL | DbType::Kingbase | DbType::PolarDB | DbType::GaussDB => {
4616                Some(Self::PostgreSQL)
4617            }
4618            DbType::Sqlite => Some(Self::SQLite),
4619            DbType::Oracle | DbType::Dameng => Some(Self::Oracle),
4620            DbType::SqlServer | DbType::Sybase | DbType::GBase => Some(Self::MSSQL),
4621            _ => None,
4622        }
4623    }
4624
4625    /// 引用标识符(match 分发,避免 vtable 查找)
4626    pub fn quote(&self, identifier: &str) -> String {
4627        match self {
4628            Self::MySQL => MySqlDialect.quote(identifier),
4629            Self::PostgreSQL => PostgreSqlDialect.quote(identifier),
4630            Self::SQLite => SqliteDialect.quote(identifier),
4631            Self::Oracle => OracleDialect.quote(identifier),
4632            Self::MSSQL => SqlServerDialect.quote(identifier),
4633        }
4634    }
4635
4636    /// 转义字符串字面量(match 分发)
4637    pub fn escape_string(&self, s: &str) -> String {
4638        match self {
4639            Self::MySQL => MySqlDialect.escape_string(s),
4640            Self::PostgreSQL => PostgreSqlDialect.escape_string(s),
4641            Self::SQLite => SqliteDialect.escape_string(s),
4642            Self::Oracle => OracleDialect.escape_string(s),
4643            Self::MSSQL => SqlServerDialect.escape_string(s),
4644        }
4645    }
4646
4647    /// 返回对应的 `DbType`
4648    pub fn db_type(&self) -> DbType {
4649        match self {
4650            Self::MySQL => DbType::MySQL,
4651            Self::PostgreSQL => DbType::PostgreSQL,
4652            Self::SQLite => DbType::Sqlite,
4653            Self::Oracle => DbType::Oracle,
4654            Self::MSSQL => DbType::SqlServer,
4655        }
4656    }
4657
4658    /// 转为 `Box<dyn Dialect>`
4659    pub fn to_dialect(&self) -> Box<dyn Dialect> {
4660        match self {
4661            Self::MySQL => Box::new(MySqlDialect),
4662            Self::PostgreSQL => Box::new(PostgreSqlDialect),
4663            Self::SQLite => Box::new(SqliteDialect),
4664            Self::Oracle => Box::new(OracleDialect),
4665            Self::MSSQL => Box::new(SqlServerDialect),
4666        }
4667    }
4668}
4669
4670#[cfg(all(test, feature = "perf-enum-dispatch"))]
4671mod enum_dispatch_tests {
4672    use super::*;
4673
4674    #[test]
4675    fn test_dialect_kind_from_db_type() {
4676        assert_eq!(
4677            DialectKind::from_db_type(DbType::MySQL),
4678            Some(DialectKind::MySQL)
4679        );
4680        assert_eq!(
4681            DialectKind::from_db_type(DbType::PostgreSQL),
4682            Some(DialectKind::PostgreSQL)
4683        );
4684        assert_eq!(
4685            DialectKind::from_db_type(DbType::Sqlite),
4686            Some(DialectKind::SQLite)
4687        );
4688        assert_eq!(
4689            DialectKind::from_db_type(DbType::Oracle),
4690            Some(DialectKind::Oracle)
4691        );
4692        assert_eq!(
4693            DialectKind::from_db_type(DbType::SqlServer),
4694            Some(DialectKind::MSSQL)
4695        );
4696        assert_eq!(DialectKind::from_db_type(DbType::Redis), None);
4697    }
4698
4699    #[test]
4700    fn test_dialect_kind_quote_diff() {
4701        let kinds = [
4702            DialectKind::MySQL,
4703            DialectKind::PostgreSQL,
4704            DialectKind::SQLite,
4705            DialectKind::Oracle,
4706            DialectKind::MSSQL,
4707        ];
4708        for kind in &kinds {
4709            let enum_result = kind.quote("users");
4710            let box_result = kind.to_dialect().quote("users");
4711            assert_eq!(enum_result, box_result);
4712        }
4713    }
4714
4715    #[test]
4716    fn test_dialect_kind_escape_diff() {
4717        let kinds = [
4718            DialectKind::MySQL,
4719            DialectKind::PostgreSQL,
4720            DialectKind::SQLite,
4721            DialectKind::Oracle,
4722            DialectKind::MSSQL,
4723        ];
4724        for kind in &kinds {
4725            let enum_result = kind.escape_string("it's");
4726            let box_result = kind.to_dialect().escape_string("it's");
4727            assert_eq!(enum_result, box_result);
4728        }
4729    }
4730}