我写了一个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
非常感谢任何帮助。谢谢。
答案 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
过程中写入日志条目dbms_utility.expand_sql_text
查看VPD附加到原始SQL语句的内容答案 1 :(得分:0)
您是否尝试使用 ACCESSED GLOBALLY 子句创建上下文?
如果set_context操作在与您查询上下文命名空间的会话不同的会话上运行,则您将不会获得任何数据。 在没有ACCESSED GLOBALLY子句的情况下创建的上下文仅保留会话级别的数据。使用ACCESSED GLOBALLY子句创建数据时,可以在实例级上访问数据。
P.S
我认为没有办法在RAC级别创建上下文,因为上下文在服务器的RAM上保存了它的数据,没有已知的选项(至少在11gR2上)将上下文的数据复制到每个RAC节点。 此外,依赖于上下文是有问题的,因为在实例关闭的情况下无法保存数据(如果计划将其用作经典全局变量或全局计数器 - 命名空间&每次都应该在实例启动时设置数据。