PL/SQL专家指南4——调用者权限和定义者权限 下篇

-------------------------------------------------------------调用者权限和定义者权限学习---------------------------------------------------

转自群友shehasgone:

--调用者权限的存储过程  print_table

create or replace procedure print_table(p_query in varchar2)
authid current_user
is
    l_theCursor integer default dbms_sql.open_cursor;
    l_columnValue varchar2(4000);
    l_status integer;
    l_descTb1 dbms_sql.desc_tab;
    l_colCnt number;
begin
   dbms_sql.parse(l_theCursor,p_query,dbms_sql.native);
   dbms_sql.describe_columns(l_theCursor,l_colCnt,l_descTb1);


     for i in 1..l_colCnt loop
        dbms_sql.define_column(l_theCursor,i,l_columnValue,4000);
     end loop;


     l_status:=dbms_sql.execute(l_theCursor);
     while(dbms_sql.fetch_rows(l_theCursor)>0) loop
     for i in 1..l_colCnt loop
         dbms_sql.column_value(l_theCursor,i,l_columnValue);
         dbms_output.put_line(rpad(l_descTb1(i).col_name,30)
                                   ||':'||
                                   l_columnValue);
      end loop;
      dbms_output.put_line('--------------------------');
    end loop;
 exception
   when others then
          dbms_sql.close_cursor(l_theCursor);
          raise;
 end;


 ---把这个存储过程的执行权限授予 public
SQL> grant execute on print_table to public;
 
Grant succeeded

--调用者权限的存储过程  desc_table
create or replace procedure desc_table(p_tname in varchar2)
authid current_user
as
begin
   dbms_output.put_line('Datatypes for Table '||p_tname);
   dbms_output.new_line;
   
   dbms_output.put_line(rpad('Column Name',31)||
                              rpad('Datatype',20) ||
                              rpad('Length',11) ||
                              'Nullable');
   dbms_output.put_line(rpad('-',30,'-')||''||
                                 rpad('-',19,'-')||''||
                                 rpad('-',10,'-')||''||
                                 '-----------');
    for x in 
      ( select column_name,
               data_type,
               substr(decode(data_type,
               'NUMBER',decode(data_precision,NULL,NULL,'('||data_precision||','||data_scale||')'),
                data_length),1,11) data_length,
                decode(nullable,'Y','null','not null') nullable
                from user_tab_columns
                where table_name = upper(trim(p_tname)) 
                order by column_id)
       loop
        dbms_output.put_line(rpad(x.column_name,31)||
                             rpad(x.data_type,20)||
                             rpad(x.data_length,11)||
                             x.nullable);
       end loop;
       
       dbms_output.put_line(chr(10)||chr(10)||'Indexes on '||p_tname);
       
       
       for z in 
        ( select a.index_name,a.uniqueness from user_indexes a 
           where a.table_name = upper(trim(p_tname))
           and index_type='NORMAL')
       loop
         dbms_output.put(rpad(z.index_name,31)||
                             z.uniqueness);
                             
           for y in 
            ( select decode(column_position,1,'(',',')||column_name column_name
                from user_ind_columns b
                where b.index_name = z.index_name
                order by column_position)
                
                loop
                dbms_output.put(y.column_name);
                end loop;
                dbms_output.put_line(')'||chr(10));
           end loop;
           
end;              
 ---把这个存储过程的执行权限授予 public
SQL> grant execute on desc_table to public;


--定义一个调用者权限函数
--先建立一个测试表

create table t ( x varchar2(255));


--建立调用者权限函数
create or replace function Invoker_rights_function return varchar2
authid current_user
as
     l_data varchar2(4000);
begin
   dbms_output.put_line('I am an IR PROC owned by A');
   select 'current_user='||sys_context('userenv','currnet_user')||
    'current_schema='||sys_context('userenv','current_schema')||
    'active roles='||cnt||
    'data from T='||t.x
    into l_data
        from (select count(*) cnt from session_roles), t;
        return l_data;

  end;


