Oracle 函数不会编译
Posted
技术标签:
【中文标题】Oracle 函数不会编译【英文标题】:Oracle function will not compile 【发布时间】:2020-08-01 19:25:24 【问题描述】:跟随 oracle 文档:https://oracle-base.com/articles/misc/pipelined-table-functions
“内存使用比较”部分提供以下功能,但失败 编译时出现错误:ORA-00942:表或视图不存在
CREATE OR REPLACE FUNCTION get_stat (p_stat IN VARCHAR2) RETURN NUMBER AS
l_return NUMBER;
BEGIN
SELECT ms.value
INTO l_return
FROM v$mystat ms,
v$statname sn
WHERE ms.statistic# = sn.statistic#
AND sn.name = p_stat;
RETURN l_return;
END get_stat;
以下查询运行成功。
select * from v$mystat;
select * from v$statname;
SELECT ms.value
FROM v$mystat ms,
v$statname sn
WHERE ms.statistic# = sn.statistic#
AND sn.name = 'session pga memory';
此外,以下匿名块可以正常工作。
declare
l_return NUMBER;
BEGIN
SELECT ms.value
INTO l_return
FROM v$mystat ms,
v$statname sn
WHERE ms.statistic# = sn.statistic#
AND sn.name = 'session pga memory';
dbms_output.put_line('l_return: ' || l_return);
END;
可能出了什么问题?
【问题讨论】:
【参考方案1】:虽然 oracle-base 是一个非常不错的网站,提供了很好的教程示例,但它绝对不是“oracle docs”。这些可以在https://docs.oracle.com/en/database/oracle/oracle-database/index.html找到。
至于您的错误,直接原因是 1)引用的表不存在,或者 2)您没有权限。由于存储过程之外的查询有效,因此我们知道该表确实存在。所以这是一个权限问题,这是由于通过角色继承的权限不适用于存储过程。您需要将权限直接授予用户。
【讨论】:
【参考方案2】:您似乎获得了通过角色访问这些表(视图)的权限。你是否?如果是这样,它将无法在命名 PL/SQL 过程(或函数)中工作,但可以在匿名过程(如您所见)中工作。
为了使其在命名过程中工作,您必须直接获得权限。
【讨论】:
【参考方案3】:之前的答案中已经提供了一些非常重要的主题,但我想花时间让我在一个测试用例场景中指导您,您将了解为什么您的一些示例有效而其他示例无效,当他们参考相同的元素(Oracle V$ Views),但您也会了解角色、系统权限和所有权之间的区别。
测试用例
我创建了一个名为 test1
的测试用户,具有一些基本权限 create procedure and create table
SQL> create user test1 identified by "Oracle_1" default tablespace tbtest temporary tablespace temp_group account unlock profile default quota unlimited on tbtest;
User created
SQL> grant create table, create procedure to test1;
Grant succeeded.
SQL> create table t ( c1 number , c2 number ) ;
Table created.
SQL> insert into t values ( 1 , 1 ) ;
1 row created.
现在我将创建一个将使用 v$views 的过程和函数。为此,我授予 test1 的 select any 字典系统权限。
SQL> create or replace procedure p
2 is
3 n integer;
4 begin
5 select count(*) into n from v$session where status = 'ACTIVE' ;
6* end;
/
Procedure created.
SQL> create or replace function f_get_count return number
is
n integer;
begin
select count(*)
INTO n
FROM v$mystat ms,
v$statname sn
WHERE ms.statistic# = sn.statistic#
AND sn.name = 'session pga memory' ;
return n;
end;
/
Function created
所以现在我们有了一个可以访问 v$ 视图的用户,因为它已被授予 SELECT ANY DICTIONARY 系统特权,我们是否应该检查一切是否确实按预期工作。
SQL> show user
USER is "TEST1"
SQL> select count(*) from v$session ;
COUNT(*)
----------
62
SQL> select count(*) from v$mystat ;
COUNT(*)
----------
1804
SQL> select count(*) from v$statname ;
COUNT(*)
----------
1804
现在让我们尝试使用这些过程。
SQL> select f_get_count from dual ;
F_GET_COUNT
-----------
1
SQL> exec p ;
PL/SQL procedure successfully completed.
在上述情况下,运行过程的用户是所有者,因此它从授予他的创建过程特权继承了自己过程的执行。他也可以成功运行程序和函数,即使包含 v$ 视图,因为用户已被直接授予系统权限选择任何字典。
但是,这同样不适用于 SELECT_CATALOG_ROLE。
SQL> grant select_catalog_role to test1 ;
Grant succeeded.
SQL> select f_get_count from dual ;
select f_get_count from dual
*
ERROR at line 1:
ORA-06575: Package or function F_GET_COUNT is in an invalid state
SQL> exec p ;
BEGIN p ; END;
*
ERROR at line 1:
ORA-06550: line 1, column 7:
PLS-00905: object TEST1.P is invalid
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
SQL> select count(*) from v$session ;
COUNT(*)
----------
63
如您所见,过程和函数不再起作用,事实上它们变为无效。但是,用户仍然可以看到这些表,因为此权限已由 SELECT_CATALOG_ROLE 授予。
如果您直接授予对 v$session、v$mystat 和 v$statname 的权限,一切都会按预期恢复工作。
SQL> grant select on v_$mystat to test1 ;
Grant succeeded.
SQL> grant select on v_$session to test1 ;
Grant succeeded.
SQL> grant select on v_$statname to test1 ;
Grant succeeded.
SQL> alter function f_get_count compile ;
Function altered.
SQL> alter procedure p compile ;
Procedure altered.
SQL> exec p;
PL/SQL procedure successfully completed.
SQL> select f_get_count from dual ;
F_GET_COUNT
-----------
1
如果过程的所有者是执行它的人,您可以授予 select any dictionary 系统权限,尽管我不推荐这样做。授予此特权时必须格外小心。
尝试直接授予对真实 v$views 的权限。最好的方案是创建一个拥有权限和过程的虚拟/容器用户,因此您只需要将过程的执行权限授予其余用户。
【讨论】:
罗伯托——感谢您的绝对惊人的回答! 我没有足够的声望点来投票。 :-(以上是关于Oracle 函数不会编译的主要内容,如果未能解决你的问题,请参考以下文章
让 oracle 函数知道数据是不是为素数。收到警告:创建时出现编译错误的函数