外部程序其实是储存在DLL或是共享库中的二进制程序,并且可以通过PL/SQL声明从存储程序中访问得到它。这既使得它们成为了存储程序开发者们最强大的开发工具之一。
但是,事实上人们很少使用这些外部程序,也许是因为人们觉得创建DLL和共享库,安装程序以及引用PL/SQL这一系列过程让人觉得非常繁琐。而对应的文档并不能起到什么帮助作用,因为这些文档提供的都是一些深奥的例子,它描述了使用的变量以及子程序使问题变得更加令人费解。
你可以在许多不同的程序编译语言中来创建DLL以及共享库,你也可通过转化脚本语言为二进制代码来实现DLL以及共享库的创建。DLL与共享库作为独立与操作系统的一部分,其实就是像调用执行文件的一部分那样被连接和调用的具有公共入口的二进制映像。这里有一个用C描述的用于二进制处理的DLL:
/* bitop.c */
#ifdef WIN32
#define DLLEXP __declspec(dllexport)
#else
#define DLLEXP
#endif /* WIN32 */
DLLEXP int bitand(int r,int l) { return r & l; }
DLLEXP int bitor(int r,int l)
{ return r | l; }
DLLEXP int bitxor(int r,int l) { return r ^ l; }
DLLEXP int bitshr(int n,int s) { return n << s; }
DLLEXP int bitshl(int n,int s) { return n s; }
DLLEXP int bitset(int n,int b) { return n | (1<<b); }
DLLEXP int bitclr(int n,int b) { return n ^ (1<<b); }
DLLEXP int bittst(int n,int b) { return (n & (1<<b)) ? 1 : 0; }
其中唯一超出标准C范围的就是DLLEXP宏,他为Windows提供了这些函数名并且可能被UNIX所忽视。将这些资源代码与你的编译文档相对照,从而你可以从中了解更多关于如何创建DLL的信息。在UNIX环境下使用GNU编译器的话,则包括以下命令行:
GNU C/C++: cc -shared -o libbitop.so.1 bitop.c
从数据库中调用DLL和共享库的下一步就是使用CREATE LIBRARY命令,并给出完整的路径,例如:
CREATE OR REPLACE LIBRARY SCOTT.bitop AS '/home/scott/bitop/libbitop.so.
通常只有DBA账号拥有执行这个命令的权限,但此命令可通过其他用户的行为发出。
然后,这个用户能调用PL/SQL和外部程序来调用任何DLL和共享库,代码如下:
create or replace package bit_op
as
function bit_and(l pls_integer,r pls_integer) return pls_integer
as language c name "bitand" library bitop;
--
function bit_or(r pls_integer,l pls_integer) return pls_integer
as language c name "bitor" library bitop;
--
function bit_xor(r pls_integer,l pls_integer) return pls_integer
as language c name "bitxor" library bitop;
--
function bit_shr(n pls_integer,s pls_integer) return pls_integer
as language c name "bitshr" library bitop;
--
function bit_shl(n pls_integer,s pls_integer) return pls_integer
as language c name "bitshl" library bitop;
--
function bit_set(n pls_integer,b pls_integer) return pls_integer
as language c name "bitset" library bitop;
--
function bit_clr(n pls_integer,b pls_integer) return pls_integer
as language c name "bitclr" library bitop;
--
function bit_tst(n pls_integer,b pls_integer) return pls_integer
as language c name "bittst" library bitop;
end bit_op;
/
注意这个过程不需要程序包。如果数据库已经设立好可以接受通过监听器发出的外部程序请求,那么这个过程就完成了。然后你就可以使用以下的查询命令:
select bit_op.bit_and(24,56) from dual where bit_op.bit_tst(24,5) = 1;
如果你没有建立好数据库来接收外部指令请求,那么你必须配置其他的监听器来完成内部联接。
外部程序请求将通过SQL*Net发送给专门的监听服务。再Oracle8i中,许多这种过程都是手工处理的。而在Oracle9i中,大部分可以通过设置完成了。
即设在你的数据库中存在tnsnames.ora这样一个文件,你要确保你有能告诉客户端怎样联接到数据库并发出外部请求的设置,设置如下:
EXTPROC_CONNECTION_DATA.world =
(DESCRIPTION=
(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC_KEY))
(CONNECT_DATA=(SID=EXTPROC_AGENT))
)
请注意,EXTPROC_CONNECTION_DATA,这个名字是强制不变的。而.world则需要设置为与你的数据库具有相同的域。同时EXTPROC_KEY和EXTPROC_AGENT则必须与你的listener.ora文件中的设置相匹配。其中listener.ora文件设置如下:
EXTERNAL_PROCEDURE_LISTENER =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL=IPC)(KEY=EXTPROC_KEY))
)
SID_LIST_EXTERNAL_PROCEDURE_LISTENER =
(SID_LIST =
(SID_DESC =
(SID_NAME=EXTPROC_AGENT)
(ORACLE_HOME = c:\oracle\ora81)
(PROGRAM = EXTPROC)
)
)
在Oracle9i数据库中,EXTPROC_CONNECTION_DATA应该已经被定义用于连接PLSExtProc,并且能在你自己的外部请求中使用。但是,任何没有在监听方的环境变量EXTPROC_DLL 中明确指出的DLL,Oracle9i都拒绝了对其的访问权,从而增加了一些附加安全定义。在外部过程中,这个变量需要使用SID_DESC中的ENV参数,具体代码如下:
SID_LIST_LISTENER =
(SID_LIST =
(SID_DESC =
(SID_NAME = PLSExtProc)
(ORACLE_HOME = /u01/app/oracle/product/9.2.0)
(PROGRAM = extproc)
(ENVS="EXTPROC_DLLS=ANY")
)
(SID_DESC =
(GLOBAL_DBNAME = ikan.us.oracle.com)
(ORACLE_HOME = /u01/app/oracle/product/9.2.0)
(SID_NAME = ikan)
)
)
为了得到更好的安全性,变量EXTPROC_DLLS可以设置为DLL序列或是共享库序列。也可以设为ANY从而与叙访问任何DLL和共享库。你可以通过一下这条命令手工的进行连接测试: