PLSQL - 一次关于SYS_CONTEXT函数、IR函数与视图的尝试

最近在学习有关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的会话中执行

以上只是我的猜测,还没有找到相关的资料,在文档中也尚未找到进一步的解释,如果哪位大佬有资料或者更合理的见解的话,烦请不吝指教!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值