运行时名称:PL/SQL;CLI
--dialect procedural(旧值oracle仍兼容);需要libdbsql_oracle_plugin.so。 先读 总览 了解方言隔离、事务模型和共享函数表。
1. 调用方式
cd /path/to/dbsql
./target/release/dbsql --root /data --dialect procedural sql mydb "SELECT 1 FROM DUAL"
插件按 plugins/libdbsql_oracle_plugin.so → plugins/dbsql_oracle_plugin.so → 当前目录 的顺序探测;也可以用 --plugin <路径>(必须写在子命令前)。
2. 支持的语法
2.1 查询
SELECT … FROM DUAL与不带FROM的SELECT都可执行:SELECT 1 AS x返回 1。||字符串拼接:SELECT 'a' || 'b' FROM DUAL→ab。- 分页:既支持通用写法,也支持 Oracle 写法:
SELECT id FROM t ORDER BY id LIMIT 2 OFFSET 0;
SELECT id FROM t ORDER BY id FETCH FIRST 2 ROWS ONLY;
SELECT id FROM t ORDER BY id OFFSET 0 ROWS FETCH NEXT 2 ROWS ONLY;
注意:FETCH NEXT 必须跟在 OFFSET 后面;单独写 FETCH NEXT 2 ROWS ONLY 报 期望关键字 'FIRST',实际遇到 'NEXT',此时用 FETCH FIRST。
- 集合运算:
UNION/UNION ALL/INTERSECT,以及 Oracle 的MINUS(解析成集合差,实测可用)。 EXCEPT关键字不识别:SELECT … EXCEPT SELECT …报只支持单条语句;语句结束后仍有多余内容:'SELECT'(位于 25);用MINUS代替。- 非递归 CTE:
WITH x AS (SELECT …) SELECT … FROM x可用。 - JOIN / GROUP BY / HAVING / ORDER BY 序号 /
DISTINCT与另两方言一致。 NULLS FIRST/NULLS LAST可写。
2.2 DDL
| 语句 | 说明 |
|---|---|
CREATE TABLE / DROP TABLE |
约束见 2.3 |
CREATE [OR REPLACE] VIEW … AS SELECT … / DROP VIEW |
OR REPLACE 可用 |
CREATE [OR REPLACE] PROCEDURE <名> () AS BEGIN … END / DROP PROCEDURE |
见 2.6 |
CREATE [OR REPLACE] FUNCTION … / DROP FUNCTION |
— |
ALTER TABLE <表> ADD <列> <类型> |
ADD 后面不带括号 |
ALTER TABLE <表> DROP COLUMN <列> |
— |
CREATE TABLE … (<列> INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, …) |
自增从 1 开始 |
不支持的对象:CREATE INDEX / TRIGGER / SEQUENCE / SYNONYM / PACKAGE(报 CREATE 后不支持的对象类型);TRUNCATE 也不是合法语句。
2.3 类型映射
map_oracle_type(crates/dbsql-sql-oracle/src/parser.rs):
| 写法 | 引擎类型 | 备注 |
|---|---|---|
NUMBER |
decimal |
(p,s) 只解析不生效:NUMBER(5,2) 可存 1234.567 |
DECIMAL / NUMERIC |
decimal |
同 NUMBER |
FLOAT / BINARY_DOUBLE / REAL |
double |
— |
BINARY_FLOAT |
float |
— |
INT / INTEGER / PLS_INTEGER / BINARY_INTEGER |
int |
— |
SMALLINT |
short |
— |
BIGINT / LONG |
long |
— |
VARCHAR2 / VARCHAR / NVARCHAR2 / CLOB / NCLOB / LONG_RAW / XMLTYPE |
string |
长度不校验(VARCHAR2(3) 可存 abcdef) |
CHAR / NCHAR / CHAR2 |
char |
单字符类型 |
DATE / TIMESTAMP |
datetime |
写字符串 '2026-01-02' 可行,读回 2026-01-02 00:00:00 |
BOOLEAN / BOOL |
bool |
— |
STRING / TEXT |
string |
— |
JSON |
json |
写入校验 JSON |
BLOB / RAW / VARBINARY / BINARY |
blob |
十六进制字面量 |
2.4 约束
- 单列主键:
id INT PRIMARY KEY可直接建。 - 复合主键:每个主键列必须显式
NOT NULL,否则报列 'a' 声明为 NULL,不能做复合主键的一部分;请改成 NOT NULL。 UNIQUE、CHECK真校验;DEFAULT只接受字面量。- 普通列默认可空(不写
NOT NULL时,省略值可插入)。 - 外键不支持,报错见 总览第 5 节。
2.5 写入
INSERT INTO t (id, nm) VALUES (1, 'a'); -- 字面量
INSERT INTO t (id, nm) VALUES (1+3, 'c'); -- 算术表达式可用
INSERT INTO t (id, nm) VALUES (2, NULL); -- NULL 可用
INSERT INTO t (id, nm) VALUES (20, 'x'), (21, 'y'); -- 多行可用
UPDATE t SET nm = 'z' WHERE id = 1; -- 右侧只写值
DELETE FROM t WHERE id = 1;
UPDATE的SET右侧必须是字面量:SET nm = nm报期望字面量,实际遇到 'nm';WHERE里可以用子查询(WHERE id IN (SELECT …)实测可用)。INSERT … VALUES不接受函数调用:VALUES (2, UPPER('a'))报期望字面量,实际遇到 'UPPER'。INSERT … SELECT不支持:期望关键字 'VALUES',实际遇到 'SELECT'。
2.6 匿名块与存储过程
-- 匿名块:可执行
BEGIN NULL; END;
-- 带声明
DECLARE
n INT;
BEGIN
n := 1;
END;
-- 存储过程
CREATE OR REPLACE PROCEDURE pz ()
AS
BEGIN
INSERT INTO pt (id, nm) VALUES (10, 'p');
END;
CALL pz();
- 过程名后带空参数括号;用
CALL p()调用。 - 过程体内的字符串字面量正常工作(实测插入
'ok'成功)。 EXCEPTION块不支持:无法识别的 PL/SQL 语句:EXCEPTION WHEN OTHERS THEN NULL。- 一次只能提交一条顶层语句;多条会报
只支持单条语句;语句结束后仍有多余内容:'SELECT'。
2.7 字面量
| 写法 | 行为 |
|---|---|
''(空串) |
不是 NULL:WHERE nm = '' 能匹配空串,IS NULL 不匹配 |
q'[it''s]' |
不做 Oracle q 引用处理,返回字面文本 it''s |
"ID" |
有同名列时按标识符去引号;FROM DUAL 后不能起别名 |
DATE '2026-01-02' |
不支持(缺少 VALUES 行右括号);直接写字符串 '2026-01-02' |
2.8 事务
COMMIT / ROLLBACK 只能在活动事务内执行;CLI 单独执行报 错误:没有活动事务。事务模型见 总览第 7 节。
3. 不支持的功能
| 功能 | 实测报错 / 表现 | 替代 |
|---|---|---|
ROWNUM |
投影:SELECT 列 'ROWNUM' 不存在;过滤:表中不存在列 'ROWNUM' |
LIMIT / FETCH FIRST |
ROW_NUMBER() OVER (…) |
只支持单条语句;语句结束后仍有多余内容:'LParen' |
应用层编号 |
| 递归 CTE | 数据表 'r' 不存在(递归名被当表名) |
迭代 SQL / 过程 |
INSERT … SELECT |
期望关键字 'VALUES',实际遇到 'SELECT' |
逐行 VALUES 或应用层导入 |
INSERT 里用函数 |
期望字面量,实际遇到 'UPPER' |
先算好值再写入 |
UPDATE 右侧引用列/表达式 |
期望字面量,实际遇到 'nm' |
拆成多条 UPDATE / 应用层计算 |
EXCEPTION 块 |
无法识别的 PL/SQL 语句:EXCEPTION … |
过程体外部判断 |
CREATE INDEX / TRIGGER / SEQUENCE / SYNONYM / PACKAGE |
CREATE 后不支持的对象类型 |
不建;触发器改由 T-SQL 方言建 |
DROP INDEX 等 |
不支持 | — |
TRUNCATE TABLE |
不支持或无法识别的 Oracle 语句(位于 0):'TRUNCATE' |
DELETE FROM t |
MERGE |
不支持或无法识别的 Oracle 语句(位于 0):'MERGE' |
UPDATE + INSERT |
CONNECT BY |
只支持单条语句;语句结束后仍有多余内容:'BY' |
递归 CTE 不可用,改应用层展开 |
SELECT … FOR UPDATE |
只支持单条语句;语句结束后仍有多余内容:'UPDATE' |
无行锁语法;依赖引擎事务 |
CROSS JOIN |
SELECT 语句末尾存在多余内容 |
逗号连接 |
DECODE / TO_DATE / GREATEST / LEAST / TO_NUMBER |
未定义的函数 'DECODE' / 'to_date' |
CASE / 先格式化字符串 / CASE 比较 |
| 顶层多条语句 | 只支持单条语句… |
一次一条;匿名块内可分多条 |
| 无列的聚合 | 无聚合的 SELECT 项必须有列引用(SELECT COUNT(*) 与 SELECT COUNT(*) FROM DUAL 都报) |
SELECT COUNT(*) FROM 实表 |
DUAL 别名 |
只支持单条语句;语句结束后仍有多余内容:'d' |
不写别名 |
| 外键 | 见总览 | NOT NULL + 应用层校验 |
4. 与 Oracle 有差异的地方
| 主题 | 本引擎(PL/SQL 方言) | Oracle |
|---|---|---|
ROWNUM |
语法上被识别,但执行不了(见上表) | 核心分页伪列 |
| 空串 | '' 是普通空字符串,不等于 NULL |
'' 即 NULL |
| NULL 排序 | ORDER BY x ASC 时 NULL 在前 |
默认 ASC 时 NULL 在后(NULLS LAST) |
NUMBER(p,s) |
精度/小数位不生效:NUMBER(5,2) 可存 1234.567 |
严格按 p/s |
VARCHAR2(n) |
长度不校验 | 严格按 n(默认字节语义) |
CHAR(n) |
单字符类型,CHAR(3) 也只能存一个字符 |
定长 n,短补空格 |
DATE 字面量 |
DATE '2026-01-02' 不支持;写字符串 |
支持 ANSI DATE 字面量 |
q'[…]' |
原样保留引号内容,不做转义解释 | 去引号、内部 '' ← ' |
| 复合主键 | 每个主键列必须显式 NOT NULL |
主键隐含 NOT NULL |
INSERT … VALUES |
只接受字面量 / 算术 / NULL,不接受函数 | 任意表达式 |
INSERT … SELECT |
不支持 | 支持 |
UPDATE … SET |
右侧只能是字面量 | 任意表达式、列引用、子查询 |
| 存储过程体 | 体内可用字符串字面量;控制流由标准过程体解释器执行 | 完整 PL/SQL(游标、异常、包) |
| 异常处理 | EXCEPTION 不支持 |
完整支持 |
| 索引/触发器/序列/同义词/包 | 不支持 | 支持 |
DUAL |
可省略 FROM DUAL;不能给 DUAL 起别名 |
可起别名 |
| 一次多条语句 | 只允许一条(匿名块内可分多条) | 脚本可多条 |
| 事务 | 进程级单事务,跨命令丢失 | 会话级 |
| 引号标识符 | "ID" 可按标识符解析(有同名列时) |
大小写敏感标识符 |
| 与 Oracle 一致的部分 | ` |
5. 迁移建议
- 所有
ROWNUM分页改LIMIT/FETCH FIRST;ROWNUM = 1改LIMIT 1。 - 把
''当 NULL 的判断改成显式NULL(本引擎里两者不同)。 INSERT … SELECT拆成逐行VALUES(或走导入工具);INSERT/UPDATE里的函数先算好。DECODE改CASE;TO_DATE改直接写'YYYY-MM-DD'字符串。NUMBER(p,s)/VARCHAR2(n)的精度与长度在应用层补校验。- 复合主键列逐个补
NOT NULL。 - 触发器、索引、序列、包不在本方言落地;需要触发器时改用 T-SQL 方言建(但语法不同,见 tsql.md)。
EXCEPTION块删除或改到调用方处理。- 匿名块可以保留,但不要依赖一次提交整个脚本。
6. 复现
cd /path/to/dbsql
./target/release/dbsql --root /tmp/dbsql-dialect-verify create database pdb
./target/release/dbsql --root /tmp/dbsql-dialect-verify --dialect procedural sql pdb \
"SELECT q'[it''s]' AS s FROM DUAL"
# s = it''s
./target/release/dbsql --root /tmp/dbsql-dialect-verify --dialect procedural sql pdb \
"SELECT ROWNUM, id FROM pt"
# 错误:SELECT 列 'ROWNUM' 不存在
完整清单:verify.sh。