适用版本:
target/release/dbsql(2026-11-10 构建)+plugins/libdbsql_oracle_plugin.so、plugins/libdbsql_mysql_plugin.so(2026-11-13 重建)。 结论口径:每条都在这套二进制上真跑过;复现脚本见 verify.sh,实测记录见 验证附录.md。 单方言细节:T-SQL · PL/SQL · MySQL
1. 三个运行时方言
方言只决定怎么解析和改写 SQL。三种方言共用同一套存储、同一批库表、同一套约束校验和同一个执行引擎——所以数据类型的行为(CHAR(n)、VARCHAR(n)、DECIMAL(p,s))在三种方言下是一致的。
| 显示名 | CLI 取值 | 旧标识(仍兼容) | 执行路径 | 是否需要插件 |
|---|---|---|---|---|
| T-SQL | standard(默认) |
mssql |
内置解析器 → 标准引擎 | 不需要;配了 --plugin 会直接报错 |
| PL/SQL | procedural |
oracle |
插件 libdbsql_oracle_plugin.so + 标准引擎 |
必需 |
| MySQL | mysql |
— | 插件 libdbsql_mysql_plugin.so + 标准引擎 |
必需 |
Dialect枚举在crates/dbsql-sql-oracle/src/dialect.rs,label()给出上表的显示名,code()/parse_code()负责 CLI 取值。- PL/SQL 与 MySQL 插件只做“解析 + 文本改写”:PL/SQL 把
MINUS解析成集合差、去掉FROM DUAL、还原双引号标识符;MySQL 插件负责词法、类型映射、DDL 翻译和存储过程体解释。真正执行仍在标准引擎,所以引用了引擎不认识的特性(例如ROWNUM)时,即使解析器收下了,执行期也会报错。
2. 怎么选方言
CLI
# 默认 T-SQL
dbsql --root /data sql mydb "SELECT TOP 5 * FROM t"
# PL/SQL(插件按方言名自动探测)
dbsql --root /data --dialect procedural sql mydb "SELECT 1 FROM DUAL"
# MySQL
dbsql --root /data --dialect mysql sql mydb "SELECT * FROM t LIMIT 1, 5"
# 显式指定插件(--plugin 必须写在子命令之前)
dbsql --root /data --dialect mysql --plugin /opt/dbsql/libdbsql_mysql_plugin.so \
sql mydb "SELECT 1"
插件探测顺序(未给 --plugin 时):plugins/dbsql_<方言>_plugin.<ext>、plugins/libdbsql_<方言>_plugin.<ext>、当前目录同名文件。so / dll / dylib。
缺插件时的报错:
PL/SQL 方言需要插件,请用 --plugin <路径> 指定 dbsql_oracle_plugin.so
MySQL 方言需要插件,请用 --plugin <路径> 指定 dbsql_mysql_plugin.so
T-SQL 配 --plugin 时:
--plugin 只在 procedural/mysql 方言下生效,当前方言为 standard;请加 --dialect procedural 或 --dialect mysql(收到的插件路径:…)
HTTP / Web
POST /api/sql、POST /api/select、建例程接口都接受请求体里的 "dialect" 字段,取值同上(standard / procedural / mysql,旧值 mssql / oracle 兼容)。注意:无法识别的取值会静默退回 T-SQL,不会报错;拼错方言名时会表现为“语法不支持”。
3. 方言之间是隔离的(例程层面)
- 例程(存储过程 / 函数)按方言登记。T-SQL 建的过程,用 PL/SQL 或 MySQL 的
CALL调用会报:错误:存储过程 'only_tsql' 不存在 - 表和数据不隔离:同一数据根下,三种方言看到同一张表、同一批行。
- 触发器、约束、索引也落在共享的表元数据上;但只有 T-SQL 能建触发器(见各分册)。
4. 三方言共同支持的基础语法
- 查询:
SELECT(DISTINCT、表达式/别名、*/t.*、派生表、标量子查询、EXISTS/IN)、JOIN的INNER/LEFT/RIGHT/FULL、GROUP BY(列/序号/表达式)、HAVING、ORDER BY(列/序号/表达式、ASC/DESC、NULLS FIRST/NULLS LAST)、LIMIT/OFFSET。 - 集合运算:
UNION、UNION ALL、INTERSECT、EXCEPT(PL/SQL 不认EXCEPT,用 Oracle 的MINUS)。T-SQL 同一查询里混用不同运算符会被拒(连续集合运算只支持同一种运算符…),需要分层时用 CTE 包一层。 - CTE:非递归 CTE 三方言都可用;递归 CTE 见 README 矩阵 与各分册(T-SQL / MySQL 可用,PL/SQL 不可用)。
- DML:
INSERT … VALUES(含多行)、UPDATE、DELETE(都允许不带WHERE)、CASE、CAST、LIKE/IN/BETWEEN/IS NULL。 - 聚合:
COUNT/SUM/AVG/MIN/MAX。 - 约束:主键(含复合主键)、
UNIQUE、CHECK(真校验)、DEFAULT(字面量)、NOT NULL。 - DDL:
CREATE/DROP的TABLE、VIEW、PROCEDURE、FUNCTION;ALTER TABLE(各分册列明支持的子句)。 - 字符串比较与
LIKE一律区分大小写(三方言一致)。 - NULL 排序:
ASC时 NULL 在前、DESC时 NULL 在后(三方言一致)。
5. 三方言共同不支持
| 功能 | 实测报错 | 替代 |
|---|---|---|
外键(REFERENCES / FOREIGN KEY) |
外键约束 '…'(引用 '…')暂不支持:外键的跨表校验还没有实现,收下声明只会让孤儿行照插进来。请改用 NOT NULL + 应用层校验,或把引用关系交给存储过程检查。 |
NOT NULL + 应用层 / 过程校验 |
CROSS JOIN |
错误:SELECT 语句末尾存在多余内容(位于 …) |
逗号连接(FROM a, b)或显式 INNER JOIN … ON 1=1 |
| 一条命令执行多条顶层语句 | 错误:SELECT 语句末尾存在多余内容 / 只支持单条语句;语句结束后仍有多余内容:'SELECT' |
一次一条;PL/SQL 可用匿名块,过程体内可分多条 |
| 跨库/带架构前缀的表名 | 表名 'dbo' 带库/架构前缀,dbsql 不支持跨库引用(请改写为同库表名) |
只写同库表名 |
| 事务跨命令/跨连接 | 单独执行 COMMIT → 错误:没有活动事务 |
见第 7 节 |
列长度/精度“装饰”(VARCHAR(n) 校验、NUMBER(p,s) 校验) |
不报错也不生效(VARCHAR(5) 可存 8 个字符) |
应用层校验;需要严格小数位时用 T-SQL 的 DECIMAL(p,s) |
6. 横向矩阵
✔ = 实测可用;✘ = 实测不可用或有明确限制;— = 该方言不存在此语法。
| 功能 | T-SQL | PL/SQL | MySQL |
|---|---|---|---|
SELECT / JOIN / GROUP BY / HAVING / ORDER BY |
✔ | ✔ | ✔ |
CROSS JOIN |
✘ | ✘ | ✘ |
| CTE(非递归) | ✔ | ✔ | ✔ |
| 递归 CTE | ✔(UNION ALL) |
✘(递归名被当表名) | ✔(WITH RECURSIVE) |
UNION / INTERSECT / EXCEPT |
✔(同类可链式) | ✔ UNION / INTERSECT / MINUS;EXCEPT 不识别 |
✔ |
ROW_NUMBER() OVER (ORDER BY …) |
✔ | ✘(窗口语法未解析) | ✘(窗口语法未解析) |
PARTITION BY |
✘ | ✘ | ✘ |
LIMIT / OFFSET |
✔ | ✔ | ✔ |
OFFSET … FETCH NEXT / FETCH FIRST |
✔ | ✔(FETCH NEXT 须跟在 OFFSET 后;单独写报「期望关键字 'FIRST'」) |
✘(用 LIMIT) |
TOP n |
✔ | — | — |
TOP n PERCENT |
✘ | — | — |
多行 VALUES (…),(…) |
✔ | ✔ | ✔ |
INSERT … SELECT |
✔ | ✘ | ✘ |
INSERT IGNORE / REPLACE / ON DUPLICATE KEY UPDATE |
— | — | ✔(只探测主键) |
| 复合主键 | ✔ | ✔(列须显式 NOT NULL) |
✔ |
UNIQUE / CHECK / DEFAULT |
✔ | ✔ | ✔ |
| 外键 | ✘ | ✘ | ✘ |
| 列默认可空性 | 默认 NOT NULL | 默认可空 | 默认可空 |
CHAR(n) |
✔(单字符,非定长 n) | ✔(同左) | ✔(同左) |
VARCHAR(n) 长度校验 |
✘ | ✘ | ✘ |
DECIMAL(p,s) 小数位校验 |
✔(超位报错,不舍入) | ✘(NUMBER(p,s) 不校验) |
✔ |
CREATE INDEX |
✔ | ✘ | ✘ |
DROP INDEX |
✘ | ✘ | ✘ |
TRUNCATE |
✘ | ✘ | ✔(等价 DELETE + 重置自增) |
ALTER TABLE |
✔ ADD / MODIFY / RENAME | ✔ ADD 列 / DROP COLUMN | ✔ ADD / DROP / MODIFY / CHANGE / RENAME(单子句) |
CREATE OR REPLACE VIEW |
✘ | ✔ | ✘ |
| 触发器 | ✔(受限语法) | ✘ | ✘ |
| 存储过程 | ✔(CALL p()) |
✔(CALL p()) |
✔(CALL p();体内字符串字面量有缺陷) |
| 匿名块 | ✘ | ✔ | ✘ |
| 序列 / 包 / 同义词 | — | ✘ | — |
| 双引号标识符 | ✘(当字符串) | ✔(有同名列时) | ✔(有同名列时,否则当字符串) |
| 反引号标识符 | ✘ | ✘ | ✔ |
| 方括号标识符 | ✔ | ✘ | ✘ |
显式事务 BEGIN / COMMIT / ROLLBACK |
✔(同一进程内) | ✔(同一进程内) | ✔(同一进程内) |
7. 事务模型
- 事务是进程级单事务:一个进程里同时只有一个活动事务,不按表/库分。
- 只有平台管理员能显式
BEGIN/COMMIT/ROLLBACK。 - CLI 每条
sql命令是一个新进程,所以BEGIN和后面的COMMIT不在同一进程里:
$ dbsql --root /data --dialect procedural sql db "COMMIT"
错误:没有活动事务
- 单条语句自身是原子的(失败回滚该语句)。需要跨语句事务时,必须在同一连接/同一进程内完成(HTTP 长连接或过程体)。
8. 内置函数(三方言共享同一张注册表)
sysdate, current_timestamp, now, getdate, rand, coalesce, nvl, ifnull, concat,
length, len, trim, ltrim, rtrim, lower, upper, isnumeric, isdate, abs, floor,
ceiling, ceil, sign, reverse, ascii, space, year, month, day, to_char, substr,
substring, instr, charindex, patindex, left, right, replicate, datepart,
dateadd, datediff, replace, iif, stuff, round, mod
- T-SQL 缺
ISNULL(用COALESCE);GETDATE()/CONVERT()/N'…'可用。 - PL/SQL 缺
DECODE/TO_DATE/GREATEST/LEAST/TO_NUMBER;NVL/SUBSTR/TO_CHAR/SYSDATE可用。 - MySQL 缺
GROUP_CONCAT/DATE_FORMAT/IF()/CURDATE;IFNULL/NOW/UPPER可用。
9. 文档索引
| 文件 | 内容 |
|---|---|
| tsql.md | T-SQL:支持项、类型映射、过程/触发器/SET 白名单、不支持项、与 SQL Server 的差异 |
| plsql.md | PL/SQL:DUAL/匿名块/过程、支持项、不支持项、与 Oracle 的差异 |
| mysql.md | MySQL:反引号/LIMIT/自增/SHOW/过程、支持项、不支持项、与 MySQL 的差异与已知缺陷 |
| verify.sh | 自包含复核脚本:新建临时数据根,逐条打印 SQL 与结果 |
| 验证附录.md | 复核环境、运行方法、关键声明的实测输出 |
10. 已知缺陷(与语法文档分开记)
- MySQL 存储过程体内的字符串字面量被当成列名:
CALL时报列 'v' 的值写错了:表中不存在列 'x';SELECT 'x' AS s报无法计算表达式:x AS s。纯数字/参数的过程体正常。 - MySQL
INSERT IGNORE/REPLACE/ON DUPLICATE KEY UPDATE只探测主键:唯一键冲突不会按 MySQL 语义忽略/替换,而是报唯一约束 'uk' 冲突。 - SHOW CREATE TABLE 查不存在的表返回空骨架(
CREATE TABLE \nope` (\n\n) ENGINE=InnoDB…`),不报错。 - MySQL
SELECT @v把@v当字符串字面量原样返回,不报“不支持”。 - PL/SQL 的
ROWNUM解析器收下但执行不了:投影报SELECT 列 'ROWNUM' 不存在,WHERE报表中不存在列 'ROWNUM'。 - T-SQL
CHAR(n)是单字符类型;VARCHAR(n)不校验长度(迁移时依赖长度的数据要单独核对)。 - MySQL
SHOW CREATE TABLE只回显主键,UNIQUE KEY/ 普通KEY全部丢失(见 mysql.md)。