4

我想在其中创建一个 Oracle 包和两个函数:一个公共函数 ( function_public) 和一个私有函数 ( function_private)。公共函数在 sql 语句中使用私有函数。

没有编译指示,代码无法编译 ( PLS-00231: function 'FUNCTION_PRIVATE' may not be used in SQL)

CREATE OR REPLACE PACKAGE PRAGMA_TEST AS
  FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2;
END PRAGMA_TEST;

CREATE OR REPLACE PACKAGE BODY PRAGMA_TEST AS
  FUNCTION function_private(y IN VARCHAR2) RETURN VARCHAR2 IS
  BEGIN
     return 'z';
  END;

  FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2 IS
  ret VARCHAR2(100);
  BEGIN
     SELECT 'x' || function_private(x) INTO ret FROM dual;
     return ret;
  END;
END PRAGMA_TEST;

如果我将WNDS, WNPSpragma 添加到function_private. 在我看来 pragma 只能在包声明中使用,不能在包体中使用,所以我也必须function_private在包中声明:

CREATE OR REPLACE PACKAGE PRAGMA_TEST AS
  FUNCTION function_private(y IN VARCHAR2) RETURN VARCHAR2;
  PRAGMA RESTRICT_REFERENCES( function_private, WNDS, WNPS);
  FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2;
END PRAGMA_TEST;

CREATE OR REPLACE PACKAGE BODY PRAGMA_TEST AS
  FUNCTION function_private(y IN VARCHAR2) RETURN VARCHAR2 IS
  BEGIN
     return 'z';
  END;

  FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2 IS
  ret VARCHAR2(100);
  BEGIN
     SELECT 'x' || function_private(x) INTO ret FROM dual;
     return ret;
  END;
END PRAGMA_TEST;

这个解决方案也让我function_private公开。是否有解决方案将编译指示添加到只能在包主体中找到的函数?

更新:用一个工作(简化)示例替换了伪代码。

UPDATE2:Rob van Wijk 建议的代码中的错误修复。

4

4 回答 4

8

您的问题与 PRAGMA 无关。正如 Rob 所说,现代 Oracle 版本会自动处理其中的大部分内容。

问题是您不能从 SQL 语句中调用私有函数,即使是嵌入在同一包中的另一个子程序中的私有函数。 当 PL/SQL 执行 SQL 时,它被移交给 SQL 引擎执行,这实际上将您带到了包的范围之外,因此它无法访问私有成员。

这编译得很好——没有编译指示,但是将“私有”函数公开:

CREATE OR REPLACE PACKAGE PRAGMA_TEST AS
  FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2;
  FUNCTION function_private(y IN VARCHAR2) RETURN VARCHAR2;
END PRAGMA_TEST;


CREATE OR REPLACE PACKAGE BODY PRAGMA_TEST AS
  FUNCTION function_private(y IN VARCHAR2) RETURN VARCHAR2 IS
  BEGIN
     return 'z';
  END;

  FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2 IS
     ret VARCHAR2(30);
  BEGIN
     SELECT 'x' || function_private(x) INTO ret FROM dual;
     RETURN ret;
  END;
END PRAGMA_TEST;

如果你想保持函数私有,你需要看看你是否可以重写公共函数,使得对私有函数的调用在 SQL 语句之外完成:

CREATE OR REPLACE PACKAGE PRAGMA_TEST AS
  FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2;
END PRAGMA_TEST;


CREATE OR REPLACE PACKAGE BODY PRAGMA_TEST AS
  FUNCTION function_private(y IN VARCHAR2) RETURN VARCHAR2 IS
  BEGIN
     return 'z';
  END;

  FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2 IS
     ret VARCHAR2(30);
  BEGIN
     ret := function_private(x);
     SELECT 'x' || ret INTO ret FROM dual;
     RETURN ret;
  END;
END PRAGMA_TEST;
于 2010-05-10T13:23:57.317 回答
2

Yourfunction_private仅在包体中声明,因此其范围仅限于包中的其他过程。因此,它必须符合那些调用过程的纯度级别,否则编译器将抛出异常。

比较这个安全声明(注意,我已经扩展了 的纯度function_public)......

SQL> CREATE OR REPLACE PACKAGE PRAGMA_TEST AS
  2    FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2;
  3    PRAGMA RESTRICT_REFERENCES( function_public, WNDS, WNPS, RNDS);
  4  END PRAGMA_TEST;
  5  /

Package created.

SQL> CREATE OR REPLACE PACKAGE BODY PRAGMA_TEST AS
  2    FUNCTION function_private(y IN VARCHAR2) RETURN VARCHAR2 IS
  3    BEGIN
  4       return 'no harm done';
  5    END;
  6
  7    FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2 IS
  8    BEGIN
  9       return function_private(x);
 10    END;
 11  END PRAGMA_TEST;
 12  /

Package body created.

SQL>

……有了这个不安全的……

SQL> CREATE OR REPLACE PACKAGE BODY PRAGMA_TEST AS
  2    FUNCTION function_private(y IN VARCHAR2) RETURN VARCHAR2 IS
  3       rv varchar2(1);
  4    BEGIN
  5       select dummy into rv from dual;
  6       return rv;
  7    END;
  8
  9    FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2 IS
 10    BEGIN
 11       return function_private(x);
 12    END;
 13  END PRAGMA_TEST;
 14  /