--把这个调用者权限函数授权到public
SQL> grant execute on she.invoker_rights_function to public;
 
Grant succeeded


--定义一个定义者权限函数
create or replace function Definer_rights_function return varchar2
as
     l_data varchar2(4000);
begin
   dbms_output.put_line('I am an IR PROC owned by A');
   select 'current_user='||sys_context('userenv','currnet_user')||
    'current_schema='||sys_context('userenv','current_schema')||
    'active roles='||cnt||
    'data from T='||t.x
    into l_data
        from (select count(*) cnt from session_roles), t;
        return l_data;

  end;


--把这个调用者权限函数授权到public
SQL> grant execute on she.Definer_rights_function to public;
 
Grant succeeded



--如果是视图则以定义者权限存储
 create view V
  as

  select invoker_rights_function from dual;


--把视图的查询权限授予public
SQL> GRANT SELECT ON V TO PUBLIC;
 
Grant succeeded
 
当切换用户执行时如果如下:
SELECT * FROM SHE.V
结果
current_user=current_schema=SHEactive roles=0data from T=SHE'table
这说明,查的不是当前用户,而是she用户下的。
此时当前用户下无T表也不会报错。 


--对于查询
SELECT SHE.INVOKER_RIGHTS_FUNCTION FROM DUAL;
结果:
current_user=current_schema=SCOTTactive roles=2data from T=SCOTT'table
这个是当前用户下的信息。
此时当前用户下无T表也会报错。 
--最后说明, 视图是以定义者权限进行编译存储的。




--健壮的程序
--建立测试表。
create table t (pk number);
create table y ( c number,d number);
-建立测试存储过程
create or replace procedure P
authid current_user
as
 no_such_table exception;
 pragma exception_init(no_such_table,-942);


 insufficient_privs exception;
 pragma exception_init(insufficient_privs,-1031);


 invalid_column_name exception;
 pragma exception_init(invalid_column_name,-904);


 inconsistent_datatypes exception;
  pragma exception_init(inconsistent_datatypes,-932);


  begin
   for x in (select pk from t ) loop
   update y set c=c+0.5 where d=x.pk;
   end loop;
 commit;
   exception
    when no_such_table then
      dbms_output.put_line('Error Caught:'||sqlerrm);


       when insufficient_privs then
      dbms_output.put_line('Error Caught:'||sqlerrm);


       when invalid_column_name then
      dbms_output.put_line('Error Caught:'||sqlerrm);


       when inconsistent_datatypes then
      dbms_output.put_line('Error Caught:'||sqlerrm);
  rollback;
 end;
---
--换其它用户执行
SQL> conn scott/tiger;
已连接。
SQL> set serveroutput on;
SQL> exec she.p;
Error Caught:ORA-00904: "PK": 无效的标识符


PL/SQL 过程已成功完成。


SQL> exec she.p;


PL/SQL 过程已成功完成。


SQL> exec she.p;
Error Caught:ORA-00904: "D": 无效的标识符


PL/SQL 过程已成功完成。


SQL> exec she.p;
Error Caught:ORA-00932: 不一致的数据类型: 要求 NUMBER 得到的却是 DATE


PL/SQL 过程已成功完成。


SQL> exec she.p;
BEGIN she.p; END;


*
ERROR 位于第 1 行:
ORA-00932: 不一致的数据类型: 要求 NUMBER 得到的却是 DATE
ORA-06512: 在"SHE.P", line 17
ORA-06512: 在line 1




SQL> exec she.p;
Error Caught:ORA-00932: 不一致的数据类型: 要求 NUMBER 得到的却是 DATE


PL/SQL 过程已成功完成。


SQL>


--注:如何获取关联的ora错误编号。 
当我把
 no_such_table exception;
 pragma exception_init(no_such_table,-942);
中的-943后,重新编译这个存储过程
执行时。
SQL> exec she.p;
BEGIN she.p; END;


