四海内皆兄弟

ora-38824这个错误我都懵了

     开发人员遇到这个错误问我怎么回事?我也没见过。
ORA-38824: A CREATE OR REPLACE command
cannot change the EDITIONABLE property
of the following object: 'P_BIZ'.
顺着报错去查 DBA_OBJECTS,一个不起眼的列露了出来:EDITIONABLE = N。
一个 N,凭什么让一句最普通的建过程语句直接失败?它又是怎么跑到你的库里来的?
这篇不复述报错文档,我们把整个过程在 Oracle 26ai 上真跑了一遍,还顺手推翻了一条被到处转载的「标准答案」。
Oracle AI Database 26ai · 23.26.1
PDB p1 / 用户 XXG / EDITIONS_ENABLED = N
expdp → impdp 真实导入复现,非纸上推演
PART 01
这个 N,是对象自己的属性
EDITIONABLE 是对象级属性。它来自 11.2 引入的 EBR(基于版本的重定义):标记这个对象是否参与「版本化」。Y 是可版本化、只在某个 edition 可见;N 是非版本化、对所有 edition 可见;表、索引、LOB 这类本身不支持版本化的类型则是 NULL。
换句话说,同一个库、同一个用户下,有的对象 Y、有的 N,完全正常——这个属性是跟着每一个对象走的。
实测:同一个 XXG 里就混着
PROCEDURE 全 Y · FUNCTION 全 Y · TRIGGER 全 Y
VIEW 却 N=62 / Y=13
所以「为什么偏偏这个存储过程是 N」这个问题,答案不在库配置,只落在一处:这个对象当初是怎么被创建出来的。
PART 02
一次数据泵导入,把关键字写了进来
绝大多数「一夜之间全库对象变 N」的案例,源头都是 expdp / impdp。我们在服务器上真跑了一轮:建一 Y 一 N 两个过程 → 导出 → 删掉 → 导回,属性被原样保留;再跑一句普通升级脚本,立刻复现 ORA-38824。
为什么能保留?用 impdp SQLFILE= 把导入实际执行的 DDL 导出来看,一眼见底:
-- SCHEMA_EXPORT/PROCEDURE/PROCEDURE
CREATE EDITIONABLE
  procedure DP_Y as begin null; end;
CREATE NONEDITIONABLE
  procedure DP_N as begin null; end;
关键字是 DDL 文本的一部分,impdp 照抄重放。不是导入后某个参数把它改成了 N,是 CREATE 语句里就明明白白写着 NONEDITIONABLE。
11g 里根本没有这个属性概念,从 11g 的 dump 导进 12c+ 时,Data Pump 按兼容策略把这类代码对象建成 NONEDITIONABLE,于是整批落地成 N。更麻烦的是它会自我延续:源端本来就是 N,再导一次还是 N。
除了导入,N 还有两个次要来源:
▸ 发布脚本里显式写了 NONEDITIONABLE,或工具导出 DDL 时把属性一起带出来
▸ 升级 / 迁移过程异常,个别 schema 新建对象默认就是 N(社区有 12.2 疑似 Bug 27314007 的记录)
顺带纠正一个常被记反的点:官方文档写明,11.2 升级到 12.1 时用户自建对象是获得 EDITIONABLE(Y),PUBLIC SYNONYM 才是 N。所以「升级把过程变 N」不成立,真正批量变 N 的是导入。
PART 03
报错逻辑本身很简单:CREATE OR REPLACE 不允许改变一个已存在对象的 EDITIONABLE 属性。而且规则是双向的——把 Y 强行写成 NONEDITIONABLE 一样报错。
对一个 N 对象,三种 REPLACE 写法实测
✕ 显式 EDITIONABLE → ORA-38824
✕ 省略关键字(普通升级脚本)→ ORA-38824
✓ 显式 NONEDITIONABLE → 成功,属性仍 N
在 26ai 23.26.1 上,这条不成立。省略关键字会被解析成 schema 默认的 EDITIONABLE,对一个 N 对象照样报 ORA-38824。
为了排除会话状态干扰,我们换了全新会话重跑,又在 EDITIONS_ENABLED=Y 的用户下复验,三次结论一致。官方文档也写明:新建对象省略关键字时,默认就是 EDITIONABLE。
另一个反直觉:报错是原子的,源码一个字没动
报错后查 USER_SOURCE,过程体还是旧版本,STATUS 仍是 VALID。对象没被删、没失效、没被改,只是这次发布没生效。别误以为「编译坏了」而去盲目重编译。
PART 04
怎么解决:一条 ALTER,一次性收口
绝大多数业务账号都是 EDITIONS_ENABLED = N,这正是能直接改属性的前提。主推方案不用改任何脚本、不用停机、不丢源码和权限:
方案 A(主推)· ALTER 一次,永久恢复
把对象属性一次性改成 Y,之后所有传统 CREATE OR REPLACE 立刻恢复正常。实测改完 E=Y / VALID,紧接一句普通升级脚本就成功。
ALTER PROCEDURE P_BIZ EDITIONABLE;
-- 复核:SELECT editionable FROM
-- user_objects WHERE object_name='P_BIZ';
批量修复就用一句 SQL 拼出所有 ALTER,过一遍再执行。两个必须记住的边界:
▸ 排除 DM$* 等系统自动生成对象,它们保持 N 是正常设计,别去「修复」
▸ PACKAGE BODY / TYPE BODY 不能单独改,会继承 spec 的属性
如果用户被 ENABLE EDITIONS,ALTER 会被 ORA-38825 挡住,此时唯一通用解是 DROP + 重建(代价是要补授权)。决策依据就一条:先查 dba_users.editions_enabled 再动手。
还有个被到处推荐的「修复」是 ALTER USER ... ENABLE EDITIONS——方向是反的:它只会把上面这条 ALTER 的路堵死,还可能直接报 ORA-38813。别用它处理 ORA-38824。
回头看,这事儿技术上并不复杂:属性是对象级的、导入会原样带过来、CREATE OR REPLACE 不许改属性。真正让人绕远的,是「看起来很对」的结论和没查就照抄的动作。
迁移完统一跑一次批量 ALTER 把属性收敛到 Y、把 editionable='N' 纳入上线前检查、导入前用 SQLFILE 预览 DDL——这三件都是十分钟的活,比事后排 ORA-38824 便宜得多。
    工作这么多年依然能遇到没接触过的,时刻保持敬畏吧。
Image