一、问题描述:
由于某种原因,我需要定期去从新编译某个schema下所有INVALID状态的试图,于是创建了如下存储过程:
1、首先单独创建一个用户,并授权dba权限;
create user db_admin identified by "passwd";
grant dba to db_admin;
2、使用db_admin用户登录数据库,创建如下存储过程;
create or replace procedure compile_invalid_views(
p_owner varchar2
) as
--编译某个用户下的无效视图
str_sql varchar2(2000);
begin
for invalid_views in (select object_name from all_OBJECTS
where status = 'INVALID' and object_type = 'VIEW' and owner=upper(p_owner))
loop
alter_sql := 'alter view '||p_owner||'.' ||invalid_views.object_name || ' compile';
begin
execute immediate alter_sql;
end;
end loop;
end;
3、指定定时任务,定期调用存储过程来从新编译sysadm用户下的无效视图;
*/5 * * * * sh /home/oracle2/scripts/compile_invalid_views.sh
[oracle2@B-GH4-PSDB-01 ~]$ cat /home/oracle2/scripts/compile_invalid_views.sh
#!/bin/bash
source /home/oracle2/.bash_profile
sql_str=sqlplus -S db_admin/'passwd'@mz <
echo $sql_str
[oracle2@B-GH4-PSDB-01 ~]$
然后过了几天后,发现有的无效视图并没有被从新编译,业务还是报错,太奇怪了,db_admin有dba权限,执行存储过程没有报错;
二、原因分析:
存储过程分成两种权限:
1.定义者权限 AUTHID DEFINER
2.调用者权限 AUTHID CURRENT_USER
默认的情况下使用的是定义者权限,也就是存储过程内部的权限是继承的创建者的权限;
1、定义者权限使用遇到的问题:
当使用定义者权限时候,不管是你自己去call,还是其他用户去call,效果是一样的,都是用你的权限去执行。但是,不是你的全部权限,是把所有Roles的权限去掉了( 因为我只给db_admin授权了dba的权限,但是他是个角色,所以存储过程里面会去掉dba这个角色,这是问题的关键,一般不注意),当去掉dba角色后,再查询all_OBJECTS是不能查到别的schema下视图信息的,所以也就无法从新编译别的用户下的视图了。
2、调用者权限使用遇到的问题:
当使用调用者权限时候,用什么用户去call存储过程,就用谁的权限,而且包括角色权限。请注意,用数据字典
user_xxx, 这个是调用者的,所以建议不要用他,而用all_xxx或dba_xxxx. (当然是要求调用者有select_catalog_role)
另外,涉及到create table, create index, ...要确认调用者是否具备在该存储过程所在的schema下有此权限(如果调用者就是你自己,肯定没问题)。
三、解决问题:
方法1:这样的话需要保证调用者的权限是足够的,调用者只有执行存储过程的权限是不行的!因为调用者是dba权限,所以如下这样让存储过程继承调用者的权限,是可以解决问题的!
添加AUTHID CURRENT_USER 关键字,这样存储过程就继承执行者(调用者)的所有权限,包括角色权限!
create or replace procedure compile_invalid_views(
p_owner varchar2
) AUTHID CURRENT_USER as
--编译某个用户下的无效视图
str_sql varchar2(2000);
begin
for invalid_views in (select object_name from all_OBJECTS
where status = 'INVALID' and object_type = 'VIEW' and owner=upper(p_owner))
loop
alter_sql := 'alter view '||p_owner||'.' ||invalid_views.object_name || ' compile';
begin
execute immediate alter_sql;
end;
end loop;
end;
方法2:建议这种的方式,存储过程保持默认继承定义者权限,依旧使用db_admin用户创建存储过程!
如果db_admin只有dba权限,存储过程继承定义者权限的时候,会去掉其拥有的角色的权限,所以会去掉dba权限;所以需要给db_admin用户dba之外的权限,不会去掉的权限,如下所示:
SQL> grant alter any table to db_admin; 因为需要存储过程需要执行alter view,
Grant succeeded.
SQL> grant select any table to db_admin; #因为需要能查看别的用户下的view后,才能在all_objects中查看到别的用户的视图信息!
SQL> grant connect to db_admin; #因为后面需要收回dba,所以需要加connect权限
最后还可以回收dba权限;并且由于存储过程是自己账户下面的,自己的账号是可以执行的
SQL>revoke dba from db_admin;
方法3:这种方式不完美,因为只能从新编译sysadm用户的试图,当你传别的schema的时候,还是有问题!
在sysadm下创建对应的存储过程,这样继承了sysadm这个用户的权限,存储过程中也是可以查到自己的下面的试图的,所以问题就解决了,但是一旦别的用户下的试图有问题也是不能从新编译的!
create or replace procedure sysadm.compile_invalid_views(
p_owner varchar2
) as
--编译某个用户下的无效视图
str_sql varchar2(2000);
begin
for invalid_views in (select object_name from all_OBJECTS
where status = 'INVALID' and object_type = 'VIEW' and owner=upper(p_owner))
loop
alter_sql := 'alter view '||p_owner||'.' ||invalid_views.object_name || ' compile';
begin
execute immediate alter_sql;
end;
end loop;
end;
然后使用db_admin用户去调用这个存储过程!
exec sysadm.compile_invalid_views('sysadm');
综上所述:Oracle创建存储过程的时候,存储过程默认继承的是定义者,也就是创建者的权限,但是会去掉定义者拥有的 角色 ,这个需要大家注意!