ORA-01031 權限不足-存儲過程中DBA 角色用戶無法執行DDL


  

  

  Oracle的存儲過程,是我們使用數據庫應用開發的重要工具手段。在存儲過程中,我們大部分應用場景都是使用DML語句進行數據增刪改操作。本篇中,我們一起探討一下數據定義語句DDL在存儲過程中使用的細節和要點。

 

1、“借道而行”的DDL

 

  從Oracle PL/SQL和存儲過程程序開發原則上,應該是不鼓勵在SP中使用DDL語句的。首先一個表現,就是Oracle在編譯時就不允許直接在SP中使用DDL語句。下面我們使用Oracle 10gR2作為實驗環境。

 

 

SQL> select * from v$version;

 

BANNER

----------------------------------------------------------------

Oracle Database10gEnterpriseEdition Release10.2.0.1.0 - Prod

PL/SQL Release10.2.0.1.0 - Production

CORE    10.2.0.1.0      Production

 

TNS for 32-bit Windows: Version10.2.0.1.0 - Production

NLSRTL Version10.2.0.1.0 – Production

 

 

  建立存儲過程p_test_nc,進行簡單的數據表創建。

 

 

SQL> create or replace procedure P_TEST_NC

 2 is

 3 begin

 4   create table t (id number);

 5 end P_TEST_NC;

 6 /

 

Warning: Procedure created with compilation errors

 

SQL> select name, text from user_errors;

 

NAME      TEXT

---------- --------------------------------------------------------------------------------

P_TEST_NC PLS-00103:出現符號"CREATE"在需要下列之一時:

           begin case declare exit

             for goto if loop mod null pragma raise return select update

             while with <an identifier>

             <a double-quoted delimited-identifier> <a bind variable> <<

             close current delete fetch lock insert open rollback

             savepoint set sql execute commit forall merge pipe

 

 

  顯然,在編譯時Oracle就報錯不允許存儲過程創建。之后的實驗droptruncate table操作,也都是不允許直接在存儲過程中書寫DDL語句。說明起碼使用直接的DDL語句,存儲過程是不能編譯通過的。

 

  那么,有沒有什么折中的方法呢?我們說是有的,就是借助“execute immediate”方法,“繞過”編譯過程中對DDL的屏蔽。我們使用truncate table DDL語句實驗。

 

 

SQL> create or replace procedure P_TEST_NC

 2 is

 3 begin

  4   execute immediate'truncate table t';

 5 end P_TEST_NC;

 6 /

Procedure created

 

 

  編譯通過了,DDL語句以一個字符串的形式避開了編譯時Oracle的語法檢查,編譯成功。那么,執行起來會不會報運行時錯誤呢?

 

 

SQL> exec p_test_nc;

PL/SQL procedure successfully completed

 

 

  執行成功,說明:Oracle存儲過程中,可以使用exectue immediate語句繞開編譯時對DDL語句的檢查,生成運行代碼。

 

 

2SPDDL權限

 

  任何程序編譯執行都會伴隨着語法語義的一系列檢查。使用execute immediate雖然可以回避編譯時檢查,但是SQL語句還是面臨着運行時檢查的問題。下面看實驗的例子。

 

--scott用戶下進行試驗;

SQL> create or replace procedure P_TEST_NC

 2 is

 3 begin

 4   execute immediate 'create table t(id number)';

 5 end P_TEST_NC;

 6 /

Procedure created編譯時通過;

 

SQL> exec p_test_nc;

 

begin p_test_nc; end;

 

ORA-01031:權限不足

ORA-06512:"SCOTT.P_TEST_NC", line 4

ORA-06512:line 1

 

 

  在用戶自己的schema下創建數據表,難道是不允許的嗎?顯然不是。

 

 

SQL> create table m (id number);

Table created

 

 

  單獨創建是允許的,說明是由於權限機制導致的問題。我們切換到sys用戶上,提高scott用戶權限。

 

 

Connected as SYS

--賦予最高創建數據表的系統權限;

SQL> grant create any table to scott;

Grant succeeded

 

 

  切換回scott用戶,繼續實驗。

 

 

SQL> conn scott/tiger@orcl;

Connected to Oracle Database10gEnterpriseEdition Release10.2.0.1.0

Connected as scott

 

SQL> exec p_test_nc;

PL/SQL procedure successfully completed

 

SQL> select * from t;

       ID

