在ADG备库上,SELECT ANY TABLE权限无效且会触发ORA-01031,必须在备库本地对每个表显式授予SELECT权限,如GRANT SELECT ON CMSPROD.PORTAL_USER_PROFILE TO cmsreadonly。

直接 GRANT SELECT ANY TABLE 是错的,而且在 ADG 备库上根本跑不通——它会报 ORA-01031,不是权限没刷,是 Oracle 内核硬拦。
SELECT ANY TABLE 在 Oracle 19c 中根本不存在
你执行 GRANT READ ANY TABLE TO user 会立刻报 ORA-01931: cannot grant read any table;执行 GRANT SELECT ANY TABLE TO user 虽然语法通过,但在 ADG 物理备库(READ ONLY WITH APPLY)上后续任何查询都会触发 ORA-01031: insufficient privileges。这不是缓存或延迟问题,是 Oracle 明确禁止该权限在只读实例中参与校验——因为它的权限检查逻辑会隐式访问 obj$、tab$ 等字典表,而备库不允许任何字典写(哪怕只是读校验路径中的临时 latch)。
-
SELECT ANY DICTIONARY同样无效,备库不开放“任意读字典”通道 - 主库授的
SELECT ANY TABLE不会同步到备库生效,备库权限必须本地显式授予 - 别信“先在主库授、等同步后就能用”的说法,这是常见误判
必须在备库上逐对象显式授权
真正能落地的只读方案,只有一条路:在备库实例内,对每个目标表明确执行 GRANT SELECT ON schema.table TO user 或角色。schema 名必须大写(除非建表时加引号),且该 schema 必须已同步存在。
- 先确认表存在:
SELECT owner, table_name FROM dba_tables WHERE owner = 'CMSPROD' AND table_name = 'PORTAL_USER_PROFILE' - 授权语句不能省略 schema:
GRANT SELECT ON CMSPROD.PORTAL_USER_PROFILE TO cmsreadonly(写成PORTAL_USER_PROFILE会报ORA-00942) - 批量生成脚本必须在备库执行(确保
dba_tables视图反映的是备库实际对象):SELECT 'GRANT SELECT ON CMSPROD.' || table_name || ' TO cmsreadonly;' FROM dba_tables WHERE owner = 'CMSPROD'
用角色封装比直接授用户更可控
把所有 GRANT SELECT ON ... TO role 集中到一个角色里,再把角色赋给用户。这样增删表权限只需改角色,不碰用户本身,也避免漏表。
- 角色必须在目标 PDB 内创建(多租户环境下,CDB 中建的角色不会自动继承到 PDB):
ALTER SESSION SET CONTAINER = ORA19CPDB后再CREATE ROLE app_readonly - 角色不带登录能力,
CREATE SESSION必须单独授予用户:GRANT CREATE SESSION TO cmsreadonly - 生产环境应锁死账号:
ALTER USER cmsreadonly ACCOUNT LOCK,防止被用于 SQL*Plus 手动探测
同义词、视图、v$ 动态性能视图要单独处理
对象级 SELECT 权限不自动传导到同义词或视图。如果应用查的是 vw_active_users 或 my_emp(同义词),你得对它们指向的原对象授权,而不是对同义词名本身。
- 查
v$session?别直接授SELECT_CATALOG_ROLE(权限过大),改用封装视图:CREATE VIEW v_session_ro AS SELECT sid, username, program FROM v$session,再GRANT SELECT ON v_session_ro TO app_readonly - 同义词本身无权限属性,必须对底层表/视图授
SELECT - 物化视图、自定义函数返回的表(如 pipelined function)也需单独授权其结果集对象
最易被忽略的点:权限脚本必须在备库上执行,且必须验证目标 schema 是否已同步(比如 CMSPROD 用户在主库存在,但未启用用户传输或未在备库手动创建,那 dba_tables 就查不到它的表)。


















