Skip to main content

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