在 Oracle 包体中使用 Pragma
我想创建一个 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您的问题与 PRAGMA 无关。正如 Rob 所说,现代 Oracle 版本会自动处理大部分内容。
问题是您无法从 SQL 语句中调用私有函数,即使是嵌入在同一包内的另一个子程序中的函数。当 PL/SQL 执行 SQL 时,它会被移交给 SQL 引擎执行,这实际上使您超出了包的范围,因此它无法访问私有成员。
这编译得很好——没有编译指示,但是将“私有”函数公开:
如果你想保持函数私有,你需要看看是否可以重写公共函数,以便完成对私有函数的调用SQL语句之外:
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:
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:
您的
function_private
仅在包主体中声明,因此其范围仅限于包中的其他过程。因此,它必须符合那些调用过程的纯度级别,否则编译器将抛出异常。比较这个安全声明(注意,我已经扩展了 function_public 的纯度)……
与这个不安全的声明……
RESTRICTS_REFERENCES pragma 的要点是在包规范中声明的过程可以被其他包使用,甚至是由其他用户(模式)拥有或执行的 SQL 语句,这些用户可能无权访问我们的包主体的源代码。编译指示是我们向他们保证将我们的代码合并到他们的代码中的影响的一种方法。这就是为什么必须在规范中声明 pragma,因为这是当我们将包上的 EXECUTE 权限授予另一个用户时暴露的代码的唯一部分。
编辑
啊,现在看到了您修改后的代码示例,我明白您想要做什么。它不会、不会、不能工作。我们只允许使用在 SQL 中的 spec = public function - 中声明的封装函数。 SQL 是用 SQL*Plus 编写还是用另一个打包过程编码并不重要。错误堆栈中的原因非常清楚:
编译器抛出
ORA-00904: invalididentifier
因为该函数未在规范中声明;它与纯度级别无关,关于范围的说明
PL/SQL 并不完全符合其作用域规则:我们可以在打包的 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
) ...... with this unsafe one ...
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:
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:
It's just functions and types which wm must declare in the spec if we want to use them in SQL statements.
您写“我想添加 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.
Oracle 会进行此检查。
以下代码无法编译,因为
function_public
具有编译指示RNDS
,并且它调用读取表的function_private
。从
function_private
中删除SELECT
并且它可以工作。Oracle does this check.
The following code does not compile, since
function_public
has the pragmaRNDS
, and it callsfunction_private
which reads a table.Remove the
SELECT
fromfunction_private
and it works.