Warning: Package Body created with compilation errors.

SQL> sho err
Errors for PACKAGE BODY PRAGMA_TEST:

LINE/COL ERROR
-------- -----------------------------------------------------------------
9/3      PLS-00452: Subprogram 'FUNCTION_PUBLIC' violates its associated
         pragma

SQL>

RESTRICTS_REFERENCES pragma 的要点是包规范中声明的过程可以被其他包使用,甚至是 SQL 语句,由其他用户(模式)拥有或执行,这些用户可能无法访问我们的包主体的源代码。编译指示是我们向他们保证将我们的代码合并到他们的代码中的影响的一种方法。这就是为什么必须在规范中声明 pragma 的原因,因为当我们将包上的 EXECUTE 授予另一个用户时,这是代码中唯一暴露的部分。

编辑

啊,现在看到您修改后的代码示例,我了解您要做什么。它不会,不会,不能工作。我们只允许在 SQL 中使用 spec = public functions 中声明的打包函数。SQL 是用 SQL*Plus 编写的,还是用另一个封装过程编码的,都没有关系。错误堆栈中的原因很清楚:

SQL> CREATE OR REPLACE PACKAGE PRAGMA_TEST AS
  2        FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2;
  3        PRAGMA RESTRICT_REFERENCES( function_public, WNDS, WNPS);
  4  END PRAGMA_TEST;
  5  /

Package created.

SQL> CREATE OR REPLACE PACKAGE BODY PRAGMA_TEST AS
  2    FUNCTION function_private(y IN VARCHAR2) RETURN VARCHAR2 IS
  3       rv varchar2(1);
  4    BEGIN
  5       select dummy into rv from dual;
  6       return rv;
  7    END;
  8
  9    FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2 IS
 10       rv varchar2(1);
 11    BEGIN
 12       select function_private(x) into rv from dual;
 13       return rv;
 14    END;
 15  END PRAGMA_TEST;
 16  /

Warning: Package Body created with compilation errors.

SQL> sho err
Errors for PACKAGE BODY PRAGMA_TEST:

LINE/COL ERROR
-------- -----------------------------------------------------------------
12/6     PL/SQL: SQL Statement ignored
12/13    PL/SQL: ORA-00904: : invalid identifier
12/13    PLS-00231: function 'FUNCTION_PRIVATE' may not be used in SQL
SQL>

编译器崩溃ORA-00904: invalid identifier是因为该函数未在规范中声明;它与纯度水平无关,

关于范围的说明

PL/SQL的作用域规则并不完全一致:我们可以在打包的 SQL 语句中使用私有变量:

SQL> CREATE OR REPLACE PACKAGE BODY PRAGMA_TEST AS
  2
  3    gv constant varchar2(8) := 'global';
  4
  5    FUNCTION function_private(y IN VARCHAR2) RETURN VARCHAR2 IS
  6       rv varchar2(1);
  7    BEGIN
  8       select dummy into rv from dual;
  9       return rv;
 10    END;
 11
 12    FUNCTION function_public(x IN VARCHAR2) RETURN VARCHAR2 IS
 13       rv varchar2(10);
 14    BEGIN
 15       select gv||'+'||dummy into rv from dual;
 16       return rv;
 17    END;
 18  END PRAGMA_TEST;
 19  /

Package body created.

SQL>

如果我们想在 SQL 语句中使用 wm 必须在规范中声明的函数和类型。

于 2010-05-10T10:39:06.273 回答
1

你写“我想添加 WNDS、WNPS pragma ...”。你为什么喜欢这样?由于版本 9(我认为)Oracle 会为您进行此项检查。您可能想要自己添加编译指示的唯一原因是:

  • 您知道要在 SQL 语句中的哪个位置使用函数 AND

  • 您知道此用途需要哪些纯度级别并且

  • 你想在编译时而不是运行时发现违规

最简单的选择是完全跳过所有的编译指示声明。

话虽如此,如果您将 TRUST 关键字添加到 function_public 的 restrict_references pragma,则可以省略对 function_private 的 restrict_references pragma。

http://download.oracle.com/docs/cd/B10501_01/appdev.920/a96590/adg10pck.htm#21958

问候,罗布。

于 2010-05-10T09:06:14.277 回答
0

Oracle 会进行此项检查。

以下代码无法编译,因为function_public有 pragma RNDS,并且它调用function_private读取表。

PLS-00452:子程序“FUNCTION_PUBLIC”违反了其相关的编译指示

删除SELECTfromfunction_private并且它可以工作。


CREATE OR REPLACE PACKAGE pragma_test AS
  FUNCTION function_public RETURN VARCHAR2;
  PRAGMA RESTRICT_REFERENCES( function_public, RNDS );
END pragma_test;

CREATE OR REPLACE PACKAGE BODY pragma_test AS
  FUNCTION function_private RETURN VARCHAR2 IS
    v_return dual.dummy%TYPE;
  BEGIN
     SELECT dummy INTO v_return FROM dual;
     RETURN v_return;
  END;
  --
  FUNCTION function_public RETURN VARCHAR2 IS
    v_return dual.dummy%TYPE;
  BEGIN
     RETURN function_private;
  END;
END pragma_test;
于 2010-05-10T08:37:40.740 回答