我们陷入了僵局,试图解决这个问题。
我们triggerA
更新myColumn
tableB
,但我们不希望我们的应用用户能够直接编辑myColumn
,只能通过调用{{} 1}}在triggerA
上(每个人说的是一个“动态”列)。
这里的问题是,如果我只是向tableA
添加了禁止更改其tableB
的触发器,那么myColumn
也无法完成其工作。< / p>
我们无法在应用程序级别进行任何更改,用户权限也无法解决,因为他们需要能够触发triggerA
。
我在这里搜索过“将参数传递给触发器”,但所有答案都在应用程序级别内。
我们的问题有解决办法吗?
答案 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
执行此操作检查UPDATE
到myColumn
并拒绝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操作
同样适用于更新操作。
使用虚拟列还可以简化派生列的使用。透明派生值不需要应用程序计算和插入其他值。这也可以防止需要在表上使用触发器来提供此功能的替代实现。在表中使用虚拟列还消除了使用视图显示派生列值的需要。