ORA-01031因AUTHID DEFINER下角色权限不生效,需显式授CREATE TABLE权限或改用AUTHID CURRENT_USER;JDBC/cx_Oracle需SET ROLE ALL激活角色。

存储过程里 EXECUTE IMMEDIATE 'CREATE TABLE' 为什么报 ORA-01031
因为 Oracle 默认用的是 AUTHID DEFINER(定义者权限),此时所有权限检查发生在编译时,且只认「直接授予」的系统权限,完全忽略角色(如 DBA、RESOURCE)里的权限。哪怕你查 SESSION_PRIVS 看到 CREATE TABLE,在存储过程里它也无效。
常见现象包括:
- SQL*Plus 或匿名块中执行
EXECUTE IMMEDIATE 'CREATE TABLE t1(id NUMBER)'成功,但一包进存储过程就报ORA-01031 - 用户已
GRANT DBA TO scott,SELECT * FROM SESSION_ROLES显示有DBA,但过程仍失败 - 错误堆栈指向
EXECUTE IMMEDIATE那行,而不是语法或对象名问题
两种可行解法:直授权限 or 改用调用者权限
必须二选一,不能混用;选哪个取决于你的安全策略和调用场景。
方案一:显式授予 DDL 权限(推荐用于固定操作者)
- 用
SYS或具备ADMIN OPTION的用户执行:GRANT CREATE ANY TABLE TO your_user - 如果只建自己 schema 下的表,
GRANT CREATE TABLE TO your_user就够了 - 别漏配套权限:
CREATE SESSION和表空间配额(如UNLIMITED TABLESPACE或ALTER USER your_user QUOTA 100M ON users)
方案二:加 AUTHID CURRENT_USER(适合多用户共用过程)
- 重写过程头:
CREATE OR REPLACE PROCEDURE my_proc AUTHID CURRENT_USER IS ... - 此时权限检查推迟到运行时,且会读取调用者的
SESSION_PRIVS——所以调用者必须已启用对应角色(比如 JDBC 连接后手动SET ROLE ALL) - 嵌套调用时,每一层涉及 DDL 的过程都得加这个子句,否则权限链断裂
JDBC / cx_Oracle 连接后角色不生效是高频坑
Java 或 Python 应用连 Oracle,默认 SESSION_ROLES 是空的,哪怕你 GRANT DBA TO app_user 也没用。角色不会自动激活。
- 连接成功后,必须立刻执行一条
SET ROLE ALL(无密码角色)或SET ROLE role_name IDENTIFIED BY 'pwd' - ojdbc8+ 一般支持,但老版本 ojdbc6 可能需在代码里显式提交该语句,不能只靠连接字符串
- 用
cx_Oracle时,要在connection.cursor().execute("SET ROLE ALL"),且必须在调用存储过程前完成 - 验证是否生效:
SELECT * FROM SESSION_ROLES应返回非空结果
CREATE GLOBAL TEMPORARY TABLE 也一样受限
临时表不是例外。只要用 EXECUTE IMMEDIATE 建,就受同样权限模型约束。很多人以为“临时”=“宽松”,其实 Oracle 对 CREATE GLOBAL TEMPORARY TABLE 的权限要求和普通表完全一致。
如果你的过程里混合了 SELECT、INSERT 和 CREATE GLOBAL TEMPORARY TABLE,前两者可能因对象权限或角色生效而成功,最后一句失败——这恰恰说明问题出在 DDL 权限隔离机制上,不是语法或拼写问题。
最易被忽略的一点:权限必须在「过程创建时」就存在。改完权限后,不要只 EXEC 存储过程,要先 CREATE OR REPLACE 一遍,让 Oracle 重新做编译期权限校验。


















