在 Oracle 包体中使用 Pragma

发布于 2024-09-01 02:34:37 字数 1630 浏览 4 评论 0原文

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

如果没有编译指示,代码将无法编译(PLS-00231:函数“FUNCTION_PRIVATE”可能无法在 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, WNPS 编译指示添加到 ,代码就会编译>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 也是公共的。是否有解决方案将编译指示添加到只能在包体中找到的函数?

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

更新2:按照 Rob van Wijk 的建议修复了代码中的错误。

I'd like to create an Oracle Package and two functions in it: A public function ( function_public ) and a private one ( function_private ). The public function uses the private one in an sql statement.

Without pragma the code does not compile (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;

The code compiles if I add WNDS, WNPS pragma to function_private. It seems to me pragma can be used only in the package declaration, and not in package body, so I have to declare function_private in the package as well:

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;

This solution makes my function_private public as well. Is there a solution to add pragma to a function which can be found only in the package body?

UPDATE: Replaced the pseudo-code with a working (simplified) example.

UPDATE2: Bugfixes in the code as suggested by Rob van Wijk.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

岁月无声 2024-09-08 02:34:37

您的问题与 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;

Your problem has nothing to do with PRAGMAs. As Rob says, modern Oracle versions handle most of this automatically.

The problem is you can't call private functions from a SQL statement, even ones embedded in another subprogram within the same package. When PL/SQL executes SQL, it is handed off to the SQL engine for execution, and that essentially takes you outside the scope of the package, so it has no access to private members.

This compiles fine -- no pragmas, but making the "private" function public:

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;

If you want to keep the function private, you need to see if you can rewrite the public function in such a way that the call to the private function is done outside the SQL statement:

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;
枯叶蝶 2024-09-08 02:34:37

您的 function_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 function - 中声明的封装函数。 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: invalididentifier 因为该函数未在规范中声明;它与纯度级别无关,

关于范围的说明

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>

这只是 wm 必须声明的函数和类型如果我们想在 SQL 语句中使用它们,请在规范中进行说明。

Your function_private is only declared in the package body, so its scope is restricted only to other procedures in your package. Consequently it will have to conform to the purity level of those calling procedures or else the compiler will hurl a exception.

Compare this safe declaration (note, I have extended the purity of the 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>

... with this unsafe one ...

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>

The point of the RESTRICTS_REFERENCES pragma is that procedures declared in the package spec can be used by other packages, even SQL statements, owned or executed by other users (schemas), who might not have access to our package body's source. The pragma is a method by which we offer assurances to them about the impact of incorporating our code in theirs. That's why the pragma has to be declared in the spec, because that is the only part of the code exposed when we grant EXECUTE on the package to another user.

edit

Ah, having now seen your revised code example I understand what you're trying to do. It doesn't, won't, can't work. We are only allowed to use packaged functions which have been declared in the spec = public functions - in SQL. It doesn't matter whether the SQL is written in SQL*Plus or coded in another packaged procedure. The reason why is quite clear in the error stack:

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>

The compiler hurls ORA-00904: invalid identifier because the function is not declared in the spec; it has nothing to do with purity levels,

a note about scope

PL/SQL is not entirely consist with regards to its scoping rules: we can use private variables in our packaged SQL statement:

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>

It's just functions and types which wm must declare in the spec if we want to use them in SQL statements.

伪心 2024-09-08 02:34:37

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

  • 您知道要在 SQL 语句中的何处使用该函数 AND

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

  • 您希望在编译时而不是运行时查找违规

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

话虽如此,如果将 TRUST 关键字添加到 function_public 的restrict_references 编译指示中,则可以省略 function_private 的restrict_references 编译指示。

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

问候,
抢。

You write "I'd like to add WNDS, WNPS pragma ...". Why do you like that? Since version 9 (I think) Oracle does this check for you. The only reason you might want to add the pragma yourself, is when:

  • you know where in a SQL statement you want to use the function AND

  • you know which purity levels are required for this use AND

  • you want to find violations at compile time instead of run time

The easiest option is to just skip all the pragma declarations altogether.

Having said that, you can omit the restrict_references pragma to function_private if you add the TRUST keyword to the restrict_references pragma of function_public.

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

Regards,
Rob.

不疑不惑不回忆 2024-09-08 02:34:37

Oracle 会进行此检查。

以下代码无法编译,因为 function_public 具有编译指示 RNDS,并且它调用读取表的 function_private

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

function_private 中删除 SELECT 并且它可以工作。


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;

Oracle does this check.

The following code does not compile, since function_public has the pragma RNDS, and it calls function_private which reads a table.

PLS-00452: Subprogram 'FUNCTION_PUBLIC' violates its associated pragma

Remove the SELECT from function_private and it works.


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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文