最近在学习有关AUTHID的内容时,发现Oracle多份官方文档中强调的一项比较有趣的规定
Oracle Database PL/SQL Language Reference 11g Release 2, P8-49
Oracle Database SQL Language Reference 11g Release 2, P5-282
这个规定的中心思想就是:1)如果在视图中调用了IR权限的存储过程,上下文CURRENT_USER总是视图的定义者,而非调用者;2)如果视图中直接调用了SYS_CONTEXT函数,其返回的上下文CURRENT_USER总是视图的调用者,而非定义者
这个说法里面,第一条很容易理解:视图总是DR权限的,所以当视图被调用的时候,CURRENT_USER会切换到其定义者,进而再调用IR权限的存储过程的时候,CURRENT_USER仍保留为视图的定义者
但是第二条就与第一条的解释冲突了,既然视图总是DR权限的,那怎么视图里直接调用SYS_CONTEXT返回的CURRENT_USER却是视图的调用者呢?
关于这个情况,文档里并没有做更多解释(也许是我没查阅到),于是我做了一个测试:
- 程序准备
APPS用户下准备一张日志表
SQL> DESC demo_tmp_log
Name Type Nullable Default Comments
------------- ------------ -------- ------- --------
LOG_ID NUMBER Y
PRGNAME VARCHAR2(30) Y
ARG1 VARCHAR2(30) Y
CREATION_DATE DATE Y
APPS用户下创建一个LOGGER过程
CREATE OR REPLACE PROCEDURE demo_logger(p_prgname VARCHAR2
,p_argument VARCHAR2) IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO demo_tmp_log
(log_id, prgname, arg1, creation_date)
VALUES
(demo_logger_s.nextval, p_prgname, p_argument, SYSDATE);
COMMIT;
END;
APPS用户下创建两个函数,分别是IR权限或DR权限的
CREATE OR REPLACE FUNCTION demo_ir_test RETURN VARCHAR2 AUTHID CURRENT_USER IS
BEGIN
--插入日志
demo_logger('IR Unit', sys_context('USERENV', 'CURRENT_USER'));
--返回CURRENT_USER
RETURN sys_context('USERENV', 'CURRENT_USER');
END;
/
CREATE OR REPLACE FUNCTION demo_dr_test RETURN VARCHAR2 AUTHID DEFINER IS
BEGIN
--插入日志
demo_logger('DR Unit', sys_context('USERENV', 'CURRENT_USER'));
--返回CURRENT_USER
RETURN sys_context('USERENV', 'CURRENT_USER');
END;
APPS用户下创建一个视图,调用上述函数和SYS_CONTEXT
CREATE OR REPLACE VIEW DEMO_IR_DR_V AS
SELECT demo_ir_test ir
,demo_dr_test dr
,sys_context('USERENV', 'CURRENT_USER') curr_user
FROM dual;
GRANT SELECT ON DEMO_IR_DR_V TO CUX WITH GRANT OPTION;
GRANT EXECUTE ON DEMO_IR_TEST TO CUX;
GRANT EXECUTE ON DEMO_DR_TEST TO CUX;
CUX用户下创建一个视图,查询上述视图,并调用上述函数
CREATE OR REPLACE VIEW CUX_IR_DR_V AS
SELECT v.ir
,v.dr
,apps.demo_ir_test ir2
,apps.demo_dr_test dr2
,v.curr_user
FROM apps.demo_ir_dr_v v;
GRANT SELECT ON CUX_IR_DR_V TO SOAU;
- 测试结果
SOAU用户查询CUX视图结果
SQL> SELECT * FROM cux.cux_ir_dr_v;
IR DR IR2 DR2 CURR_USER
------ ------ ------ ------ ----------
APPS APPS CUX APPS SOAU
APPS用户查看日志表情况
SQL> SELECT * FROM demo_tmp_log;
LOG_ID PRGNAME ARG1 CREATION_DATE
---------- ------------ ------------ --------------------
1 IR Unit APPS 2020/10/16 16:02:53
2 DR Unit APPS 2020/10/16 16:02:53
3 IR Unit CUX 2020/10/16 16:02:53
4 DR Unit APPS 2020/10/16 16:02:53
- 结果分析
日志表数据已经证明了在SOAU用户查询日志时,上下文CURRENT_USER的切换过程如下图
从视图查询结果可以看出,APPS.DEMO_IR_DR_V里调用的两个函数,只在该视图里执行过,回到CUX.CUX_IR_DR_V视图时不再执行;同样的,CUX.CUX_IR_DR_V视图里调用的两个函数,也只在这个视图里执行过,不在回到SOAU执行第二遍
而APPS.DEMO_IR_DR_V里的SYS_CONTEXT突破层层调用,返回了最终的视图调用者SOAU
文档中对于SYS_CONTEXT函数在SQL语句中的表现特别指出了一点:
Oracle Database SQL Language Reference 11g Release 2, P5-279
在SQL语句中直接调用的SYS_CONTEXT函数,无论出现多少次,也无论实际返回了多少行,SYS_CONTEXT只会统一执行一次
所以结合测试结果,我猜测既然只执行一次,那么就要放到最后执行,以保持上下文的一致
加之普通视图在本质上是存储SQL语句的信息而非实际的数据,所以在层层嵌套中,APPS.DEMO_IR_DR_V最先执行完两个存储过程,并把函数结果返回给CUX.CUX_IR_DR_V,同样的CUX.CUX_IR_DR_V又执行了存储过程,也只把结果返回给调用者SOAU,而嵌套最内层的SYS_CONTEXT被保留直到最后,在SOAU的会话中执行
以上只是我的猜测,还没有找到相关的资料,在文档中也尚未找到进一步的解释,如果哪位大佬有资料或者更合理的见解的话,烦请不吝指教!