*
ERROR 位于第 1 行:
ORA-00942: 表或视图不存在
ORA-06512: 在"SHE.P", line 17
ORA-06512: 在line 1
这里是会报错的,真正的错误是ORA-00942
这时就已经提示我们,这个异常编号是和-942关联的
此时,把-943 再换成-942编译后,达到捕捉异常后,过程能运行的目的。 




--select * 的副作用 
 建立测试表及数据。
create table t (msg varchar2(25),c1 int,c2 int);
insert into t values('c1=1,c2=2',1,2);
commit;
--建立调用者权限存储过程
create or replace procedure p
authid current_user
as
begin
  for x in (select * from t) loop
      dbms_output.put_line('msg='||x.msg);
      dbms_output.put_line('c1='||x.c1);
      dbms_output.put_line('c2='||x.c2);
  end loop;
end;
--执行 
exec p;
--结果
msg=c1=1,c2=2
c1=1
c2=2


--把执行权限授予public
SQL> grant execute on p to public;
 
Grant succeeded
--建立测试用户
SQL> create user u1 identified by u1 default tablespace users;


用户已创建


SQL> grant dba,resource,connect to u1;


授权成功。
--切换到u1用户
--建立测试表及数据
create table t (msg varchar2(25),c2 int ,c1 int);
insert into t values('c1=2,c2=1',1,2);
commit;
--调用上面的存储过程 
SQL> set serveroutput on;
SQL> exec she.p;
 
msg=c1=2,c2=1
c1=1
c2=2
 
PL/SQL procedure successfully completed
注意此时的结果 c1=1,c2=2当前用户表中的c1=2,c2=1
分析:它根本不是所要求的--但认真思考后会发现,这确实是按程序实现的。
在编译时,PL/SQL为设置了隐含记录X。记录X只是一个数据结构,具有3个元素:
MSG VARCHAR2,C1 NUMBER,C2 NUMBER。在用户SHE的查询分析阶段,SELECT * 
按次序展开为MSG,C1,C2。然而作为U1,它们被展开为MSG,C2,C1。因为数据类型
完全与隐含的记录X匹配,您不会收到INCONSISTENT DATATYPE 的错误提示(如果
数据类型不匹配,这一错误还是会发生)。数据读取成功,便把记录属性C1列放入
了C2列。这是预期的行为,也是在产品程序中不使用SELECT * 的另一个充分的理由。




---隐藏的伪列
--以she用户建立表和测试数据
create table t (c1 int);
insert into t values(1);
commit;
--建立调用者权限存储过程
create or replace procedure p
authid current_user
as
    c2 number default 5;
begin
   update t set c1=c2;
   commit;
end;  
--在当前用户下执行并查询表T
SQL> exec p;
 
PL/SQL procedure successfully completed
 
SQL> select * from t;
 
                                     C1
---------------------------------------
                                      5
注意这里是5.
--如果表t 有字段c2,同时c2的值为8,那此时看到的值应该是8.


--接着切换到用户u1
--建立测试表及数据
create table t (c1 int ,c2 int);
insert into t values(1,2);
commit;
--执行用户she下的存储过程P并查看结果。
SQL> exec she.p;
 
PL/SQL procedure successfully completed
 
SQL> select * from t;
 
                                     C1                                      C2
--------------------------------------- ---------------------------------------
                                      5                                       2
注意,此时的结果  c1的值为5,但c2的值没有变化。
分析:它的对错在于您如何看待。对于UPDATE SET C1=C2语句,如果在SQL*PLUS提示符状态
运行,结果是C1被设置为2,而不是5.然而,因为编译时PL/SQL重写此查询没有任何对于C2
的引用,所以它对T数据表复本进行操作,与它对其化T数据表复本所做的完全相同--将
C1设置为5。此PL/SQL程序不能“看到”C2列,因为C2不存在于它编译的对象中。
  起先,这似乎有些混乱,因为您不有正常地看到改写后的更新内容,但一旦您了解了它,
它是非常有意义的。


------------------------------2011-10-13-------------------------------------
--------------------------------全文完---------------------------------------
发布了842 篇原创文章 · 获赞 84 · 访问量 221万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览