2017-09-12 145 views
2

我们在这里试图弄清楚这一点。Oracle仅允许通过触发器对列进行编辑

我们有triggerA,更新从tableBmyColumn,但我们不希望我们的应用程序的用户可以直接编辑0​​,只有通过调用triggerA这是对tableA(这是每说一个“动态”栏)。

这里的问题是,如果我简单地添加一个触发器到tableB,禁止更改其myColumn,那么triggerA也将无法完成其任务。

我们无法在应用程序级别进行任何更改,并且用户权限也无法解决,因为它们需要能够触发triggerA

我在这里搜索了“传递参数给触发器”,但是所有的答案都在应用程序级别。

有没有解决我们问题的方法?

+0

感谢Mojimi。如果用户试图直接修改myColumn,他们会收到一个异常(或者至少是数据库服务器抛出异常),那么可以接受吗? – alexgibbs

+0

@alexgibbs是的,这将是可以接受的,我会测试你的答案和报告 – Mojimi

回答

4

只需从应用程序以另一个用户身份登录到Oracle数据库,而不是架构所有者,并正确配置权限。

一个例子胜过千言万语 - 用户test,架构test

CREATE TABLE TABLEa(
    id int, 
    myColumn int 
); 

CREATE TRIGGER update_table_B_mycolumn 
AFTER UPDATE OF myColumn ON tableA 
FOR EACH ROW 
BEGIN 
    UPDATE tableB 
    SET myColumn = :new.mycolumn 
    WHERE id = :new.id; 
END; 
/

CREATE TABLE TableB(
    id int, 
    myColumn int 
); 

现在授予appriopriate特权用户scott

GRANT ALL ON TableA to scott; 
GRANT update (id) ON TableB to scott; -- update only ID, no myColumn 
GRANT SELECT, INSERT ON TableB to scott; 

注意UPDATE权限仅授予对于表A中的id列!
用户scott对列没有更新特权myColumn



现在让我们登录为scott和测试我们的解决方案:

insert into test.tablea values(1,5); 

insert into test.tableb values(1,5); 

commit; 

现在scott试图更新mycolumntableb

update test.tableb set mycolumn = 3 where id = 1; 

QL Error: ORA-01031: insufficient privileges 
01031. 00000 - "insufficient privileges" 
*Cause: An attempt was made to perform a database operation without 
      the necessary privileges. 
*Action: Ask your database administrator or designated security 
      administrator to grant you the necessary privileges 

scott能够在tablea更新mycolumn,并触发(与模式所有者的权限解雇 - 用户test)更新的tableb.mycolumn值:

update test.tablea set mycolumn = 3 where id = 1; 

select * from test.tableb; 

     ID MYCOLUMN 
---------- ---------- 
     1   3 

编辑 - 基于触发器的解决方案


如果您必须使用触发器,那么在启用/禁用触发器的包中保留一个标志。
请看下面的例子:

CREATE TABLE TABLEa(
    id int, 
    myColumn int 
); 
CREATE TABLE TableB(
    id int, 
    myColumn int 
); 
INSERT INTO tableA values(5,5); 
INSERT INTO tableB values(5,5); 
commit; 

一种包装

CREATE or replace PACKAGE table_b_trigger_switch 
IS 
    PROCEDURE ENABLE_UPDATE_TRIGGER(sw BOOLEAN); 
    FUNCTION IS_UPDATE_ENABLED RETURN BOOLEAN; 
END; 
/

CREATE or replace PACKAGE BODY table_b_trigger_switch 
IS 
    enable_flag BOOLEAN := FALSE; 

    PROCEDURE ENABLE_UPDATE_TRIGGER(sw BOOLEAN) 
    IS 
    BEGIN 
     enable_flag := sw; 
    END; 

    FUNCTION IS_UPDATE_ENABLED RETURN BOOLEAN 
    IS 
    BEGIN 
     RETURN enable_flag; 
    END; 
END; 
/

触发器:

CREATE or replace TRIGGER prevent_update_tableB_mycolumn 
BEFORE UPDATE OF myColumn ON tableB 
FOR EACH ROW 
BEGIN 

    IF NOT table_b_trigger_switch.IS_UPDATE_ENABLED THEN 
     raise_application_error(-20222, 'Updating of myColumn in TABLE_B is NOT ALLOWED'); 
    END IF; 
END; 
/

CREATE or replace TRIGGER update_tableB_mycolumn 
AFTER UPDATE OF mycolumn ON TableA FOR EACH ROW 
BEGIN 
    table_b_trigger_switch.ENABLE_UPDATE_TRIGGER(TRUE); 

    UPDATE TABLEB b SET b.MYCOLUMN = :NEW.MYCOLUMN 
    WHERE b.id = :NEW.id; 

    table_b_trigger_switch.ENABLE_UPDATE_TRIGGER(FALSE); 
EXCEPTION WHEN OTHERS THEN 
    table_b_trigger_switch.ENABLE_UPDATE_TRIGGER(FALSE); 
    raise; 
END; 
/

一个试验:

select * from tableb; 

     ID MYCOLUMN 
---------- ---------- 
     5   5 

UPDATE tableb SET myColumn = 3; 
ORA-20222: Updating of myColumn in TABLE_B is NOT ALLOWED 
ORA-06512: at "TEST.PREVENT_UPDATE_TABLEB_MYCOLUMN", line 4 

UPDATE tablea SET myColumn = 3; 
1 row updated. 

select * from tableb; 

     ID MYCOLUMN 
