Oracle使列只能由触发器编辑

时间:2017-09-12 20:19:43

标签: sql oracle oracle11g

我们陷入了僵局,试图解决这个问题。

我们triggerA更新myColumn tableB,但我们不希望我们的应用用户能够直接编辑myColumn,只能通过调用{{} 1}}在triggerA上(每个人说的是一个“动态”列)。

这里的问题是,如果我只是向tableA添加了禁止更改其tableB的触发器,那么myColumn也无法完成其工作。< / p>

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

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

我们的问题有解决办法吗?

3 个答案:

答案 0 :(得分: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
);

现在向用户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;

请注意,仅为tableA中的列id授予UPDATE权限! 用户scott对列myColumn

没有更新权限

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

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

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

commit;

现在scott正在尝试更新mycolumn中的tableb

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能够更新mycolumn中的tablea,并且触发器(使用架构所有者 - 用户test的权限触发)会更新tableb.mycolumn的值1}}:

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 :(得分:1)

如果您只想允许一个TRIGGER更新myColumn,无论用户尝试制作UPDATE,您都可以使用其他TRIGGER执行此操作检查UPDATEmyColumn并拒绝TABLEA生成的任何内容 - &gt; 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 - &gt;更改TABLEB的{​​{1}} TRIGGER。在此示例中,它只会将myColumn替换为其自己myColumn中的数据:

MY_COLUMN_DRIVER

然后做出警卫 - 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.

然后使用SELECT * FROM TABLEB ORDER BY 1; TABLE_B_KEY TABLE_B_OTHER_DATA MYCOLUMN 1 10 100 2 20 200 更新TABLEA

myColumn

然后,UPDATE TABLEB中的其他列:

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

答案 2 :(得分:-1)

Oracle引入了一项新功能,允许您创建一个&#34;虚拟列&#34;,一个包含其他表列上的函数的空列。 您无法尝试在虚拟列中插入任何内容,否则您将收到新错误:

ORA-54013:虚拟列上不允许INSERT操作

同样适用于更新操作。

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