2017-09-12 56 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

相關問題