PRO*C中LOB字段的处理(转)
PRO*C中LOB字段的处理
和普通类型的字段相比,LOB字段的处理是相对比较麻烦的,在C语言等宿主语言中如何操作LOB字段呢?从ORACLE 8I开始,ORACLE数据库在嵌入式语言中提供了多种处理LOB字段的方法。在ORACLE 8I以后的版本中,可以使用日下3个方法来处理LOB字段:
l 在PL/SQL块中调用DBMS_LOB 包来处理LOB字段
l 使用OCI函数
l 使用嵌入式SQL语句来操作LOB字段
以下是在Pro*C/C++中处理LOB字段的语句:
l APPEND:在一个LOB后面添加一个LOB:exec sql lob append :src to:dst;
l ASSIGN:把LOB或者BFILE Locator付给另外一个值:exec sql lob assign :src to:dst;
l CLOSE:关闭lob或者bfile:EXEC SQL LOB CLOSE :src;
l COPY:把一个LOB值拷贝到另外一个LOB:EXEC SQL LOB COPY :amt FROM :src [AT :src_offset] TO :dst [AT dst_offset];
l CREATE TEMPORARY:创建一个临时LOB:EXEC SQL LOB CREATE TEMPORARY :SRC;
l ERASE:从某个偏移两开始清楚一个LOB数据:EXEC SQL ERASE :amt FROM :src [AT :src_offset];
l FILE CLOSE ALL:把当前session打开的BFILE全部关闭:EXEC SQL LOB FILE CLOSE ALL;
l FILE SET:在一个BFILE中设置DIRECTORY 别名和FILENAME:EXEC SQL LOB FILE SET :file DIRECTORY=:alias,FILENAME=:filename;
l FREE TEMPORARY:释放临时LOB的空间:EXEC SQL LOB FREE TEMPORARY :src;
l LOAD FROM FILE:把BFILE的一部分数据装载到一个内部LOB中:EXEC SQL LOB LOAD :amt FROM FILE :file [AT :src_offset] INTO :dst [AT :dst_offset];
l OPEN:打开一个BFILE:EXEC SQL LOB OFEN :src [READ ONLY|READ WRITE];
l READ:把LOB活BFILE读入一个缓冲区:EXEC SQL LOB READ :amt FROM :srt [AT :src_offset] INTO :buffer [WITH LENGTH :buflen];
l TRIM:截断LOB的值:EXEC SQL LOB TRIM :src TO :newlen;
l WRITE:把一个buffer写入LOB:EXEC SQL LOB WRITE [APPEND] [FIRST|NEXT|LAST|ONE] :amt FROM :buffer [WITH LENGTH :buflen] INTO :dst [AT :dst_offset];
l DESCRIBE:获得LOB的属性:EXEC SQL LOB DESCRIBE :src GET attribute1[{,attributeN}] INTO :hv1 [[INDICATOR] :HV_IND1][{,hvN[[INDICATOR] :hv_indN]}];
可以查看的LOB属性包括:
l CHUNKSIZE:存放的LOB的段的大小
l DIRECTORY:BFILE的目录名
l FILEEXISTS:BFILE的文件是否存在
l FILENAME:BFILE的文件名
l ISOPEN:BFILE是否处于打开状态
l ISTEMPORARY: LOB是否临时的
l LENGTH:LOB活BFILE的大小(BFILE和BLOB是字节数,CLOB和NCLOB是字符数)
下面通过一个实际的例子来说明如何使用上面提供的嵌入式语句来实现对LOB字段的处理。 要使用下面的例子,需要又CREATE ANY DIRECTORY和DROP ANY DIRECTORY权限。如果没有安装utl_raw包,可以通过执行:
$ORACLE_HOME/rdbms/admin/utlraw.sql
$ORACLE_HOME/rdbms/admin/utlraw.plb
来安装这个包。首先创建相关的测试表:
以下是lob_test.sql的脚本
-- lob_test.sql scripts
--运行以下的脚本可以创建下面的测试pro*c程序所需要使用的表和目录
drop table lob_table;
create table lob_table (key number, a_blob BLOB, a_clob CLOB);
drop table lobdemo;
create table lobdemo (key number, a_blob BLOB, a_bfile BFILE);
drop directory dir_alias;
create directory dir_alias as '<YOUR DIRECTORY HERE>';
insert into lob_table values(1, utl_raw.cast_to_raw('1111111111'), 'aaaaaaaa');
commit;
--lob_test.sql结束
下面的lobdemo1.pc可以在UNIX下使用:
make -f demo_proc.mk build EXE=progname OBJS=progname.o
(demo_proc.mk 可以在$ORACLE_HOME/precomp/demo/proc目录下找到)
lobdemo1.pc是一个来自ORACLE的例子,实现将从一个BLOB中读出数据,并写入out.gif。以下的代码中,有详细的注释,读者可以通过阅读代码了解如何处理LOB字段。以下是这个程序的代码:
/* lobdemo1.pc代码开始*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sqlda.h>
#include <sqlcpr.h>
/* 定义用户名和口令的最大长度 */
#define UNAME_LEN 20
#define PWD_LEN 40
/* 定义ORACLE数据类型 */
VARCHAR username[UNAME_LEN]; /* VARCHAR 是ORACLE支持的结构 */
varchar password[PWD_LEN]; /* varchar 也可以小写 */
#ifndef ORA_PROC
#include <oci.h>
#endif
#include <sqlca.h>
/*定义lob指示器字段,可以通过该指示器变量访问LOB*/
OCIBlobLocator *blob;
OCIClobLocator *clob;
FILE *fp;
unsigned int amt, offset = 1;
#define MAXBUFLEN 5000
unsigned char buffer[MAXBUFLEN];
EXEC SQL VAR buffer IS RAW(MAXBUFLEN);
/* 错误处理函数,一旦发生ORACLE所无,自动调用这个处理函数 */
void sql_error(msg)
char *msg;
{
char err_msg[128];
size_t buf_len, msg_len;
EXEC SQL WHENEVER SQLERROR CONTINUE;
printf("\n%s\n", msg);
buf_len = sizeof (err_msg);
sqlglm(err_msg, &buf_len, &msg_len);
printf("%.*s\n", msg_len, err_msg);
EXEC SQL ROLLBACK RELEASE;
exit(EXIT_FAILURE);
}
void main()
{
/* 第一步:连接数据库
* 给用户名赋值
*/
strncpy((char *) username.arr, "SCOTT", UNAME_LEN);
/* 设置用户名的长度 */
username.len =
(unsigned short) strlen((char *) username.arr);
/* 给口令赋值并设置长度. */
strncpy((char *) password.arr, "TIGER", PWD_LEN);
password.len =
(unsigned short) strlen((char *) password.arr);
/*注册错误处理函数. */
EXEC SQL WHENEVER SQLERROR DO sql_error("ORACLE error--\n");
/*
* 连接数据库
*/
EXEC SQL CONNECT :username IDENTIFIED BY :password;
printf("\nConnected to ORACLE as user: %s\n", username.arr);
/* 为LOB字段分配空间 */
EXEC SQL ALLOCATE :blob;
EXEC SQL ALLOCATE :clob;
/* 取出LOB数据 */
EXEC SQL SELECT a_blob INTO :blob FROM lob_table WHERE key=1;
/* 打开外部文件,用于存储LOB数据 */
fp = fopen("out.gif", "w");
EXEC SQL WHENEVER NOT FOUND GOTO end_of_lob;
/* 设置读5000字节,并从blob中读出5000字节,放入buffer,并将buffer写入文件*/
amt = 5000;
EXEC SQL LOB READ :amt FROM :blob AT :offset INTO :buffer;
fwrite(buffer, MAXBUFLEN, 1, fp);
EXEC SQL WHENEVER NOT FOUND DO break;
/* 循环读,并把读出的数据写入文件,直到全部读完为止 */
while (TRUE)
{
EXEC SQL LOB READ :amt FROM :blob INTO :buffer;
fwrite(buffer, MAXBUFLEN, 1, fp);
}
end_of_lob:
fwrite(buffer, amt, 1, fp);
printf("\nG'day.\n\n\n");
/* 断开数据库连接 */
EXEC SQL ROLLBACK WORK RELEASE;
exit(EXIT_SUCCESS);
}
/* lobdemo1.pc结束*/
lobdemo2.pc使用第一个例子生成的BFILE,把这个文件读入一个内部BLOB字段。
/*loaddemo2.pc写BLOB的例子*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sqlda.h>
#include <sqlcpr.h>
/* 定义用户名口令的长度. */
#define UNAME_LEN 20
#define PWD_LEN 40
/* 用户名口令 */
VARCHAR username[UNAME_LEN]; /* VARCHAR is an Oracle-supplied struct */
varchar password[PWD_LEN]; /* varchar can be in lower case also. */
#ifndef ORA_PROC
#include <oci.h>
#endif
#include <sqlca.h>
/*定义blob和bfile指示器变量*/
OCIBlobLocator *blob;
OCIBFileLocator *bfile;
char *alias = "DIR_ALIAS";
char *filename = "out.gif";
unsigned int amt = 50;
unsigned int filelen;
/* 错误处理函数 */
void sql_error(msg)
char *msg;
{
char err_msg[128];
size_t buf_len, msg_len;
EXEC SQL WHENEVER SQLERROR CONTINUE;
printf("\n%s\n", msg);
buf_len = sizeof (err_msg);
sqlglm(err_msg, &buf_len, &msg_len);
printf("%.*s\n", msg_len, err_msg);
EXEC SQL ROLLBACK RELEASE;
exit(EXIT_FAILURE);
}
void main()
{
/*
* 连接数据库.
*/
strncpy((char *) username.arr, "SCOTT", UNAME_LEN);
/* Set the length component of the VARCHAR. */
username.len =
(unsigned short) strlen((char *) username.arr);
/* Copy the password. */
strncpy((char *) password.arr, "TIGER", PWD_LEN);
password.len =
(unsigned short) strlen((char *) password.arr);
/* Register sql_error() as the error handler. */
EXEC SQL WHENEVER SQLERROR DO sql_error("ORACLE error--\n");
EXEC SQL CONNECT :username IDENTIFIED BY :password;
printf("\nConnected to ORACLE as user: %s\n", username.arr);
/* 分配空间 */
EXEC SQL ALLOCATE :blob;
EXEC SQL ALLOCATE :bfile;
/* 初始化BFILE */
EXEC SQL LOB FILE SET :bfile
DIRECTORY = :alias, FILENAME = :filename;
/*首先创建一条记录,插入一个空的BLOB和一个bfile*/
EXEC SQL INSERT INTO lobdemo values (1, EMPTY_BLOB(), :bfile);
EXEC SQL SELECT a_blob, a_bfile INTO :blob, :bfile FROM lobdemo
WHERE key = 1;
/*打开bfile*/
EXEC SQL LOB OPEN :bfile;
/* 获得BFILE的长度 */
EXEC SQL LOB DESCRIBE :bfile
GET LENGTH INTO :filelen;
printf("File length is: %d\n", filelen);
amt = filelen;
/* 把BFILE装载到blob中,并关闭bfile */
EXEC SQL LOB LOAD :amt FROM FILE :bfile INTO :blob;
EXEC SQL LOB CLOSE :bfile;
printf("\nG'day.\n\n\n");
/*提交保存并断开和数据库的连接 */
EXEC SQL COMMIT WORK RELEASE;
exit(EXIT_SUCCESS);
}