透明数据加密
对于加密,许多用户深感矛盾:他们既感兴趣,又因意识密钥管理的复杂性而感到慎重,如果处理不当,则会导致设置的效率低下。加密和解密值还会带来相关的性能开销,这使得大部分应用程序架构师不太乐于接受该过程。结果是,很多系统设计根本没有加密,只是构筑了强大的外围防护,如强大的口令和适当的授权方案。
但是,请想象一下如果整个服务器被盗了,甚至只是磁盘被盗,这些磁盘可以装配在具有相同操作系统的服务器上,然后其中的数据将被销毁殆尽。或者有一个的 DBA 品行不端,在日常业务活动中恶意突破了外围防护,然后将您所有重要的客户信息洗劫一空。在这两种情况下,如果所涉及的商业机构是在加利福尼亚州(可能不久之后在美国的其他州),它们在法律上有责任将安全漏洞的情况通知给所有受到影响的客户。
在上述罕见(但确是事实)的情况中,认证方案没有实际意义。这就是为什么对于那些将安全作为头等大事的机构而言,透明数据加密 (TDE) 是一个如此有用的特性;它支持加密,同时将密钥管理的复杂性交给数据库引擎来处理。同时,它允许 DBA 在不必实际看到数据的情况下管理数据库表。
在 Oracle 数据库 10g 第 2 版中使用 TDE 时,可以随时地对表中的一列或多列进行加密;只需将列定义为加密形式即可,不用编写代码。请记住,加密需要使用密钥和算法对输入值进行加密。TDE 为特定的表生成单独的密钥。这种方法方便了密钥管理却也更易被他们窃取,所以数据库提供了另一种密钥 — 万能密钥 — ,它可以在数据库级别上设置。表密钥是利用万能密钥进行加密的,要获得表密钥就需要这个万能密钥。因此,对列进行解密时需要万能密钥和表密钥。
万能密钥存储在数据库外一个称为“钱夹”的地方 — 默认位置在 $ORACLE_BASE/admin/$ORACLE_SID/wallet。在概念上,它类似于下图。
create table accounts ( acc_no number not null, first_name varchar2(30) not null, last_name varchar2(30) not null, SSN varchar2(9) ENCRYPT USING 'AES128', acc_type varchar2(1) not null, folio_id number ENCRYPT USING 'AES128', sub_acc_type varchar2(30), acc_open_dt date not null, acc_mod_dt date, acc_mgr_id number ) |
alter system set encryption key authenticated BY "topSecret"; |
create table account_ext organization external ( type oracle_datapump default directory dump_dir location ('accounts_1_ext.dmp', 'accounts_2_ext.dmp', 'accounts_3_ext.dmp', 'accounts_4_ext.dmp') ) parallel 4 as select ACC_NO, FIRST_NAME, LAST_NAME, SSN ENCRYPT IDENTIFIED BY "topSecret", ACC_TYPE, FOLIO_ID ENCRYPT IDENTIFIED BY "topSecret", SUB_ACC_TYPE, ACC_OPEN_DT, ACC_MOD_DT from accounts; |
在文件 accounts_*_ext.dmp 中,SSN 和 FOLIO_ID 的值不会是明文,而是加密形式。如果您希望使用这些文件作为外部表,则必须提供 topSecret 作为口令以读取这些文件。
在这里您可以看到,TDE 是访问控制的理想补充(而不是替代)。
在 SQL 中查询 XML
长期以来,对于很多包含大量字符内容的应用程序的数据类型而言,XML 已成为事实上的标准。最近它也已成为其他应用程序的的存储方法,而不仅仅限于大量的内容。
Oracle 从 Oracle9i 数据库开始就提供 XML 与数据库的集成。在该版本中,您可以使用很多不同的方法来查询 XML 内容。在 Oracle 数据库 10g 第 2 版中,新的 XQuery 和 XMLTable 函数使查询 XML 内容变得更容易。#p#分页标题#e#
XQuery
首先,让我们来看这两种方法中较简单的一种:XQuery。请看下面的示例:
SQL> xquery 2 for $var1 in (1,2,3,4,5,6,7,8,9) 3 let $var2 := $var1 + 1 4 where $var2 < 6 5 order by $var2 descending 6 return $var2 7 / Result Sequence ------------------ 5 4 3 2 |
create table acc_comm_log ( acc_no number, comm_details xmltype ); |
insert into acc_comm_log values ( 1, xmltype( '<CommRecord> <CommType>EMAIL</CommType> <CommDate>3/11/2005</CommDate> <CommText>Dear Mr Smith</CommText> </CommRecord>') ) / insert into acc_comm_log values ( 2, xmltype( '<CommRecord> <CommType>LETTER</CommType> <CommDate>3/12/2005</CommDate> <CommText>Dear Mr Jackson</CommText> </CommRecord>') ); insert into acc_comm_log values ( 3, xmltype( '<CommRecord> <CommType>PHONE</CommType> <CommDate>3/10/2005</CommDate> <CommText>Dear Ms Potter</CommText> </CommRecord>') ); |
SQL> l 1 select acc_no, 2 XMLQuery( 3 'for $i in /CommRecord 4 where $i/CommType != "EMAIL" 5 order by $i/CommType 6 return $i/CommDate' 7 passing by value COMM_DETAILS 8 returning content) XDetails 9 from acc_comm_log 10 / ACC_NO XDETAILS ---------- ------------------------------ 1 2 <CommDate>3/12/2005</CommDate> 3 <CommDate>3/10/2005</CommDate> |
1 select t.column_value 2 from acc_comm_log a, 3 xmltable ( 4 'for $root in $date 5 where $root/CommRecord/CommType!="EMAIL" 6 return $root/CommRecord/CommDate/text()' 7 passing a.comm_details as "date" 8* ) t SQL> / COLUMN_VALUE --------------------- 3/12/2005 3/10/2005 |
此示例演示了如何将常规的 SQL 语句用于 XML 查询所返回的 XML 表。查询按照非常结构化的 FLOWR 模式来指定命令。
XQuery 与 XMLTable 的对比
既然您已经了解了在常规 SQL 查询中使用 XML 的两种方法,就让我们来看这二种方法适用的情形。
第一种方法 XQuery 允许您获取 XMLType 形式的数据,在任何支持它的程序或应用程序中都可以将其作为 XML 来处理。在您所看到的示例中,帐户数据的结果输出是 XML 格式,而您可以使用任何工具(不必是关系型工具)来处理和显示这些数据。第二种方法 XMLTable 结合了常规 SQL 和 XML 的功能。帐户数据的结果输出不是 XML 格式,而是关系型数据。
注意两个案例中的源代码都是 XML,但是 XQuery 使用 XMLType 来表示 XML 格式的数据,而 XMLTable 将其表示为关系表,可以像常规表一样进行处理。这种功能非常适用于要输出表的现有程序,它引入了 XML 的特性。
XML 在预先不太了解确切的数据结构的场合中非常有用。在以上示例中,根据不同模式,通信记录也不相同。如果是电子邮件,则属性可能是接收方的电子邮件地址、回复地址、任何复本(cc:、bcc: 等等)、消息的文本等等。如果是电话呼叫,则属性是所呼叫的电话号码、号码的类型(家庭、工作、移动电话等等)、应答者、留下的语音邮件等等。如果您要设计一个包含所有可能的属性类型的表,则它会包括很多列,并且极其冗长,造成读取困难。但是,如果您只有一个 XMLType 列,则可以将所有内容填在那里,但仍然保持通信类型的独特属性。查询仍然可以使用简单的 SQL 接口,使应用程序的开发变得轻而易举。#p#分页标题#e#
增强的 COMMIT
当提交会话时,将刷新重做日志缓冲区,将其内容存储到磁盘上的联机重做日志中。此过程确保在对数据库进行恢复操作时,可以根据需要利用重做日志中回放事务处理。
但是有时您可能想对一部分受保证的恢复能力进行调整,以获得更好的性能。利用 Oracle 数据库 10g 第 2 版,您现在可以控制如何将重做流写入到联机日志文件。您可以在执行提交语句时控制这种行为,或者只需更改数据库的默认行为即可。
让我们来看提交语句是工作过程。在事务处理后,当执行 COMMIT 时,可以附带一个子句:
COMMIT WRITE <option> |
COMMIT WRITE WAIT; |
COMMIT WRITE NOWAIT; |
COMMIT WRITE BATCH; |
COMMIT WRITE IMMEDIATE; |
ALTER SYSTEM SET COMMIT_WRITE = NOWAIT; |
ALTER SESSION SET COMMIT_WORK = NOWAIT; |
SQL> insert into accounts 2 select * from accounts_ny; insert into accounts * ERROR at line 1: ORA-00001:unique constraint (ARUP.PK_ACCOUNTS) violated |
exec dbms_errlog.CREATE_ERROR_LOG ('ACCOUNTS','ERR_ACCOUNTS') |
SQL> insert into accounts 2 select * from accounts_ny 3 log errors into err_accounts 4 reject limit 200 5 / 6 rows created. |
SQL> select ORA_ERR_NUMBER$, ORA_ERR_MESG$, ACC_NO 2 from err_accounts; ORA_ERR_NUMBER$ ORA_ERR_MESG$ ACC_NO --------------- -------------------------------------------------- ------ 1 ORA-00001:unique constraint (ARUP.PK_ACCOUNTS) vi 9997 olated 1 ORA-00001:unique constraint (ARUP.PK_ACCOUNTS)vi 9998 olated 1 ORA-00001:unique constraint (ARUP.PK_ACCOUNTS) vi 9999 olated 1 ORA-00001:unique constraint (ARUP.PK_ACCOUNTS) vi 10000 olated |
create or replace procedure p1 as begin null; end; |
begin dbms_ddl.create_wrapped ('create or replace procedure p1 as begin null; end;') end; / |
SQL> select text from user_source where name = 'P1'; Text ----------------------------------------------------------------- procedure p1 wrapped a000000 369 abcd abcd |
SQL> select dbms_ddl.wrap 2 ('create or replace procedure p1 as begin null; end;') 3 from dual 4 / DBMS_DDL.WRAP('CREATEORREPLACEPROCEDUREP1ASBEGINNULL;END;') ---------------------------------------------------------------------- create or replace procedure p1 wrapped a000000 369 abcd abcd ... and so on ... |
create or replace procedure myproc as l_key VARCHAR2(200); begin l_key := 'ARUPNANDA'; end; |
1 declare 2 l_input_code dbms_sql.varchar2s; 3 begin 4 l_input_code (1) := 'Array to hold the MYPROC'; 5 l_input_code (2) := 'create or replace procedure myproc as '; 6 l_input_code (3) := ' l_key VARCHAR2(200);'; 7 l_input_code (4) := 'begin '; 8 l_input_code (5) := ' l_key := ''ARUPNANDA'';'; 9 l_input_code (6) := 'end;'; 10 l_input_code (7) := 'the end'; 11 sys.dbms_ddl.create_wrapped ( 12 ddl => l_input_code, 13 lb => 2, 14 ub => 6 15 ); 16* end; |
在这里我们定义了一个变量 l_input_code 来保存输入的明文代码。在第 4 行到第 10 行中,我们用要打包的代码来填充这些行。在本示例中,同样为了简单起见,我使用了非常短的行。实际上,您可能要使用非常长的行,其大小多达 32KB。同样,我在数组中只使用了 7 个单元;实际上您可能要使用若干单元来填充全部代码。 第 11 到第 15 行表明我如何调用该过程,以便将该过程创建为打包形式。在第 12 行中,我将集合作为一个参数 DDL 来传递。但是,在这里暂停一下 — 我已经分配了一个注释作为数组的第一个单元,可能用于文档。但它不是有效的语法。同样,我将另一个注释分配给数组的最后一个单元 (7),它也不是用于创建过程的有效语法。为了使包装操作仅仅处理有效的行,我在第 13 和第 14 行中指定了存储我们代码的集合的最低 (2) 和最高 (6) 的单元。参数 LB 表示数组的下界,在本示例中是 2,而 HB 是上界 (6)。
使用这种方法,现在可以从您的 PL/SQL 代码中以打包方式创建任意大小的过程
PL/SQL 中的条件编译:一次编写,多次执行
你们中很多人曾经使用过 C 语言,它支持编译器指令的概念。在 C 程序中,根据相关编译器的版本,特定变量的值有可能不同。
在 Oracle 数据库 10g 第 2 版中,PL/SQL 有一个类似的特性:现在可以提供预处理器指令,它们在编译期间而不是在运行时进行求值。例如,让我们创建一个非常简单的返回字符串的函数。
1 create or replace function myfunc 2 return varchar2 3 as 4 begin 5 $if $$ppval $then 6 return 'PPVAL was TRUE'; 7 $else 8 return 'PPVAL was FALSE'; 9 $end 10* end; |
SQL> alter session set plsql_ccflags = 'PPVAL:TRUE'; Session altered. |
SQL> alter function myfunc compile; Function altered. SQL> select myfunc from dual; MYFUNC ------------------------------------- PPVAL was TRUE |
SQL> alter session set plsql_ccflags = 'PPVAL:FALSE'; Session altered. SQL> select myfunc from dual; MYFUNC --------------------------------------------------------- PPVAL was TRUE |
Function altered. SQL> select myfunc from dual; MYFUNC --------------------------------------------------- PPVAL was FALSE |
alter function calculate_interest compile plsql_ccflags = 'ACTIVE_STATUS_ONLY:TRUE' reuse settings; alter function apply_interest compile plsql_ccflags = FOREIGN_ACCOUNTS:TRUE' reuse settings; |
create or replace package debug_pkg is debug_flag constant boolean := FALSE; end; |
create or replace procedure myproc as begin $if debug_pkg.debug_flag $then dbms_output.put_line ('Debug=T'); $else dbms_output.put_line ('Debug=F'); $end end; |
SQL> exec myproc Debug=F |
create or replace package debug_pkg is debug_flag constant boolean := TRUE; end; |
SQL> exec myproc Debug=T |
ERROR at line 1: ORA-20000:ORU-10027:buffer overflow, limit of 1000000 bytes ORA-06512:at "SYS.DBMS_OUTPUT", line 32 ORA-06512:at "SYS.DBMS_OUTPUT", line 97 ORA-06512:at "SYS.DBMS_OUTPUT", line 112 ORA-06512:at line 2 |
SQL> show serveroutput serveroutput ON size 2000 format WORD_WRAPPED |
SQL> show serveroutput serveroutput ON SIZE UNLIMITED FORMAT WORD_WRAPPED |
ERROR at line 1: ORA-20000:ORU-10028:line length overflow, limit of 255 chars per line ORA-06512:at "SYS.DBMS_OUTPUT", line 35 ORA-06512:at "SYS.DBMS_OUTPUT", line 115 ORA-06512:at line 2 |
评论 {{userinfo.comments}}
{{child.content}}
{{question.question}}
提交