味着不再发生上下文切换。
3. 引用一个包中常量
尽管你能在SQL中调用包中函数,你却不能引用一个包中的常量(除非将SQL语句放在PL/SQL块中执行)。这里的例子展示了这个限制:
SQL> CREATE OR REPLACE PACKAGE pkg
? 2? IS
? 3? ? year_number?
? ? ? ? CONSTANT INTEGER := 2013;
? 4? END;
? 5? /
Package created.
SQL> SELECT pkg.year_number
FROM employees
? 2? WHERE employee_id = 138
? 3? /
SELECT pkg.year_number FROM employees
ERROR at line 1:
ORA-06553: PLS-221: 'YEAR_NUMBER' is not
a procedure or is undefined
经典的变通方案是在包中定义一个函数来返回这个常量(够拼的。。。(⊙﹏⊙))
SQL> CREATE OR REPLACE PACKAGE pkg
? 2? IS
? 3? ? FUNCTION year_number
? 4? ? ? ? RETURN INTEGER;
? 5? END;
? 6? /
Package created.
SQL> CREATE OR REPLACE PACKAGE BODY pkg
? 2? IS
? 3? ? c_year_number?
? ? ? ? CONSTANT INTEGER := 2013;
? 4
? 5? ? FUNCTION year_number
? 6? ? ? ? RETURN INTEGER
? 7? ? IS
? 8? ? BEGIN
? 9? ? ? ? RETURN c_year_number;
?10? ? END;
?11? END;
?12? /
Package body created.
SQL> SELECT pkg.year_number
? 2? ? FROM employees
? 3? WHERE employee_id = 138
? 4? /
YEAR_NUMBER
———————————
? ? ? 2013
为了引用一个常量多出了这么多代码!然而在Oracle 12c中,这种做法大可不必。我们只需要再WITH子句中创建自定义函数来返回包中的常量即可:
WITH
?FUNCTION year_number
?RETURN INTEGER
?IS
?BEGIN
? RETURN pkg.year_number;
?END;
SELECT year_number
? FROM employees
?WHERE employee_id = 138
这个WITH FUNCTION特性是对SQL语言非常有用的增强。然而你应该在用之前想一下这个问题:我需要在程序中多个地方用到它吗?
如果需要,你应该权衡WITH FUNCTION带来的性能提升和复制、粘贴这套逻辑到多个SQL语句的弊端。
4. 白名单和ACCESSIBLE BY子句
大多数基于PL/SQL的应用程序都是由许多包组成,其中一些是顶层(top level)API,供开发者调用实现用户需求而其他则是帮助包,仅被特定的包调用。
12c以前,PL/SQL无法阻止具有包执行权限的用户会话使用包中任一程序。自12c开始,相比之下,所有的PL/SQL程序单元都有一个ACCESSIBLE BY子句选项,目的在于指定哪一些程序单元可调用当前正在创建和修改的程序单元。
来看一个例子。首先我创建一个公共包说明,供其他开发者调用以创建应用程序。
CREATE OR REPLACE PACKAGE public_pkg
IS
? PROCEDURE do_only_this;
END;
/
接下来,我创建了我的“私有”包说明。并保证只允许公共包public_pkg调用。所以我增加了ACCESSIBLE BY子句。
CREATE OR REPLACE PACKAGE private_pkg?
? ACCESSIBLE BY (public_pkg)
IS
? PROCEDURE do_this;
? PROCEDURE do_that;
END;
/
现在,是时候实现包体了。Public_pkg.do_only_this过程调用private_pkg子程序。
CREATE OR REPLACE PACKAGE BODY public_pkg
IS
? PROCEDURE do_only_this
? IS
? BEGIN
? ? ? private_pkg.do_this;
? ? ? private_pkg.do_that;
? END;
END;
/
CREATE OR REPLACE PACKAGE BODY
private_pkg
IS
? PROCEDURE do_this
? IS
? BEGIN
? ? ? DBMS_OUTPUT.put_line ('THIS');
? END;
? PROCEDURE do_that
? IS
? BEGIN
? ? ? DBMS_OUTPUT.put_line ('THAT');
? END;
END;
/
现在可以毫无问题的运行这个公共包的过程:
BEGIN
? public_pkg.do_only_this;
END;
/
THIS
THAT
但是如果我试图在匿名块中调用私有包的子过程,出现以下错误:(呦吼!耍不了赖了!嗯哼,有意思!)
BEGIN
? private_pkg.do_this;
END;
/
ERROR at line 2:
ORA-06550: line 2, column 1:
PLS-00904: insufficient privilege to
access object PRIVATE_PKG
ORA-06550: line 2, column 1:
PL/SQL: Statement ignored
程序试图调用私有包的子程序,编译则会报同样的错误:
SQL> CREATE OR REPLACE PROCEDURE
use_private
? 2? IS
? 3? BEGIN
? 4? ? private_pkg.do_this;
? 5? END;
? 6? /
Warning: Procedure created with
compilation errors.
SQL> SHOW ERRORS
Errors for PROCEDURE USE_PRIVATE:
LINE/COL ERROR
———————— ——————————————————————————
4/4? ? ? PL/SQL: Statement ignored
4/4? ? ? PLS-00904: insufficient
? ? ? ? privilege to access object
? ? ? ? PRIVATE_PKG
看好了,是“PLS”错误提示,这个问题将在编译期间即被捕捉。使用这个特性不会带来任何的运行时性能影响。
5. 将角色授权给程序单元
12c以前,一个定义者权限的程序单元(以AUTHID DEFINER定义或不指定)总是以单元所有者的权限执行。一个调用者权限程序单元(以AUTHID CURRENT_USER定义)总是以单元调用者的权限执行。
这种设置的一个结果是,如果一个程序需要被所有用户执行,那么该程序将被设置为定义者权限单元。这样一来将拥有定义者所有权限来执行程序单元,从安全角度来看不是很好。
自12c起,你可以将角色授权给PL/SQL包和模式级过程和函数。基于角色权限的程序单元使开发者更细致地分配相应的程序单元给调用者。
你现在可以定义一个调用者权限的程序单元,然后通过授权有限的权限给相应角色来补足调用者权限。
让我们来走查以下例子,展示如何授权角色给程序单元。假设HR模式包含departments和employees表,定义和填充数据如下:
CREATE TABLE d