您的位置:首页 > 数据库 > Oracle

oracle 11g PL/SQL Programming学习十四

2013-06-15 02:44 344 查看
----------------------------------------------------------------------------

-----------------PL/SQL学习笔记系列 By Cryking-----------------

------------------------转载请注明出处,谢谢!------------------------


4.使用C共享库

oracle为外部存储构建了一个可扩展的架构.

它非常灵活地支持任何能被C调用的编程语言.

定义C共享库(需要熟悉简单的C语言)

你将使用下面的C程序作为一个动态链接库(DLL)或共享库.

你需要安装一个C编译器来编译这个例子.

writestr1.c文件内容

/* Include standard IO. */
#include <stdio.h>
/* Declare a writestr function. */
void writestr(char *path, char *message){
/* Declare a FILE variable. */
FILE *file_name;
/* Open the file in write-only mode. */
file_name = fopen(path,"w");
/* Write to file the message received. */
fprintf(file_name,"%s\n",message);
/* Close the file. */
fclose(file_name);
}



--编译一个C共享库
[oracle@oracleserver oracle]$ vi writestr1.c
[oracle@oracleserver oracle]$ gcc -shared -o writestr1.so writestr1.c
[oracle@oracleserver oracle]$ ls *.so
writestr1.so
[oracle@oracleserver oracle]$ mkdir $ORACLE_HOME/customlib
[oracle@oracleserver oracle]$ mv writestr1.so /u01/app/oracle/product/11.2.0/dbhome_1/customlib/




你应当注意上面的C程序没有main函数,它只是作为一个DLL或共享库使用.

注意上面的编译方法(不要直接使用 gcc -o编译,否则会报缺失main函数).

你需要在$ORACLE_HOME目录下创建一个自定义目录customlib.

你需要保证用户或组对customlib目录有读、写、执行的权限.

定义和调用PL/SQL Library Wrapper

首先,定义PL/SQL库.

需要先在数据库定义外部库.(上面已经定义好,见listener.ora文件中的ENV参数.)

创建PL/SQL库语法原型为:

CREATE [OR REPLACE] LIBRARY <library_name> AS | IS

'<file_specification>'

AGENT '<agent_dblink>';

/

示例:

23:46:18 SYS@ORCL> CREATE OR REPLACE LIBRARY library_write_string AS
23:47:50   2  '/u01/app/oracle/product/11.2.0/dbhome_1/customlib/writestr1.so';
23:47:52   3  /

Library created.

Elapsed: 00:00:00.23



然后,建立PL/SQL library wrapper.

PL/SQL library wrapper主要用来定义一个数据库和外部存储的接口.

接口定义了PL/SQL参数和C数据类型的映射关系.

ORACLE提供额外的派生类型来支持OCI.

在PL/SQL和C数据类型转换之间存在以下限制:

1.PL/SQL有NULL变量,但在C中没有对应的值.

当一个变量可能为NULL时,你需要使用另外一个变量(指示器变量)来通知该变量是否为NULL。

你使用OCI_IND_NULL和OCI_IND_NOTNULL来检查指示器变量是否为NULL.

2.当数据交换时,C和PL/SQL都需要知道string字符串的长度.

在处理二进制字符串时,你应当使用VSIZE来获得长度.

3.当extproc代理运行在一个不同的数据库时,CHARSETID和CHARSETFORM受制于全球化的复杂性.

创建一个C Library Wrapper存储过程的语法:

CREATE [OR REPLACE] PROCEDURE name [parameter_list]

AS EXTERNAL

LIBRARY_NAME library_name

NAME "<external_library_name>"

AGENT IN [parameter_list]

WITH CONTEXT

PARAMETER [parameter_list];

下面的示例提出了一个比较独特的方法,使用隐式的SELF.

在PL/SQL,你不用去管理对象类型的SELF成员函数,因为它是隐式管理的.

如:

