# uniSql **Repository Path**: tansar/uniSql ## Basic Information - **Project Name**: uniSql - **Description**: uinsql - **Primary Language**: C# - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-09-16 - **Last Updated**: 2026-09-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # uniSql - 统一SQL中间语言 ## 简介 uniSql 是一个 C# 库,提供统一的 SQL 中间表示(Intermediate Representation),用于跨方言 SQL 翻译。它将 MySQL、PostgreSQL、SQL Server、Oracle、SQLite、SAP HANA 等方言的 SQL 解析为通用 AST(抽象语法树),再通过方言生成器输出目标方言的 SQL。 核心理念:**一次解析,多处生成**。无论源 SQL 来自哪种数据库,都先转化为统一的 AST,再由目标方言的生成器输出兼容语法。 ## 架构设计 ### 数据流 ``` Source SQL → Dialect Parser → Common AST → Dialect Generator → Target SQL ``` ### 项目结构 ``` uniSqlFull/ ├── src/ │ └── uniSql.Core/ # 核心库(包含所有方言) │ ├── Ast/ # AST 节点定义 │ ├── Generation/ # 生成器基类 │ ├── Parsing/ # AST 构建辅助类 │ ├── Grammar/ # ANTLR 语法文件 │ │ ├── SqlLexer.g4 # 核心词法定义 │ │ ├── SqlParser.g4 # 核心语法定义 │ │ └── Dialects/ # 方言语法扩展 │ │ ├── MySql/ │ │ ├── PostgreSql/ │ │ ├── SqlServer/ │ │ ├── Oracle/ │ │ ├── Sqlite/ │ │ └── Hana/ │ └── Dialects/ # 方言实现 │ ├── MySql/ # MySQL 方言 │ ├── PostgreSql/ # PostgreSQL 方言 │ ├── SqlServer/ # SQL Server (T-SQL) 方言 │ ├── Oracle/ # Oracle 方言 │ ├── Sqlite/ # SQLite 方言 │ ├── Hana/ # SAP HANA 方言 │ └── Mongo/ # MongoDB 方言(SQL → mongosh) ├── tests/ │ └── uniSql.Tests/ # 单元测试(1504 个测试用例) │ ├── Ddl/ # DDL 测试(CREATE TABLE / ALTER TABLE) │ ├── Dml/ # DML 测试(SELECT / INSERT / UPDATE / DELETE) │ ├── CrossDialect/ # 跨方言翻译测试 │ ├── Mongo/ # MongoDB 翻译测试 │ └── test-results/ # 各方言测试输出文件 │ ├── ddl/ # DDL 测试输出 │ └── dml/ # DML 测试输出 ├── design/ # 设计文档 │ ├── DDL.md # DDL 实现计划 │ ├── sql.md # SQL 特性支持指南 │ └── test.md # 测试指南 └── scripts/ # 构建脚本 ├── regenerate_antlr.sh # ANTLR 语法重新生成脚本 └── run_tests.sh # 运行测试并保存输出 ``` ### 核心组件 | 组件 | 位置 | 说明 | |------|------|------| | `SqlNode` | `Ast/SqlNode.cs` | AST 基类,所有节点继承此类 | | `ISqlParser` | `Dialects/ISqlParser.cs` | 解析器接口,将 SQL 字符串解析为 AST | | `ISqlGenerator` | `Dialects/ISqlGenerator.cs` | 生成器接口,将 AST 转换为 SQL 字符串 | | `SqlDialect` | `Dialects/SqlDialect.cs` | 方言抽象基类,封装 Name / Parser / Generator | | `DialectRegistry` | `Dialects/DialectRegistry.cs` | 方言注册表,管理所有已注册的方言实例 | | `SqlGeneratorBase` | `Generation/SqlGeneratorBase.cs` | 生成器基类,提供通用 SQL 生成逻辑,子类只需覆写方言差异 | ### AST 节点层次 ``` SqlNode (基类) ├── SqlStatement │ ├── SelectStatement # SELECT 语句(含 UNION/INTERSECT/EXCEPT) │ ├── InsertStatement # INSERT 语句(VALUES / INSERT...SELECT) │ ├── UpdateStatement # UPDATE 语句 │ ├── DeleteStatement # DELETE 语句 │ └── CreateTableStatement # CREATE TABLE 语句 ├── ColumnDefinition # 列定义 ├── TableConstraint # 表约束 ├── DataType # 数据类型 ├── TableName # 表名(schema + name) ├── ForeignKeyReference # 外键引用 ├── Clauses │ ├── FromClause / FromItem # FROM 子句及表项 │ ├── JoinClause # JOIN 子句(INNER/LEFT/RIGHT/FULL/CROSS/NATURAL) │ ├── WhereClause # WHERE 子句 │ ├── GroupByClause # GROUP BY 子句 │ ├── HavingClause # HAVING 子句 │ ├── OrderByClause / OrderByItem # ORDER BY 子句 │ ├── LimitClause # LIMIT / OFFSET / FETCH FIRST 子句 │ ├── TopClause # TOP 子句(SQL Server 专用) │ ├── SelectItem # SELECT 列项 │ └── WindowClause # OVER() 窗口函数子句 ├── Expressions │ ├── ColumnRef # 列引用(table.column / schema.table.column) │ ├── TableRef # 表引用(schema.table / database.schema.table) │ ├── Literal # 字面量(字符串、数字、布尔、NULL) │ ├── BinaryExpression # 二元表达式(a op b) │ ├── UnaryExpression # 一元表达式(-a / IS NULL / IS NOT NULL) │ ├── FunctionCall # 函数调用(func(args)) │ ├── CaseExpression # CASE 表达式 │ ├── CastExpression # CAST(expr AS type) │ ├── SubqueryExpression # 子查询表达式((SELECT ...)) │ ├── ExistsExpression # EXISTS (SELECT ...) │ ├── InExpression # IN (值列表) 或 IN (子查询) │ ├── WindowedFunction # 窗口函数(ROW_NUMBER() OVER(...)) │ └── StarExpression # 通配符(* / table.*) └── Enums ├── JoinType # Inner, Left, Right, Full, Cross, Natural ├── SetOperator # Union, UnionAll, Intersect, Except ├── SortDirection # Asc, Desc ├── ComparisonOperator # Equal, NotEqual, LessThan, ... ├── ConstraintType # PrimaryKey, Unique, ForeignKey, Check └── ReferenceAction # Cascade, SetNull, SetDefault, NoAction ``` ## 快速开始 ### 安装 ```xml ``` ### 使用示例 ```csharp using uniSql.Core.Dialects; using uniSql.Dialect.MySql; using uniSql.Dialect.PostgreSql; using uniSql.Dialect.SqlServer; using uniSql.Dialect.Oracle; using uniSql.Dialect.Hana; using uniSql.Dialect.Sqlite; // 注册方言 DialectRegistry.Register(new MySqlDialect()); DialectRegistry.Register(new PostgreSqlDialect()); DialectRegistry.Register(new TSqlDialect()); DialectRegistry.Register(new OracleDialect()); DialectRegistry.Register(new SqliteDialect()); DialectRegistry.Register(new HanaDialect()); // 从 MySQL 方言解析 SQL var parser = DialectRegistry.Get("mysql")!.CreateParser(); var ast = parser.Parse("SELECT * FROM users WHERE age > 18"); // 生成 PostgreSQL 方言 SQL var pgGen = DialectRegistry.Get("postgresql")!.CreateGenerator(); var pgResult = pgGen.Generate(ast); // → SELECT * FROM "users" WHERE "age" > 18; // 生成 SQL Server 方言 SQL var tsqlGen = DialectRegistry.Get("sqlserver")!.CreateGenerator(); var tsqlResult = tsqlGen.Generate(ast); // → SELECT * FROM [users] WHERE [age] > 18; // 生成 Oracle 方言 SQL var oracleGen = DialectRegistry.Get("oracle")!.CreateGenerator(); var oracleResult = oracleGen.Generate(ast); // → SELECT * FROM "users" WHERE "age" > 18; // 生成 HANA 方言 SQL var hanaGen = DialectRegistry.Get("hana")!.CreateGenerator(); var hanaResult = hanaGen.Generate(ast); // → SELECT * FROM "users" WHERE "age" > 18; ``` ## 支持的 SQL 语句 | 语句类型 | 支持的子句/特性 | |----------|-----------------| | **SELECT** | DISTINCT / ALL, TOP (SQL Server), JOIN (INNER/LEFT/RIGHT/FULL/CROSS/NATURAL), WHERE, GROUP BY, HAVING, ORDER BY (ASC/DESC, NULLS FIRST/LAST), LIMIT/OFFSET, FETCH FIRST, UNION / UNION ALL / INTERSECT / EXCEPT, 子查询, CASE WHEN, 窗口函数 OVER() | | **INSERT** | VALUES 多行插入, INSERT...SELECT, 列列表指定 | | **UPDATE** | SET 多列赋值, WHERE, LIMIT, FROM 子句 (SQL Server) | | **DELETE** | WHERE, LIMIT, USING (PostgreSQL), ORDER BY + LIMIT (MySQL) | | **SET 操作** | UNION, UNION ALL, INTERSECT, EXCEPT (Oracle 中为 MINUS) | | **CREATE TABLE** | 列定义, 数据类型, PRIMARY KEY / UNIQUE / CHECK 约束, DEFAULT, NOT NULL / NULL, AUTO_INCREMENT, COMMENT, FOREIGN KEY REFERENCES, 临时表 (CREATE LOCAL TABLE) | | **ALTER TABLE** | ADD COLUMN, DROP COLUMN, MODIFY/ALTER COLUMN, RENAME TO | ## 方言差异处理 | 特性 | MySQL | PostgreSQL | SQL Server | Oracle | SQLite | HANA | |------|-------|------------|------------|--------|--------|------| | **标识符引用** | `` `name` `` | `"name"` | `[name]` | `"name"` | `"name"` | `"name"` | | **限制行数** | `LIMIT n` | `LIMIT n` | `TOP (n)` / `OFFSET...FETCH` | `FETCH FIRST n ROWS ONLY` | `LIMIT n` | `LIMIT n` / `FETCH FIRST` | | **OFFSET 语法** | `LIMIT n OFFSET m` | `OFFSET m ROWS` | `OFFSET m ROWS FETCH NEXT n ROWS` | `OFFSET m ROWS FETCH FIRST n ROWS` | `LIMIT n OFFSET m` | `OFFSET m ROWS FETCH FIRST n ROWS` | | **表别名语法** | `AS` 可选 | `AS` 可选 | `AS` 可选 | 不支持 `AS` 关键字 | `AS` 可选 | `AS` 可选 | | **EXCEPT** | EXCEPT | EXCEPT | EXCEPT | MINUS | EXCEPT | EXCEPT | | **UPDATE...FROM** | 不支持 | 不支持 | 支持 FROM 子句 | 不支持 | 不支持 | 不支持 | | **DELETE...LIMIT** | `DELETE...LIMIT n` | 不支持 | 不支持 | 不支持 | `DELETE...LIMIT n` | `DELETE...LIMIT n` | | **GROUP BY...WITH ROLLUP** | 支持 | 不支持 | 不支持 | 不支持 | 不支持 | 不支持 | | **NULLS FIRST/LAST** | 不支持 | 支持 | 不支持 | 支持 | 不支持 | 支持 | | **NATURAL JOIN** | 支持 | 支持 | 不支持 | 支持 | 不支持 | 不支持 | | **窗口函数** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | | **FETCH FIRST PERCENT** | 不支持 | 不支持 | 不支持 | 支持 | 不支持 | 不支持 | | **FETCH FIRST WITH TIES** | 不支持 | 支持 | 不支持 | 支持 | 不支持 | 支持 | | **Dummy 表处理** | 丢弃 `FROM dual` | 丢弃 `FROM dual` | 丢弃 `FROM dual` | 保留 `FROM dual` | 丢弃 `FROM dual` | 转换为 `FROM dummy` | | **CREATE TABLE** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | | **临时表** | `TEMPORARY TABLE` | `TEMP TABLE` | `GLOBAL TEMPORARY TABLE` | `#name` | `TEMP TABLE` | `LOCAL TEMPORARY TABLE` | | **AUTO_INCREMENT** | 直接支持 | `GENERATED AS IDENTITY` | `GENERATED AS IDENTITY` | `IDENTITY(1,1)` | `AUTOINCREMENT` | `GENERATED AS IDENTITY` | | **COMMENT** | 内联支持 | `COMMENT ON COLUMN` | `COMMENT ON COLUMN` | `sp_addextendedproperty` | 不支持 | 内联支持 | | **BIT 类型** | `TINYINT(1)` | `BOOLEAN` | `NUMBER(1)` | `BIT` | `INTEGER` | `BOOLEAN` | | **NVARCHAR 类型** | `VARCHAR(n) utf8mb4` | `VARCHAR(n)` | `NVARCHAR2(n)` | `NVARCHAR(n)` | `TEXT` | `NVARCHAR(n)` | ### ALTER TABLE 各方言适配 | 操作 | MySQL | PostgreSQL | Oracle | SQL Server | SQLite | HANA | |------|-------|-----------|--------|-----------|--------|------| | **ADD COLUMN** | `ADD [COLUMN] col` | `ADD [COLUMN] col` | `ADD (col)` | `ADD col` | `ADD COLUMN col` | `ADD (col)` | | **DROP COLUMN** | `DROP [COLUMN] col` | `DROP [COLUMN] col` | `DROP COLUMN col` | `DROP COLUMN col` | 不支持 | `DROP [COLUMN] col` | | **MODIFY COLUMN** | `MODIFY [COLUMN] col type` | `ALTER COLUMN col TYPE type` | `MODIFY (col type)` | `ALTER COLUMN col type` | 不支持 | `ALTER COLUMN col TYPE type` | | **RENAME TABLE** | `RENAME TO new` | `RENAME TO new` | `RENAME t TO new` | `sp_rename 't', 'new'` | `RENAME TO new` | `RENAME TO new` | ### JOIN 类型各方言适配 所有 JOIN 类型均使用标准 SQL 语法,各方言原生支持,无需特殊转换。 | JOIN 类型 | MySQL | PostgreSQL | Oracle | SQL Server | SQLite | HANA | |-----------|-------|-----------|--------|-----------|--------|------| | `INNER JOIN` | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | | `LEFT JOIN` | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | | `RIGHT JOIN` | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | | `FULL JOIN` | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | | `CROSS JOIN` | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ### CREATE TABLE DDL 支持 uniSql 支持跨方言的 CREATE TABLE 语法,使用统一的中间表示,由 Generator 自动适配各目标方言。 #### 统一语法示例 ```sql -- 统一语法(与方言无关) CREATE TABLE employees ( id INT AUTO_INCREMENT KEY, name VARCHAR(100) NOT NULL COMMENT '员工姓名', email VARCHAR(200) UNIQUE COMMENT '邮箱地址', salary DECIMAL(10,2) DEFAULT 0 COMMENT '薪资', department_id INT REFERENCES departments(id) ON DELETE SET NULL ); ``` #### 各方言生成结果 | 方言 | 生成结果 | |------|----------| | **MySQL** | `CREATE TABLE \`employees\` (\`id\` INT AUTO_INCREMENT, \`name\` VARCHAR(100) NOT NULL COMMENT '员工姓名', \`email\` VARCHAR(200) UNIQUE COMMENT '邮箱地址', \`salary\` DECIMAL(10, 2) DEFAULT 0 COMMENT '薪资', \`department_id\` INT REFERENCES \`departments\` (\`id\`) ON DELETE SET NULL);` | | **PostgreSQL** | `CREATE TABLE "employees" ("id" INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, "name" VARCHAR(100) NOT NULL, ...); COMMENT ON COLUMN "employees"."name" IS '员工姓名';` | | **Oracle** | `CREATE TABLE "employees" ("id" NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, "name" VARCHAR2(100) NOT NULL, ...); COMMENT ON COLUMN "employees"."name" IS '员工姓名';` | | **SQL Server** | `CREATE TABLE [employees] ([id] INT IDENTITY(1,1) PRIMARY KEY, [name] NVARCHAR(100) NOT NULL, ...); EXEC sys.sp_addextendedproperty ...` | | **SQLite** | `CREATE TEMP TABLE "employees" ("id" INTEGER PRIMARY KEY AUTOINCREMENT, "name" TEXT NOT NULL, ...)` | | **HANA** | `CREATE LOCAL TEMPORARY TABLE "employees" ("id" INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, "name" NVARCHAR(100) NOT NULL COMMENT '员工姓名', ...)` | #### 支持的特性 | 特性 | 统一语法 | 说明 | |------|----------|------| | **列定义** | `column_name data_type [constraints]` | 支持所有标准数据类型 | | **主键** | `KEY` (列级) / `PRIMARY KEY (col_list)` (表级) | 列级用 `KEY` 关键字,表级用 `PRIMARY KEY` | | **唯一约束** | `UNIQUE` (列级) / `UNIQUE (col_list)` (表级) | | | **外键** | `REFERENCES table(column) [ON DELETE/UPDATE action]` | 支持 CASCADE, SET NULL, SET DEFAULT, NO ACTION | | **CHECK 约束** | `CHECK (expression)` (列级/表级) | | | **DEFAULT** | `DEFAULT expression` | | | **NOT NULL / NULL** | `NOT NULL` / `NULL` | 默认为 NULL | | **AUTO_INCREMENT** | `AUTO_INCREMENT` | 各方言自动适配 | | **COMMENT** | `COMMENT 'string'` | 各方言自动适配 | | **临时表** | `CREATE LOCAL TABLE` | 各方言自动适配 | | **Schema** | `schema.table_name` | 支持 Schema 前缀 | #### 数据类型映射 | 统一类型 | MySQL | PostgreSQL | Oracle | SQL Server | SQLite | HANA | |---------|-------|-----------|--------|-----------|--------|------| | `INT` | `INT` | `INTEGER` | `NUMBER(10)` | `INT` | `INTEGER` | `INTEGER` | | `BIGINT` | `BIGINT` | `BIGINT` | `NUMBER(19)` | `BIGINT` | `INTEGER` | `BIGINT` | | `VARCHAR(n)` | `VARCHAR(n)` | `VARCHAR(n)` | `VARCHAR2(n)` | `NVARCHAR(n)` | `TEXT` | `NVARCHAR(n)` | | `DECIMAL(p,s)` | `DECIMAL(p,s)` | `NUMERIC(p,s)` | `NUMBER(p,s)` | `DECIMAL(p,s)` | `NUMERIC` | `DECIMAL(p,s)` | | `FLOAT` | `DOUBLE` | `DOUBLE PRECISION` | `BINARY_DOUBLE` | `FLOAT` | `REAL` | `DOUBLE` | | `DATE` | `DATE` | `DATE` | `DATE` | `DATE` | `TEXT` | `DATE` | | `BIT` | `TINYINT(1)` | `BOOLEAN` | `NUMBER(1)` | `BIT` | `INTEGER` | `BOOLEAN` | | `NVARCHAR(n)` | `VARCHAR(n) utf8mb4` | `VARCHAR(n)` | `NVARCHAR2(n)` | `NVARCHAR(n)` | `TEXT` | `NVARCHAR(n)` | #### 临时表映射 | 统一语法 | MySQL | PostgreSQL | Oracle | SQL Server | SQLite | HANA | |---------|-------|-----------|--------|-----------|--------|------| | `CREATE LOCAL TABLE t` | `CREATE TEMPORARY TABLE t` | `CREATE TEMP TABLE t` | `CREATE GLOBAL TEMPORARY TABLE t` | `CREATE TABLE #t` | `CREATE TEMP TABLE t` | `CREATE LOCAL TEMPORARY TABLE t` | ## 跨方言函数名映射 uniSql 在将 MySQL 解析的 AST 生成为目标方言 SQL 时,会自动翻译函数名。 ### 日期/时间函数 | 源函数 (MySQL) | PostgreSQL | SQL Server | Oracle | SQLite | HANA | |---------------|------------|------------|--------|--------|------| | `NOW()` | `NOW()` | `GETDATE()` | `SYSDATE` | `datetime('now')` | `CURRENT_TIMESTAMP` | | `CURRENT_TIMESTAMP` | `CURRENT_TIMESTAMP` | `GETDATE()` | `SYSTIMESTAMP` | `CURRENT_TIMESTAMP` | `CURRENT_TIMESTAMP` | | `CURDATE()` | `CURRENT_DATE` | `CAST(GETDATE() AS DATE)` | `TRUNC(SYSDATE)` | `date('now')` | `CURRENT_DATE` | | `CURTIME()` | `CURRENT_TIME` | `CONVERT(TIME, GETDATE())` | `TO_CHAR(SYSDATE, 'HH24:MI:SS')` | `time('now')` | `CURRENT_TIME` | | `YEAR(date)` | `EXTRACT(YEAR FROM date)` | `YEAR(date)` | `EXTRACT(YEAR FROM date)` | `strftime('%Y', date)` | `YEAR(date)` | | `MONTH(date)` | `EXTRACT(MONTH FROM date)` | `MONTH(date)` | `EXTRACT(MONTH FROM date)` | `strftime('%m', date)` | `MONTH(date)` | | `DAY(date)` | `EXTRACT(DAY FROM date)` | `DAY(date)` | `EXTRACT(DAY FROM date)` | `strftime('%d', date)` | `DAY(date)` | | `DATE_ADD(date, interval)` | `(date + interval)` | `DATEADD(day, interval, date)` | `(date + interval)` | `date(date, '+' \|\| interval \|\| ' days')` | `ADD_DAYS(date, interval)` | | `DATE_SUB(date, interval)` | `(date - interval)` | `DATEADD(day, -interval, date)` | `(date - interval)` | `date(date, '-' \|\| interval \|\| ' days')` | `SUBTRACT_DAYS(date, interval)` | | `DATEDIFF(d1, d2)` | `(d1 - d2)` | `DATEDIFF(d1, d2)` | `(d1 - d2)` | `julianday(d1) - julianday(d2)` | `DAYS_BETWEEN(d1, d2)` | | `DATE_FORMAT(date, fmt)` | `TO_CHAR(date, fmt)` | `FORMAT(date, fmt)` | `TO_CHAR(date, fmt)` | `DATE_FORMAT(date, fmt)` | `TO_CHAR(date, fmt)` | | `TO_CHAR(date, fmt)` | `TO_CHAR(date, fmt)` | `TO_CHAR(date, fmt)` | `TO_CHAR(date, fmt)` | `TO_CHAR(date, fmt)` | `TO_CHAR(date, fmt)` | ### 字符串函数 | 源函数 (MySQL) | PostgreSQL | SQL Server | Oracle | SQLite | HANA | |---------------|------------|------------|--------|--------|------| | `UPPER()` | `UPPER()` | `UPPER()` | `UPPER()` | `UPPER()` | `UPPER()` | | `LOWER()` | `LOWER()` | `LOWER()` | `LOWER()` | `LOWER()` | `LOWER()` | | `LENGTH()` | `LENGTH()` | `LEN()` | `LENGTH()` | `LENGTH()` | `LENGTH()` | | `CHAR_LENGTH()` | `CHAR_LENGTH()` | `LEN()` | `LENGTH()` | `LENGTH()` | `CHARACTER_LENGTH()` | | `CHARACTER_LENGTH()` | `CHARACTER_LENGTH()` | `LEN()` | `LENGTH()` | `LENGTH()` | `CHARACTER_LENGTH()` | | `LOCATE()` | `POSITION()` | `CHARINDEX()` | `INSTR()` | `INSTR()` | `LOCATE()` | | `IFNULL()` | `COALESCE()` | `ISNULL()` | `NVL()` | `IFNULL()` | `IFNULL()` | | `CONCAT()` | `CONCAT()` | `CONCAT()` | `CONCAT()` | `CONCAT()` | `CONCAT()` | | `TRIM()` | `TRIM()` | `TRIM()` | `TRIM()` | `TRIM()` | `TRIM()` | | `SUBSTRING()` | `SUBSTRING()` | `SUBSTRING()` | `SUBSTR()` | `SUBSTR()` | `SUBSTRING()` | | `SUBSTR()` | `SUBSTRING()` | `SUBSTRING()` | `SUBSTR()` | `SUBSTR()` | `SUBSTRING()` | | `REPLACE()` | `REPLACE()` | `REPLACE()` | `REPLACE()` | `REPLACE()` | `REPLACE()` | | `LEFT()` | `LEFT()` | `LEFT()` | `LEFT()` | `LEFT()` | `LEFT()` | | `RIGHT()` | `RIGHT()` | `RIGHT()` | `RIGHT()` | `RIGHT()` | `RIGHT()` | | `LPAD()` | `LPAD()` | `RIGHT(REPLICATE(...))` | `LPAD()` | `LPAD()` | `LPAD()` | | `RPAD()` | `RPAD()` | `LEFT(...REPLICATE(...))` | `RPAD()` | `RPAD()` | `RPAD()` | | `REVERSE()` | `REVERSE()` | `REVERSE()` | `REVERSE()` | `REVERSE()` | `REVERSE()` | ### 数学函数 | 源函数 (MySQL) | PostgreSQL | SQL Server | Oracle | SQLite | HANA | |---------------|------------|------------|--------|--------|------| | `ABS()` | `ABS()` | `ABS()` | `ABS()` | `ABS()` | `ABS()` | | `ROUND()` | `ROUND()` | `ROUND()` | `ROUND()` | `ROUND()` | `ROUND()` | | `CEIL()` | `CEIL()` | `CEILING()` | `CEIL()` | `CEILING()` | `CEILING()` | | `FLOOR()` | `FLOOR()` | `FLOOR()` | `FLOOR()` | `FLOOR()` | `FLOOR()` | | `MOD()` | `MOD()` | `MOD()` | `MOD()` | `MOD()` | `MOD()` | | `POWER()` | `POWER()` | `POWER()` | `POWER()` | `POWER()` | `POWER()` | | `SQRT()` | `SQRT()` | `SQRT()` | `SQRT()` | `SQRT()` | `SQRT()` | | `SIGN()` | `SIGN()` | `SIGN()` | `SIGN()` | `SIGN()` | `SIGN()` | | `TRUNC()` | `TRUNC()` | `ROUND()` | `TRUNC()` | `ROUND()` | `TRUNC()` | | `TRUNCATE()` | `TRUNC()` | `ROUND()` | `TRUNC()` | `ROUND()` | `TRUNC()` | | `POW()` | `POWER()` | `POWER()` | `POWER()` | `POWER()` | `POWER()` | ### 聚合函数 所有聚合函数均使用标准 SQL 语法,各方言原生支持,无需特殊转换。 | 函数 | MySQL | PostgreSQL | Oracle | SQL Server | SQLite | HANA | |------|-------|-----------|--------|-----------|--------|------| | `COUNT(expression)` | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | | `COUNT(DISTINCT expression)` | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | | `SUM(expression)` | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | | `AVG(expression)` | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | | `MIN(expression)` | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | | `MAX(expression)` | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ### 条件/空值处理函数(无需映射,所有方言同名) | 函数 | 说明 | |------|------| | `COALESCE()` | 空值合并 | | `NULLIF()` | 空值比较 | | `CASE WHEN ... THEN ... ELSE ... END` | 条件表达式 | ### 零参数函数处理 零参数日期函数在各方言中输出有差异: | 源函数 (MySQL) | MySQL | PostgreSQL | SQL Server | Oracle | SQLite | HANA | |---------------|-------|------------|------------|--------|--------|------| | `NOW()` | `NOW()` | `NOW()` | `GETDATE()` | `SYSDATE` | `datetime('now')` | `CURRENT_TIMESTAMP` | | `CURDATE()` | `CURDATE()` | `CURRENT_DATE` | `CAST(GETDATE() AS DATE)` | `TRUNC(SYSDATE)` | `date('now')` | `CURRENT_DATE` | | `CURTIME()` | `CURTIME()` | `CURRENT_TIME` | `CONVERT(TIME, GETDATE())` | `TO_CHAR(SYSDATE, 'HH24:MI:SS')` | `time('now')` | `CURRENT_TIME` | | `CURRENT_TIMESTAMP` | `CURRENT_TIMESTAMP` | `CURRENT_TIMESTAMP` | `GETDATE()` | `SYSTIMESTAMP` | `CURRENT_TIMESTAMP` | `CURRENT_TIMESTAMP` | > **注意:** 零参数函数在 Oracle、SQLite、HANA 中输出时不带括号(如 `SYSDATE`、`CURRENT_DATE`)。 ## MongoDB 方言 MongoDB 是文档数据库,uniSql 将其作为特殊方言:输入仍是 SQL,输出为 **mongosh 命令文本**(非 SQL),由调用方交给 mongosh 或驱动执行。 ```csharp using uniSql.Dialect.Mongo; using uniSql.Dialect.MySql; var result = MongoSql.Translate("SELECT * FROM users WHERE age > 18", new MySqlParser()); // → db.users.find({"age": {"$gt": 18}}) ``` - 表名取最后一段作集合名,列名原样作字段名,`id` 列默认映射到 `_id` - 普通查询走 `find` + 投影/排序/跳限;GROUP BY/HAVING/聚合走 `aggregate([...])` - 不支持:JOIN、UNION/INTERSECT/EXCEPT、子查询、CASE、DISTINCT、INSERT...SELECT ## 窗口函数支持 uniSql 支持标准 SQL 窗口函数,包括: ```sql -- ROW_NUMBER / RANK / DENSE_RANK SELECT id, name, ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn FROM employees; -- 聚合窗口函数 SELECT dept, SUM(salary) OVER (PARTITION BY dept) AS total FROM employees; -- 帧范围 SELECT id, SUM(amount) OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM orders; ``` 支持的窗口函数特性: - **函数**:ROW_NUMBER(), RANK(), DENSE_RANK(), NTILE(), LAG(), LEAD(), FIRST_VALUE(), LAST_VALUE(), NTH_VALUE() - **聚合函数**:SUM(), AVG(), COUNT(), MIN(), MAX() 均可带 OVER() - **PARTITION BY**:分区字段列表 - **ORDER BY**:排序字段 + ASC/DESC + NULLS FIRST/LAST - **帧范围**:ROWS / RANGE BETWEEN ... AND ... - UNBOUNDED PRECEDING / n PRECEDING / CURRENT ROW / n FOLLOWING / UNBOUNDED FOLLOWING ### 窗口函数各方言适配 所有窗口函数均使用标准 SQL 语法,各方言原生支持,无需特殊转换。 | 函数 | MySQL (8.0+) | PostgreSQL | Oracle | SQL Server | SQLite (3.25+) | HANA | |------|-------------|-----------|--------|-----------|---------------|------| | `ROW_NUMBER()` | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | | `RANK()` | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | | `DENSE_RANK()` | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | | `NTILE(n)` | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | | `LAG(expr, n)` | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | | `LEAD(expr, n)` | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | | `FIRST_VALUE(expr)` | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | | `LAST_VALUE(expr)` | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | | `NTH_VALUE(expr, n)` | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | | `ROWS/RANGE 帧` | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | > **注意**: MySQL 需要 8.0+ 版本,SQLite 需要 3.25.0+ 版本才支持窗口函数。 ## 扩展新方言 要为 uniSql 添加新的数据库方言支持,按以下步骤操作: ### 1. 创建项目结构 在 `src/uniSql.Core/Dialects/` 下创建新方言目录: ``` src/uniSql.Core/Dialects/YourDialect/ ├── YourDialect.cs # 方言入口(继承 SqlDialect) ├── YourParser.cs # SQL 解析器(实现 ISqlParser) └── YourGenerator.cs # SQL 生成器(继承 SqlGeneratorBase) ``` ### 2. 实现 Parser(语法解析器) 实现 `ISqlParser` 接口,基于 ANTLR 生成的解析器构建统一 AST: ```csharp public class YourParser : ISqlParser { public SqlStatement Parse(string sql) { var inputStream = new AntlrInputStream(sql); var lexer = new YourLexer(inputStream); var tokenStream = new CommonTokenStream(lexer); var parser = new YourDialectParser(tokenStream); // ... 使用 AST 构建辅助类转换为统一 AST } } ``` ### 3. 实现 Generator(SQL 生成器) 继承 `SqlGeneratorBase`,覆写方言差异方法: ```csharp public class YourGenerator : SqlGeneratorBase { protected override string QuoteIdentifier(string name) => $"\"{name}\""; // 覆写 GenerateLimit、GenerateTop 等方言差异方法 protected override string GenerateLimit(LimitClause limit) { if (limit.Count != null) return $"LIMIT {GenerateExpression(limit.Count)}"; return ""; } } ``` ### 4. 实现 Dialect(方言定义) 继承 `SqlDialect`,将 Parser 和 Generator 绑定: ```csharp public class YourDialect : SqlDialect { public override string Name => "yourdialect"; public override ISqlParser CreateParser() => new YourParser(); public override ISqlGenerator CreateGenerator() => new YourGenerator(); } ``` ### 5. 注册方言 ```csharp DialectRegistry.Register(new YourDialect()); ``` ### 6. 添加 ANTLR 语法扩展(可选) 如果目标方言有特殊语法,可在 `Grammar/Dialects/YourDialect/` 下添加语法扩展文件,继承核心语法。 ## 测试 项目包含 **1504 个单元测试**,覆盖所有方言的语法解析和 SQL 生成。 ```bash # 运行所有测试 dotnet test tests/uniSql.Tests/ # 运行 DDL 测试(CREATE TABLE / ALTER TABLE) dotnet test tests/uniSql.Tests/ --filter "FullyQualifiedName~uniSql.Tests.Ddl" # 运行 DML 测试(SELECT / INSERT / UPDATE / DELETE) dotnet test tests/uniSql.Tests/ --filter "FullyQualifiedName~uniSql.Tests.Dml" # 运行跨方言测试 dotnet test tests/uniSql.Tests/ --filter "FullyQualifiedName~uniSql.Tests.CrossDialect" # 运行 MongoDB 翻译测试 dotnet test tests/uniSql.Tests/ --filter "FullyQualifiedName~uniSql.Tests.Mongo" # 运行特定方言 DDL 测试 dotnet test tests/uniSql.Tests/ --filter "FullyQualifiedName~DdlMySqlTests" dotnet test tests/uniSql.Tests/ --filter "FullyQualifiedName~DdlPostgreSqlTests" dotnet test tests/uniSql.Tests/ --filter "FullyQualifiedName~DdlOracleTests" dotnet test tests/uniSql.Tests/ --filter "FullyQualifiedName~DdlSqlServerTests" dotnet test tests/uniSql.Tests/ --filter "FullyQualifiedName~DdlSqliteTests" dotnet test tests/uniSql.Tests/ --filter "FullyQualifiedName~DdlHanaTests" # 运行特定方言 DML 测试 dotnet test tests/uniSql.Tests/ --filter "ClassName~MySqlTests" dotnet test tests/uniSql.Tests/ --filter "ClassName~PostgreSqlTests" dotnet test tests/uniSql.Tests/ --filter "ClassName~SqlServerTests" dotnet test tests/uniSql.Tests/ --filter "ClassName~OracleTests" dotnet test tests/uniSql.Tests/ --filter "ClassName~HanaTests" dotnet test tests/uniSql.Tests/ --filter "ClassName~SqliteTests" # 运行按操作类型分类的 DML 测试 dotnet test tests/uniSql.Tests/ --filter "FullyQualifiedName~uniSql.Tests.Dml.Select" dotnet test tests/uniSql.Tests/ --filter "FullyQualifiedName~uniSql.Tests.Dml.Insert" dotnet test tests/uniSql.Tests/ --filter "FullyQualifiedName~uniSql.Tests.Dml.Update" dotnet test tests/uniSql.Tests/ --filter "FullyQualifiedName~uniSql.Tests.Dml.Delete" # 运行 ALTER TABLE 测试 dotnet test tests/uniSql.Tests/ --filter "FullyQualifiedName~AlterTable" ``` 测试输出文件保存在 `tests/uniSql.Tests/test-results/` 目录下: - `ddl/` - DDL 测试输出(CREATE TABLE / ALTER TABLE) - `dml/` - DML 测试输出(SELECT / INSERT / UPDATE / DELETE) - `select/` - SELECT 测试输出 - `insert/` - INSERT 测试输出 - `update/` - UPDATE 测试输出 - `delete/` - DELETE 测试输出 ## 技术栈 - **.NET Standard 2.0**(兼容 .NET 6 / 7 / 8 / 9 / 10) - **C# latest**(启用 `Nullable` 和 `ImplicitUsings`) - **xUnit 2.5**(单元测试框架) - **ANTLR 4.13**(语法分析器生成工具) - **Zulu JDK 21**(ANTLR 运行时依赖) ## 环境要求 ### JAVA_HOME ```bash export JAVA_HOME=/opt/homebrew/opt/zulu/zulu-21.jdk/Contents/Home ``` ### ANTLR JAR 将 `antlr4.jar` 放置在 `$HOME/bin/antlr4.jar` ### 重新生成 ANTLR 解析器 修改语法文件后,需要重新生成解析器: ```bash # 重新生成核心语法 cd src/uniSql.Core/Grammar java -jar $HOME/bin/antlr4.jar -Dlanguage=CSharp -visitor -listener \ -o AntlrGenerated SqlLexer.g4 SqlParser.g4 # 重新生成方言语法(以 Oracle 为例) cd src/uniSql.Core/Grammar/Dialects/Oracle java -jar $HOME/bin/antlr4.jar -Dlanguage=CSharp -visitor -listener \ -lib ../../Grammar -o AntlrGenerated \ OracleLexer.g4 OracleDialectParser.g4 ``` ## 不支持的语法特性 以下语法特性目前尚未实现: | 特性 | 说明 | 状态 | |------|------|------| | CTE (WITH ... AS) | 公共表表达式 | 语法未定义 | | DELETE TOP | SQL Server 特有 | 语法未定义,生成器已实现 | | UPDATE TOP | SQL Server 特有 | 语法未定义 | | CROSS APPLY / OUTER APPLY | SQL Server 特有 | 语法未定义 | | PIVOT / UNPIVOT | SQL Server 特有 | 语法未定义 | | UPDATE FROM | SQL Server 特有 | 语法未定义 | | JOIN ... USING | 使用 USING 的连接条件 | 语法仅支持 ON | | DISTINCT ON | PostgreSQL 特有 | 语法已定义,但 ON 表达式未保存到 AST | | LATERAL | 派生表中的横向连接 | 语法已定义,但 AST 未捕获 IsLateral 标志 | | GROUP BY WITH ROLLUP | MySQL 特有 | AST 属性存在,但语法未定义 | | 多行 VALUES | INSERT 多行值 | 语法仅支持单行 | ## 许可证 MIT License