---------- ---------- 
     5   3 
+1

为了让这一点更清楚,我认为这将是有益的,如果你将模式名称添加到CREATE TABLE语句,所以它很清楚它们在TEST模式中而不是在SCOTT。 –

+0

我知道这对其他人来说是个答案,但在我的情况下它不起作用,公司应用程序已经管理权限,我们不能撤销权限 – Mojimi

+0

@Mojimi我已经更新了我的答案基于触发器的解决方案。 – krokodilko

-1

Oracle引入了一项新功能,允许您创建一个“虚拟列”,一个包含其他表列的函数的空列。 你不能试图插入任何物件虚拟列或者你会得到新的错误:

ORA-54013:不允许对虚拟列

同样更新操作INSERT操作。

使用虚拟列还简化了派生列的使用。透明派生的值不需要应用程序计算并插入附加值。这也可以防止在表上使用触发器来提供该功能的替代实现。在表中使用虚拟列也消除了使用视图来显示派生列值的需要。

1

如果你想只允许一个TRIGGER更新myColumn,无论什么用户试图使UPDATE的,你可以用另一个TRIGGER,对于UPDATE旅行支票要做到这一点myColumn并拒绝任何不是由TABLEA - >TABLEB触发器生成的。

这里是一个11g兼容的例子。 (从12c开始,UTL_CALL_STACK有一些很好的替代工具)。

首先,创建测试表:

CREATE TABLE TABLEB(
    TABLE_B_KEY NUMBER NOT NULL PRIMARY KEY, 
    TABLE_B_OTHER_DATA NUMBER, 
    MYCOLUMN NUMBER 
); 

CREATE TABLE TABLEA(
    TABLE_A_DATA NUMBER NOT NULL PRIMARY KEY, 
    TABLE_B_FK NUMBER NOT NULL REFERENCES TABLEB(TABLE_B_KEY), 
    MYCOLUMN_DRIVER NUMBER NOT NULL 
); 

Table TABLEB created. 
Table TABLEA created. 

然后,TABLEA - >TABLEBTRIGGER改变myColumn。在这个例子中的情况下,它只会在自己的MY_COLUMN_DRIVER取代myColumn与数据:

CREATE OR REPLACE TRIGGER TABLEA_MYCOL_UPDATER 
    AFTER INSERT OR UPDATE ON TABLEA 
    FOR EACH ROW 
    BEGIN 
    UPDATE TABLEB SET MYCOLUMN = :NEW.MYCOLUMN_DRIVER 
    WHERE TABLEB.TABLE_B_KEY = :NEW.TABLE_B_FK; 
    END; 
/

Trigger TABLEA_MYCOL_UPDATER compiled 

然后使特派守TRIGGER

CREATE OR REPLACE TRIGGER MYCOLUMN_DIRECT_GUARD 
BEFORE UPDATE 
    ON TABLEB 
FOR EACH ROW 
    DECLARE 
    C_FORBIDDEN_MESSAGE    VARCHAR2(128) := 'Direct Modification of MYCOLUMN is forbidden.'; 
    C_ALLOWED_CALLER CONSTANT  VARCHAR2(128) := 'TABLEA_MYCOL_UPDATER'; 
    BEGIN 
    IF ((:NEW.MYCOLUMN <> :OLD.MYCOLUMN) AND NOT (DBMS_UTILITY.FORMAT_CALL_STACK LIKEC '%TABLEA_MYCOL_UPDATER%')) 
    THEN 
     RAISE_APPLICATION_ERROR(-20819, C_FORBIDDEN_MESSAGE); 
    END IF; 
    END; 
/


Trigger MYCOLUMN_DIRECT_GUARD compiled 

然后尝试一下。 初始化数据:

INSERT INTO TABLEB VALUES(1,10,100); 
INSERT INTO TABLEB VALUES(2,20,200); 

1 row inserted. 
1 row inserted. 

Inital国家:

SELECT * FROM TABLEB ORDER BY 1; 
TABLE_B_KEY TABLE_B_OTHER_DATA MYCOLUMN 
1   10     100  
2   20     200  

然后在TABLEB使用TABLEA更新myColumn

INSERT INTO TABLEA VALUES(1,1,1); 
1 row inserted. 

SELECT * FROM TABLEB ORDER BY 1; 
TABLE_B_KEY TABLE_B_OTHER_DATA MYCOLUMN 
1   10     1   
2   20     200  

然后,更新另一列:

UPDATE TABLEB SET TABLE_B_OTHER_DATA = 500 WHERE TABLE_B_KEY = 2; 
1 row updated. 

SELECT * FROM TABLEB ORDER BY 1; 
TABLE_B_KEY TABLE_B_OTHER_DATA MYCOLUMN 
1   10     1   
2   500     200  

而尝试直接UPDATE myColumn

UPDATE TABLEB SET MYCOLUMN = 7777 WHERE TABLE_B_KEY = 2; 

Error starting at line : 1 in command - 
UPDATE TABLEB SET MYCOLUMN = 7777 WHERE TABLE_B_KEY = 2 
Error report - 
ORA-20819: Direct Modification of MYCOLUMN is forbidden. 
+0

我相信这正是我所寻找的,一种确定来电者的方式 – Mojimi

+0

谢谢@Mojimi很高兴听到。还有什么我可以添加,这将有助于在这里,或这是否回答你的问题? – alexgibbs

+0

嗯,我相信这两个答案的解决方案,我会测试和报告结果 – Mojimi

相关问题