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 函数知道数据是不是为素数。收到警告:创建时出现编译错误的函数

Oracle Forms 编译器将类型标记为函数

限制 Oracle 创建编译错误程序

在 Oracle 11g2 XE 中编译 PL/SQL 函数有时会导致 ORA-00600:内部错误

Oracle使用java函数

MySQL 存储函数不会使用 MODIFIES SQL DATA 编译