oracle 11g PL/SQL Programming学习十四

----------------------------------------------------------------------------
-----------------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.使用JAVA共享库
oracle为外部存储构建了一个可扩展的架构.
它非常灵活地支持任何能被C调用的编程语言.
oracle直接支持java作为数据库的一部分.(除了oracle Express版)
java库不使用extproc,因为它们已经是本地oracle数据库的一部分了.
这简化了很多操作,但也带来了一些不便.(使用extproc代理和外部C库的时候)
java相比C,有几点优势:
  1.JAVA理解SQL类型.它避免了乏味的数据类型映射.
  2.JAVA是加载到oracle数据库里的.
    这样避免了文件管理问题和监听ENV参数进程,因为它不需要使用extproc代理.
  3.JAVA是本地线程安全的.
    它不需要你去处理那些线程的细微差别,避免了静态变量.
  4.JAVA不需要去管理内存地址.(也就是说JAVA没有指针)
java相比C,也有几点劣势:
  1.JAVA使用了SGA中的JAVA池来管理进程.而C外部存储使用它们自己的内存空间.
    换句话说就是C外部存储降低了SGA的内存消耗,而JAVA增加了SGA的负载.
  2.JAVA的速度比不上C.
    因为JAVA需要通过JVM来解释.
  3.JAVA限制对文件的访问.这是为了保护数据库的完整性.
    DBMS_JAVA包提供了一个方法来实现JAVA库程序的读写访问.
  4.使用java库的PL/SQL wrapper函数在方法定义上有限制.
    所有通过PL/SQL wrapper函数访问的JAVA class方法必须是静态的.
    这样支持PL/SQL wrapper函数的java库就不是线程安全的了.

下面来实现定义一个简单的java库.
首先,由于后面的程序包含了一个标准的I/O库,需要访问到外部物理文件.
所以需要先使用DBMS_JAVA.GRANT_PERMISSION来对相应目录进行授权.

01:51:44 SYS@ORCL>  BEGIN
01:52:16   2     DBMS_JAVA.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


 

然后建立JAVA库.(你需要配置JAVA环境,也就是需要JDK+环境变量)
提示:这里可以直接利用ORACLE_HOME/JDK/BIN路径下的JAVAC来编译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库)
注意JAVA库和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 JAVA 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外部库时,隐式转换到库的形式参数时发生的错误.

原文地址:https://www.cnblogs.com/dyllove98/p/3137478.html