Skip to main content

sz_orm_model/
dialect.rs

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