/ 三方言语法手册 / PL/SQL
DbSql · 文档

PL/SQL 方言

每条支持 / 不支持 / 与厂商数据库的差异,都由 verify.sh 在真实二进制上跑过。

运行时名称: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 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。

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 约束

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;

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();

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. 迁移建议

  1. 所有 ROWNUM 分页改 LIMIT / FETCH FIRST;ROWNUM = 1 改 LIMIT 1。
  2. 把 '' 当 NULL 的判断改成显式 NULL(本引擎里两者不同)。
  3. INSERT … SELECT 拆成逐行 VALUES(或走导入工具);INSERT/UPDATE 里的函数先算好。
  4. DECODE 改 CASE;TO_DATE 改直接写 'YYYY-MM-DD' 字符串。
  5. NUMBER(p,s) / VARCHAR2(n) 的精度与长度在应用层补校验。
  6. 复合主键列逐个补 NOT NULL。
  7. 触发器、索引、序列、包不在本方言落地;需要触发器时改用 T-SQL 方言建(但语法不同,见 tsql.md)。
  8. EXCEPTION 块删除或改到调用方处理。
  9. 匿名块可以保留,但不要依赖一次提交整个脚本。

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。