在CDB$ROOT中创建公共角色必须使用C##前缀且CONTAINER=ALL(或省略),PDB中创建本地角色禁止C##前缀;授角色时CONTAINER子句决定生效范围;查CDB_ROLES的COMMON和CON_ID列区分角色类型。
在CDB$ROOT中创建公共角色必须带C##前缀
不加前缀直接 create role myrole 会报 ora-65096: invalid common user or role name。这是硬性限制,由参数 common_user_prefix 控制(默认值为 c##)。即使你改了这个参数,oracle 也不建议绕过前缀要求。
正确写法只有两种:
-
CREATE ROLE c##admin_role CONTAINER = ALL;—— 显式声明作用域,语义清晰 -
CREATE ROLE c##admin_role;—— 省略CONTAINER子句,但效果等同于CONTAINER = ALL
注意:CONTAINER = CURRENT 在CDB$ROOT下对公共角色无效,会触发 ORA-65094 错误。
在PDB中创建本地角色不能用C##前缀
一旦连接到某个PDB(比如 CONN system@pdb1),再执行 CREATE ROLE c##pdb_role 就会失败,报错仍是 ORA-65096。因为PDB只允许本地角色,而本地角色命名规则禁止以 C## 开头。
本地角色的创建和使用完全类比Non-CDB:
-
CREATE ROLE hr_manager;—— 不加前缀,不写CONTAINER子句(默认即CURRENT) -
GRANT SELECT, INSERT ON hr.employees TO hr_manager;—— 权限只能是本地权限,不能含跨容器操作 - 该角色只存在于当前PDB的
DBA_ROLES和CDB_ROLES中,CON_ID固定为当前PDB的ID
不同PDB可以有同名本地角色(如 hr_manager 在 pdb1 和 pdb2 中各自独立存在),互不影响。
给用户授角色时CONTAINER子句决定权限生效范围
角色本身是公共还是本地,和“把角色授予谁”以及“怎么授予”是两件事。关键看 GRANT ... TO ... CONTAINER = ? 的写法:
-
GRANT c##admin_role TO c##cdb_admin CONTAINER = ALL;→ 公共角色被以公共方式授予,c##cdb_admin在所有PDB中都能激活该角色 -
GRANT hr_manager TO pdb1_user CONTAINER = CURRENT;→ 本地角色只能本地授予,且仅在当前PDB生效 -
GRANT CONNECT TO c##cdb_admin CONTAINER = CURRENT;→ 即使是公共用户,若用CURRENT授予,权限也只在当前容器(比如CDB$ROOT)起作用,进PDB仍会报ORA-01045(缺少CREATE SESSION)
最容易踩的坑:以为公共用户天然拥有所有PDB里的权限。其实权限必须显式授予,且 CONTAINER 子句必须匹配预期作用域。
查询角色类型和作用域要查CDB_*视图,别只看DBA_*
DBA_ROLES 只反映当前容器的角色信息,无法区分公共/本地属性。真正判断依据是 CDB_ROLES 中的 COMMON 列和 CON_ID:
SELECT role, common, con_id FROM cdb_roles WHERE role IN ('C##ADMIN_ROLE', 'HR_MANAGER');- 若某角色在多行出现(
CON_ID为1、3、4…),且COMMON = 'YES'→ 公共角色 - 若某角色只在一行出现(
CON_ID为3),且COMMON = 'NO'→ 本地角色
漏查 CDB_ROLES 是定位权限失效原因最常见的盲区——比如发现角色“存在”,却不知道它根本没同步到目标PDB。











