1. 程式人生 > >select...for update的鎖許可權

select...for update的鎖許可權

今天生產遇到一個dblink使用者自由select許可權,但能通過for update鎖表,感覺很奇怪

網上的大拿給了參考,mark下:

幾年以前有朋友問過類似的問題,當時考慮了一下,沒有想到好的解決方法,前些天有客戶詢問同樣的問題,沒有辦法週末仔細琢磨了一下,總算是找到一個解決的方法。

 

 

其實現在Oracle有專門的工具可以解決這個問題,就是Oracle的FireWall,通過直連的配置方式可以阻塞預配置好的FOR UPDATE操作,不過那需要單獨的軟體。這裡主要方案是要通過資料庫現有的功能實現這個目標。

限制FOR UPDATE是有實際意義的,有時候只希望給使用者分配查詢許可權,但是一旦分配了SELECT許可權後,使用者就自動擁有了FOR UPDATE能力,雖然使用者並沒有真正UPDATE的許可權,但是仍然可以將表的記錄鎖定,而這有時候並不是所期望的。

SQL> conn test/test
Connected.
SQL> create table t_update (id number, name varchar2(30));

Table created.

SQL> insert into t_update values (1, 'a');

1 row created.

SQL> commit;

Commit complete.

SQL> create user u1 identified by u1;

User created.

SQL> grant create session to u1;

Grant succeeded.

SQL> grant select on t_update to u1;

Grant succeeded.

切換到U1使用者,現在可以對T_UPDATE進行SELECT FOR UPDATE操作:

SQL> conn u1/u1
Connected.
SQL> set sqlp 'SQL2> '

SQL2> select * from test.t_update where id = 1 for update;

        ID NAME
---------- ------------------------------
         1 a

為了避免FOR UPDATE操作,可以封裝一層檢視。

SQL> create view v_update as select * from t_update;

View created.

SQL> grant select on v_update to u1;

Grant succeeded.

但是如果僅是檢視,那麼沒有任何作用,FOR UPDATE操作同樣可以對單表查詢的檢視執行:

SQL2> select * from test.v_update where id = 1 for update;

        ID NAME
---------- ------------------------------
         1 a

如果新增ROWNUM等偽列,可以避免直接FOR UPDATE:

SQL> create or replace view v_update as select rownum rn, a.* from t_update a;

View created.

但是如果FOR UPDATE指定ROWNUM偽列外的真實列,還是可以繞開:

SQL2> select * from test.v_update where id = 1 for update; 
select * from test.v_update where id = 1 for update
                   *
ERROR at line 1:
ORA-02014: cannot select FOR UPDATE from view with DISTINCT, GROUP BY, etc.

SQL2> select id, name from test.v_update where id = 1 for update;

        ID NAME
---------- ------------------------------
         1 a

通過報錯資訊可以看出,如果包含了GROUP BY或DISTINCT就可以阻止FOR UPDATE操作:

SQL> create or replace view v_update as select distinct * from t_update;

View created.

但是這種方式無疑會帶來效能問題,更重要的是,如果表中存在重複記錄,那麼DISTINCT操作會使得重複記錄丟失。

而最好的解決方法是採用UNION ALL方式建立檢視:

SQL> create or replace view v_update as 
2 select * from t_update
3 union all
4 select * from t_update where 1 = 2;

View created.

現在就達到了阻止FOR UPDATE的操作,且對於查詢基表的效能影響最小:

SQL2> select * from test.v_update where id = 1 for update;
select * from test.v_update where id = 1 for update
*
ERROR at line 1:
ORA-02014: cannot select FOR UPDATE from view with DISTINCT, GROUP BY, etc.

對於不希望使用者執行FOR UPDATE操作的表,可以建立成UNION ALL檢視,並將檢視的查詢許可權授權給使用者。

  原文:http://blog.itpub.net/4227/viewspace-713128/