----------

 

  執行成功!這個原因是什么呢?還是由於存儲過程權限體系特點和DDL語句特點共同造成的。

 

  在之前筆者的系列文章《所有者權限和調用者權限》(http://space.itpub.net/17203031/viewspace-692161)中,介紹了Oracle存儲過程采用的兩種權限體系方式和role權限在存儲過程執行中的特殊性。

 

  默認情況下,Oracle對存儲過程是使用所有者權限,也就是說:如果用戶B調用了用戶A schema下的一個存儲過程,其中使用的對象權限和系統權限,全部都是用戶A的。如果用戶A沒有權限,用戶B執行要報錯。

 

  同時,用戶的角色權限在進入存儲過程后,會被剝離掉,是不起效果的。

 

  結合上面的實驗,就好解釋了:scott自身只擁有一個resource的角色權限,單獨在SQL中使用沒有問題。進入到SP之后,這個create table的權限就被剝離掉了。而該SP存在被其他用戶調用生成數據表的可能。所以會在運行時報錯權限不足。

 

  當我們顯示的賦予scott用戶create any table/create table之后,系統權限就可以滲透到SP中起效果了。

 

  這並不是解決該問題的唯一方法。此處我們可以使用調用者權限機制,改寫SP代碼。首先我們剔除掉scottcreate any table權限。

 

 

Connected to Oracle Database10gEnterpriseEdition Release10.2.0.1.0

Connected as SYS

 

SQL> revoke create any table from scott;

Revoke succeeded

 

SQL> conn scott/tiger@orcl;

Connected to Oracle Database10gEnterpriseEdition Release10.2.0.1.0

Connected as scott

 

SQL> exec p_test_nc;

begin p_test_nc; end;

 

ORA-01031:權限不足

ORA-06512:"SCOTT.P_TEST_NC", line 4

ORA-06512:line 1

 

 

我們改寫代碼為:

 

 

SQL> create or replace procedure P_TEST_NC

 2 Authid Current_User

 3 is

 4 begin

 5   execute immediate 'create table t (id number)';

 6 end P_TEST_NC;

 7 /

Procedure created

 

SQL> exec p_test_nc;

PL/SQL procedure successfully completed

 

 

  執行成功,這里使用“authid Current_user”將存儲過程轉化為調用者權限。每次調用存儲過程,都是動態根據調用者的權限構成去判定是否有權限,這樣就回避了該問題的出現。

 

  總之:在使用DDL在存儲過程中時,權限管理和使用的復雜度是在增加。

 

 

4DDL對事務的提交影響

 

  將DDL語句放置在存儲過程中,潛在最大風險就是對事務管理的破壞。在Oracle中,如果調用一個DDL語句,潛藏效果就是將當前會話的未提交事務進行提交。這個過程顯然是對原有的事務邏輯破壞。

 

 

SQL> create table m (id number);

Table created

 

SQL> select * from m;

       ID

----------

 

SQL> create or replace procedure P_TEST_NC

 2 is

 3 begin

 4   insert into m values (3);

 5   execute immediate 'truncate table t';

 6 

 7   rollback;

 8 end P_TEST_NC;

 9 /

 

Procedure created

 

--執行代碼

SQL> exec p_test_nc;

PL/SQL procedure successfully completed

 

--事務提交

SQL> select * from m;

       ID

----------

        3

 

 

  從上面的例子上,我們可以清楚的看到現象。由於中間的truncate table操作,引起數據表m的插入操作被提交commit。而真正的事務邏輯可能是一個rollback

 

  所以,在SP中使用DDL命令,可能引起業務邏輯的不可控提交和數據不一致,這個風險在任何應用中是不可以允許的。

 

  那么,有沒有方法回避這個過程呢?經一個同事提醒,的確可以使用手段回避。

 

 

5DDL與自治事務

 

  自治事務(AUTONOMOUS_TRANSACTION)是保證在事務進行過程中一段獨立的事務過程。如果在DDL操作外套入一個自治事務過程,是否就可以回避問題了。

 

 

SQL> select * from m;

 

       ID

----------

 

SQL> create or replace procedure P_TEST_NC is

 2   procedure p_inner_test

 3   is

 4   PRAGMA AUTONOMOUS_TRANSACTION;

 5   begin

 6     --調用ddl

 7     execute immediate 'truncate table t';

 8   end;

 9 begin

 10   insert into m values (3);

 11   p_inner_test;

 12 

 13   rollback;

 14 end P_TEST_NC;

 15 /

Procedure created

 

SQL> exec p_test_nc;

PL/SQL procedure successfully completed

\

SQL> select * from m;

       ID

----------

 

 

  實驗成功,通過自治事務的確可以回避DDL的事務問題。

 

6、結論

 

  DDLSP中,與常規的DML操作差異很大。這種差異不僅僅是語法上,更多的是權限、事務等更深層次復雜的差異。所以,從Oracle的角度看,盡量少在SP中使用DDL語句,避免出現不可控的問題。

 

  PLS-00157:   AUTHID only allowed on schema-level programs

  查了下錯誤原因 An AUTHID clause was specified for a subprogram inside a package or type. These clauses are only supported for top-level stored procedures, packages, and types.

  大致意思就是authid只能用在頂級的存儲過程、包、類型上,不能用在包或類型的子程序上

 

  在包上加入authid,執行正常了。

  create or replace package rule_execute
  authid current_user

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM