Oracle VPD'Set_Context'不起作用

时间:2013-11-21 07:10:29

标签: sql oracle database-administration

我写了一个VPD,其中有管理员用户,如JAdmin,PAdmin,属于company_ID = 90,FAdmin,KAdmin属于Company_ID = 91.员工表中还有其他员工属于这两家公司。还有其他表格包含来自两家公司的信息,例如Timesheet,Payroll_Period等。还有一个名为Company_Administrators的表格,其中包含管理员列表及其Company_ID。目标是使用Set_Context在登录时获取管理员的company_ID,并使用它来显示来自他们公司的信息。代码:

CREATE USER JAdmin IDENTIFIED BY JAdmin
DEFAULT TABLESPACE IA643_TBS
TEMPORARY TABLESPACE TEMP
ACCOUNT UNLOCK;

CREATE USER FAdmin IDENTIFIED BY FAdmin
DEFAULT TABLESPACE IA643_TBS
TEMPORARY TABLESPACE TEMP
ACCOUNT UNLOCK;

CREATE USER PAdmin IDENTIFIED BY PAdmin
DEFAULT TABLESPACE IA643_TBS
TEMPORARY TABLESPACE TEMP
ACCOUNT UNLOCK;

CREATE USER KAdmin IDENTIFIED BY KAdmin
DEFAULT TABLESPACE IA643_TBS
TEMPORARY TABLESPACE TEMP
ACCOUNT UNLOCK;

GRANT CONNECT, RESOURCE TO JAdmin;
GRANT CONNECT, RESOURCE TO FAdmin;
GRANT CONNECT, RESOURCE TO PAdmin;
GRANT CONNECT, RESOURCE TO KAdmin;

CREATE OR REPLACE PUBLIC SYNONYM COMPANY 
FOR DBA643.COMPANY;
GRANT INSERT, SELECT, UPDATE, DELETE ON COMPANY 
TO FAdmin, JAdmin, PAdmin, KAdmin;

CREATE OR REPLACE PUBLIC SYNONYM EMPLOYEE 
FOR DBA643.EMPLOYEE;
GRANT INSERT, SELECT, UPDATE, DELETE ON EMPLOYEE 
TO FAdmin, JAdmin, PAdmin, KAdmin;

CREATE OR REPLACE PUBLIC SYNONYM TIMESHEET 
FOR DBA643.TIMESHEET;
GRANT INSERT, SELECT, UPDATE, DELETE ON TIMESHEET 
TO FAdmin, JAdmin, PAdmin, KAdmin;

CREATE OR REPLACE PUBLIC SYNONYM PAYROLL_PERIOD 
FOR DBA643.PAYROLL_PERIOD;
GRANT INSERT, SELECT, UPDATE, DELETE ON PAYROLL_PERIOD 
TO FAdmin, JAdmin, PAdmin, KAdmin;

CREATE OR REPLACE PUBLIC SYNONYM DAILY_WORK_HOURS 
FOR DBA643.DAILY_WORK_HOURS;
GRANT INSERT, SELECT, UPDATE, DELETE ON DAILY_WORK_HOURS 
TO FAdmin, JAdmin, PAdmin, KAdmin;


Conn sys as sysdba

CREATE USER sysadmin_ctx IDENTIFIED BY secAdmin;
GRANT CREATE SESSION, CREATE ANY CONTEXT, CREATE PROCEDURE, CREATE TRIGGER, ADMINISTER
DATABASE TRIGGER TO sysadmin_ctx IDENTIFIED BY secAdmin;
GRANT EXECUTE ON DBMS_SESSION TO sysadmin_ctx;
GRANT EXECUTE ON DBMS_RLS TO sysadmin_ctx;
GRANT RESOURCE TO sysadmin_ctx;

GRANT SELECT ON Company_Administrators TO sysadmin_ctx; 


CREATE OR REPLACE CONTEXT Company_Admin USING PKG_Comp_Admin;
CREATE OR REPLACE PACKAGE PKG_Comp_Admin IS
PROCEDURE Get_Company_ID;
END;
/
CREATE OR REPLACE PACKAGE BODY PKG_Comp_Admin IS
PROCEDURE Get_Company_ID IS
V_Company_ID NUMBER;
BEGIN
    SELECT Company_ID
    INTO V_Company_ID
    FROM DBA643.Company_Administrators
    WHERE System_Username = SYS_CONTEXT('USERENV', 'SESSION_USER');
    DBMS_SESSION.SET_CONTEXT('COMPANY_ADMIN', 'CompanyID', 'V_Company_ID');
    EXCEPTION
    WHEN NO_DATA_FOUND THEN NULL;
    END;
END;
/
SHOW ERROR;



CREATE OR REPLACE FUNCTION Company_Admin_fun (P_schema_name IN varchar2, 
P_object_name IN varchar2) RETURN varchar2 IS
V_where varchar2(300);
BEGIN
    IF User = 'DBA643' then
    V_where := '';
ELSE
    V_where := 'Company_ID = '||NVL(SYS_CONTEXT('Company_Admin', 'CompanyID'),0);
    END IF;
RETURN V_where;
END;
/


EXEC DBMS_RLS.DROP_Policy ('DBA643','COMPANY','COMPANY_POLICY');
EXEC DBMS_RLS.DROP_Policy ('DBA643','EMPLOYEE','EMPLOYEE_POLICY');
EXEC DBMS_RLS.DROP_Policy ('DBA643','TIMESHEET','TIMESHEET_POLICY');
EXEC DBMS_RLS.DROP_Policy ('DBA643','DAILY_WORK_HOURS','DAILY_WORK_HOURS_POLICY');


EXEC DBMS_RLS.ADD_Policy ('DBA643','COMPANY','COMPANY_POLICY','sysadmin_ctx','Company_Admin_fun','SELECT, UPDATE, DELETE, INSERT', TRUE);
EXEC DBMS_RLS.ADD_Policy ('DBA643','EMPLOYEE','EMPLOYEE_POLICY','sysadmin_ctx','Company_Admin_fun','SELECT, UPDATE, DELETE, INSERT', TRUE);
EXEC DBMS_RLS.ADD_Policy ('DBA643','TIMESHEET','TIMESHEET_POLICY','sysadmin_ctx','Company_Admin_fun','SELECT, UPDATE, DELETE, INSERT', TRUE);
EXEC DBMS_RLS.ADD_Policy ('DBA643','DAILY_WORK_HOURS','DAILY_WORK_HOURS_POLICY','sysadmin_ctx','Company_Admin_fun','SELECT, UPDATE, DELETE, INSERT', TRUE);


CREATE OR REPLACE TRIGGER After_Logon_Trigger
AFTER LOGON
ON DATABASE
BEGIN
    sysadmin_ctx.PKG_Comp_Admin.Get_Company_ID;
END;
/

当我以任何管理员身份登录并执行选择查询时,我得到“没有选择行”。我认为我已将问题缩小到Set_context部分,因为当我作为其中一个管理员登录后使用以下命令时,它不会在Company_Admin上下文中显示更新的值,该值应为管理员的公司ID。

select SYS_CONTEXT('Company_Admin', 'CompanyID') from dual

非常感谢任何帮助。谢谢。

2 个答案:

答案 0 :(得分:1)

您将上下文设置为错误的值(文字字符串V_Company_ID而不是变量值) - 而不是

DBMS_SESSION.SET_CONTEXT('COMPANY_ADMIN', 'CompanyID', 'V_Company_ID');

应该是

DBMS_SESSION.SET_CONTEXT('COMPANY_ADMIN', 'CompanyID', V_Company_ID);

有关调试的一些进一步建议:

  • 在记录用户的sysadmin_ctx.PKG_Comp_Admin.Get_Company_ID过程中写入日志条目
  • 如果您使用的是Oracle 12c:使用dbms_utility.expand_sql_text查看VPD附加到原始SQL语句的内容

答案 1 :(得分:0)

您是否尝试使用 ACCESSED GLOBALLY 子句创建上下文?

如果set_context操作在与您查询上下文命名空间的会话不同的会话上运行,则您将不会获得任何数据。 在没有ACCESSED GLOBALLY子句的情况下创建的上下文仅保留会话级别的数据。使用ACCESSED GLOBALLY子句创建数据时,可以在实例级上访问数据。

P.S

我认为没有办法在RAC级别创建上下文,因为上下文在服务器的RAM上保存了它的数据,没有已知的选项(至少在11gR2上)将上下文的数据复制到每个RAC节点。 此外,依赖于上下文是有问题的,因为在实例关闭的情况下无法保存数据(如果计划将其用作经典全局变量或全局计数器 - 命名空间&每次都应该在实例启动时设置数据。