00:19:33 SYS@ORCL> CREATE OR REPLACE TYPE object_library_sample AS OBJECT(V NUMB
ER,member function get_tea_temperature return number);
00:21:06   2  /

Type created.

Elapsed: 00:00:00.45
--使用了上面创建好的library_write_string PL/SQL库
00:27:41 SYS@ORCL> CREATE OR REPLACE TYPE BODY object_library_sample AS
00:27:43   2    MEMBER FUNCTION get_tea_temperature RETURN NUMBER AS
00:27:43   3      LANGUAGE C NAME "tea_temp"
00:27:43   4      library library_write_string
00:27:43   5      WITH CONTEXT
00:27:43   6      PARAMETERS(CONTEXT,
00:27:43   7                 SELF   ,
00:27:43   8                 SELF    INDICATOR STRUCT,
00:27:43   9                 SELF    TDO,
00:27:43  10                 RETURN  INDICATOR);
00:27:43  11  END;
00:27:44  12  /

Type body created.

Elapsed: 00:00:00.19


创建PL/SQL外部存储.

00:27:45 SYS@ORCL> CREATE OR REPLACE PROCEDURE write_string(path VARCHAR2, message VARCHAR2) AS
01:00:25   2    EXTERNAL
01:00:25   3    LIBRARY library_write_string NAME "writestr"
01:00:25   4    PARAMETERS(path STRING, message STRING);
01:00:26   5  /

Procedure created.

Elapsed: 00:00:00.22



在你学会如何定义和配置PL/SQL wrapper前,你应当知道如何建立网络连接(配置监听)、编译共享库、代理配置等.

完成这些后,现在你要知道如何使用它。

--调用外部存储write_string
01:00:28 SYS@ORCL> BEGIN
01:05:19   2  write_string('/tmp/file.txt','Hello World!');
01:05:19   3  END;
01:05:21   4  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.29




根据之前建立的C共享库,可以知道执行该外部存储之后,会建立对应文件,写入传入的内容.

验证:

[oracle@oracleserver oracle]$ more /tmp/file.txt
Hello World!



使用外部存储时,有几点注意:

1.你不应使用全局变量,因为它们不是线程安全的.

2.你不应使用外部静态变量,因为它们不是线程安全的.

3.你只能在支持DLL或共享库的平台上使用外部存储.

4.你只能使用能被C调用的编程语言.

5.当你需要传递游标或记录类型到外部存储的时候,你必须使用对象来传递

6.你不能在PL/SQL wrapper的LIBRARY子句中使用DB_LINK.

7.最多传递128个参数.其中浮点类型或双精度类型算2个参数.



5.使用J***A共享库

oracle为外部存储构建了一个可扩展的架构.

它非常灵活地支持任何能被C调用的编程语言.

oracle直接支持java作为数据库的一部分.(除了oracle Express版)

java库不使用extproc,因为它们已经是本地oracle数据库的一部分了.

这简化了很多操作,但也带来了一些不便.(使用extproc代理和外部C库的时候)

java相比C,有几点优势:

1.J***A理解SQL类型.它避免了乏味的数据类型映射.

2.J***A是加载到oracle数据库里的.

这样避免了文件管理问题和监听ENV参数进程,因为它不需要使用extproc代理.

3.J***A是本地线程安全的.

它不需要你去处理那些线程的细微差别,避免了静态变量.

4.J***A不需要去管理内存地址.(也就是说J***A没有指针)

java相比C,也有几点劣势:

1.J***A使用了SGA中的J***A池来管理进程.而C外部存储使用它们自己的内存空间.

换句话说就是C外部存储降低了SGA的内存消耗,而J***A增加了SGA的负载.

2.J***A的速度比不上C.

因为J***A需要通过JVM来解释.

3.J***A限制对文件的访问.这是为了保护数据库的完整性.

DBMS_J***A包提供了一个方法来实现J***A库程序的读写访问.

4.使用java库的PL/SQL wrapper函数在方法定义上有限制.

所有通过PL/SQL wrapper函数访问的J***A class方法必须是静态的.

这样支持PL/SQL wrapper函数的java库就不是线程安全的了.

下面来实现定义一个简单的java库.

首先,由于后面的程序包含了一个标准的I/O库,需要访问到外部物理文件.

所以需要先使用DBMS_J***A.GRANT_PERMISSION来对相应目录进行授权.

01:51:44 SYS@ORCL>  BEGIN
01:52:16   2     DBMS_J***A.GRANT_PERMISSION('SCOTT',
01:52:16   3                                'SYS:java.io.FilePermission',
01:52:16   4                                '/home/oracle/file.txt',
01:52:16   5                                'read');
01:52:16   6   END;
01:52:18   7  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:28.79




然后建立J***A库.(你需要配置J***A环境,也就是需要JDK+环境变量)

提示:这里可以直接利用ORACLE_HOME/JDK/BIN路径下的J***AC来编译java文件.

编译ReadFile1.java,文件内容如下:

import java.io.*;

public class ReadFile1
{
// Convert the string to a file resource and call private method.
public static String readString(String s){
  return readFileString(new File(s)); }
// Read an external file.
  private static String readFileString(File file) {
// Define control variables.
  int c;
  String s = new String();
  FileReader inFile;
  try {
  inFile = new FileReader(file);
  while ((c = inFile.read()) != -1) {
  s += (char) c; }
     }
catch (IOException e) {
  return e.getMessage(); }
  return s; }
// Testing method.
public static void main(String[] args) {
  String file = new String("/tmp/file.txt");
  System.out.println(ReadFile1.readString(file)); }
}


将上面的java文件编译成ReadFile1.class后,使用loadjava工具将其加载到数据库中.

[oracle@oracleserver ~]$ ls ReadFile1*
ReadFile1.class
[oracle@oracleserver ~]$ loadjava -r -f -o -user scott/tiger ReadFile1.class
[oracle@oracleserver ~]$


没有提示,说明ReadFile1.class文件加载成功.



发布java库(也就是定义和使用PL/SQL Library Wrapper调用java库)

注意J***A库和PL/SQL也有数据类型的映射.LONG和LONG RAW数据类型最大只能为32K.

大多数类型的映射是简单的.

--建立函数,发布java类ReadFile1
02:14:34 SCOTT@orcl> CREATE OR REPLACE FUNCTION read_string(file IN VARCHAR2)
02:14:40   2  RETURN VARCHAR2 IS
02:14:40   3    LANGUAGE J***A NAME 'ReadFile1.readString(java.lang.String) return String';
02:14:43   4  /

Function created.

Elapsed: 00:00:00.23


使用函数read_string读取/home/oracle/file.txt文件的内容.

--先在文件中写点内容
[oracle@oracleserver ~]$ echo Just a test>>file.txt
[oracle@oracleserver ~]$ more file.txt
Just a test

--然后使用函数读取
02:21:20 SCOTT@orcl> SELECT read_string('/home/oracle/file.txt') a FROM dual;

A
---------------
Just a test

1 row selected.

Elapsed: 00:00:00.01


6.外部存储故障诊断

外部存储的失败通常是因为两种原因.

一是监听的配置,共享库的配置、或者环境的配置.

以下错误比较常见:

1.监听的ENV参数不正确.

2.文件路径问题

3.监听中EXTPROC_DLLS参数值问题

4.extproc监听不正确或没有运行

5.没有单独的extproc监听

6.PL/SQL Wrapper定义的NAME值不正确



二是外部程序的定义和PL/SQL wrapper的定义不同.

这一般是由于数据类型的不正确转换导致的.

如错误:

BEGIN

*

ERROR at line 1:

ORA-28576: lost RPC connection to external procedure agent

ORA-06512: at "PLSQL.WRITE_STRING", line 1

ORA-06512: at line 4

这种错误是由于数据类型不能隐式转换时发生.

也就是当你尝试使用实际参数来fork外部库时,隐式转换到库的形式参数时发生的错误.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: