OCI编程高级篇(八) LOB写操作

访问www.tomcoding.com网站,学习Oracle内部数据结构,详细文档说明,下载Oracle的exp/imp,DUL,logminer,ASM工具的源代码,学习高技术含量的内容。

上一节我们介绍了LOB定位符的绑定和定义操作,这里重点强调一下定义操作,这个行为一般用于LOB SELECT操作,LOB SELECT操作是所有LOB操作的基础,所有的关于LOB操作的OCI函数都是针对LOB定位符的,LOB SELECT的目的就是为了得到一个LOB定位符,一个LOB表的每行数据的每个LOB字段都对应一个LOB定位符,所以处理一行数据的多个LOB字段也要得到多个LOB定位符。下面看看LOB写操作的步骤。

  1. 准备LOB SELECT语句,一般SQL语句文本为SELECT lob_column FROM lob_table WHERE XXXX=XXXX FOR UPDATE。

  2. 定义输出的LOB定位符。这个定位符在之前要分配好,不用设置为空LOB。

  3. 执行LOB SELECT语句。

  4. 执行OCIStmtFetch()操作,把LOB定位符和LOB字段关联起来。

  5. 打开LOB定位符,使用OCILobOpen()函数。

  6. 写入LOB数据,可以循环写入多次LOB数据,使用OCILobWrite2()函数。

  7. 关闭LOB定位符,使用OCILobClose()函数。

  8. 提交数据库改变。

先看看用到的OCI函数原型和参数。

打开LOB函数。

sword OCILobOpen ( OCISvcCtx *svchp,

OCIError *errhp,

OCILobLocator *locp,

ub1 mode );

svchp是一个输入参数,是OCI服务上下文句柄。

errhp是一个输入/输出参数,错误句柄,用于返回错误码和错误信息文本。

locp是一个输入/输出参数,需要打开的LOB定位符。

mode是一个输入参数,打开的方式,取值OCI_LOB_READONLY或OCI_LOB_READWRITE。

关闭LOB函数。

sword OCILobClose ( OCISvcCtx *svchp,

OCIError *errhp,

OCILobLocator *locp );

svchp是一个输入参数,是OCI服务上下文句柄。

errhp是一个输入/输出参数,错误句柄,用于返回错误码和错误信息文本。

locp是一个输入/输出参数,需要关闭的LOB定位符。

写LOB函数。

sword OCILobWrite2 ( OCISvcCtx *svchp,

OCIError *errhp,

OCILobLocator *locp,

oraub8 *byte_amtp,

oraub8 *char_amtp,

oraub8 offset,

void *bufp,

oraub8 buflen,

ub1 piece,

void *ctxp,

OCICallbackLobWrite2 (cbfp)

(

void *ctxp,

void *bufp,

oraub8 *lenp,

ub1 *piecep

void **changed_bufpp,

oraub8 *changed_lenp

)

ub2 csid,

ub1 csfrm );

svchp是一个输入/输出参数,是OCI服务上下文句柄。

errhp是一个输入/输出参数,错误句柄,用于返回错误码和错误信息文本。

locp是一个输入/输出参数,需要操作的LOB定位符,唯一引用一个LOB。

byte_amtp是一个输入/输出参数,写入LOB的字节数,在BLOB写入时使用。

char_amtp是一个输入/输出参数,写入LOB的字符个数,在CLOB写入时使用,BLOB写入时忽略这个参数。

offset是一个输入参数,写入LOB的绝对偏移量,从LOB头开始计算。对CLOB计算单位是字符,对BLOB计算单位是字节。offset的位置从1开始计算。如果使用流写入方式,只需要在第一次调用写函数时设置offset值,后续的写函数可以忽略offset,函数会自动判断。

bufp是一个输入参数,是写入的LOB数据的缓冲区指针。

buflen是一个输入参数,表示LOB数据缓冲区中数据的大小,以字节计算。

piece是一个输入参数,表示写入的是哪个数据片。取值为OCI_ONE_PIECE,流模式中为OCI_FIRST_PIECE,OCI_NEXT_PIECE和OCI_LAST_PIECE。

ctxp是一个输入参数,是回调函数的上下文指针,可以设置为NULL。

cbfp是一个输入参数,是回调函数的函数指针,不使用回调函数设置为NULL。

csid是一个输入参数,是缓冲区中数据的字符集ID,只对CLOB起作用。

csfrm是一个输入参数,是缓冲区数据的字符集形式,取值SQLCS_IMPLICIT表示与数据库字符集一致,取值SQLCS_NCHAR表示使用国际字符集。

我们使用上一节中创建的表test_clob_tab,里面已经插入一条空LOB数据,现在看看怎样写入LOB数据,我们向LOB中写入20次,每次写入4000字符,使用流模式写入。代码如下。

复制代码
OCIEnv        *envhp = NULL;
OCIError    *errhp = NULL;
OCIServer    *svrhp = NULL;
OCISession    *usrhp = NULL;
OCISvcCtx    *svchp = NULL;
OCIStmt        *smthp = NULL;

​​int write_to_lob(void){
    int              i;
    sword            rc;
    sb4              ec;
    ub1              piece;
    int              slen;
    oraub8           amt;
    OCIDefine        *defp;
    OCILobLocator    *locp;
    char             sqltxt[1024];
    text             errbuf[512];
    char             buf[4096];


    /* 分配LOB定位符 */
    rc = OCIDescriptorAlloc((const void *)envhp, (void **)&locp,
        OCI_DTYPE_LOB, 0, (void **)NULL);
    if (rc != OCI_SUCCESS) {
        OCIErrorGet(envhp, 1, NULL, &ec, errbuf, 512, OCI_HTYPE_ERROR);
        fprintf(stderr, "OCIDescriptorAlloc() - [%d] %s\n", ec, errbuf);
        return (-1);
    }

    /* 下面执行LOB SELECT操作 */
    strcpy(sqltxt, "SELECT MESSAGE FROM test_clob_tab WHERE ID=1 FOR UPDATE");
    slen = strlen(sqltxt);

    rc = OCIStmtPrepare(smthp, errhp, (const OraText *)sqltxt, slen,
          OCI_NTV_SYNTAX, OCI_DEFAULT);

    if (rc != OCI_SUCCESS) {
        OCIErrorGet(errhp, 1, NULL, &ec, errbuf, 512, OCI_HTYPE_ERROR);
        fprintf(stderr, "OCIStmtPrepare() - [%d] %s\n", ec, errbuf);
        return (-1);
    }

    /* 定义LOB定位符输出 */
    rc = OCIDefineByPos((OCIStmt *)smthp,
        (OCIDefine **)&defp,
        (OCIError *)errhp,
        (ub4)1,
        (void *)&locp,
        (sb4)sizeof(OCILobLocator *),
        (ub2)SQLT_CLOB,
        (void *)NULL,
        (ub2 *)NULL,
        (ub2 *)NULL,
        (ub4)OCI_DEFAULT);

    if (rc != OCI_SUCCESS) {
        OCIErrorGet(errhp, 1, NULL, &ec, errbuf, 512, OCI_HTYPE_ERROR);
        fprintf(stderr, "OCIDefineByPos() - [%d] %s\n", ec, errbuf);
        return (-1);
    }

    /* 执行语句 */
    rc = OCIStmtExecute(svchp,
           smthp,           /* stmthp */
           errhp,           /* errhp */
           0,               /* iters */
           0,               /* rowoff */
           NULL,            /* snap_in */
           NULL,            /* snap_out */
           OCI_DEFAULT);    /* mode */

    if (rc != OCI_SUCCESS) {
        OCIErrorGet(errhp, 1, NULL, &ec, errbuf, 512, OCI_HTYPE_ERROR);
        fprintf(stderr, "OCIExecute() - [%d] %s\n", ec, errbuf);
        return (-1);
    }

    /* 执行fetch操作 */
    rc = OCIStmtFetch(smthp, errhp, 1, OCI_FETCH_NEXT, OCI_DEFAULT);

    if (rc != OCI_SUCCESS) {
        OCIErrorGet(errhp, 1, NULL, &ec, errbuf, 512, OCI_HTYPE_ERROR);
        fprintf(stderr, "OCIStmtFetch() - [%d] %s\n", ec, errbuf);
        return (-1);
    }

    /* 打开LOB */
    rc = OCILobOpen(svchp, errhp, locp, OCI_LOB_READWRITE);
    if (rc != OCI_SUCCESS) {
        OCIErrorGet(errhp, 1, NULL, &ec, errbuf, 512, OCI_HTYPE_ERROR);
        fprintf(stderr, "OCILobOpen() - [%d] %s\n", ec, errbuf);
        return (-1);
    }

    for (i=0; i<20; i++) {
        if (i == 0)
            piece = OCI_FIRST_PIECE;
        else if (i == 19)
            piece = OCI_LAST_PIECE;
        else
            piece = OCI_NEXT_PIECE;

        sprintf(buf, "%02d", i);
        memset(&buf[2], 3998, 'A');

        amt = 4000;

        rc = OCILobWrite2(svchp, errhp, locp, NULL, &amt, 1,
            buf, 4000, piece, 0, SQLCS_IMPLICIT);
        if (rc != OCI_SUCCESS) {
            OCIErrorGet(errhp, 1, NULL, &ec, errbuf, 512, OCI_HTYPE_ERROR);
            fprintf(stderr, "OCILobWrite2() - [%d] %s\n", ec, errbuf);
            return (-1);
        }
    }

    /* 关闭LOB */
    rc = OCILobClose(svchp, errhp, locp);
    if (rc != OCI_SUCCESS) {
        OCIErrorGet(errhp, 1, NULL, &ec, errbuf, 512, OCI_HTYPE_ERROR);
        fprintf(stderr, "OCILobClose() - [%d] %s\n", ec, errbuf);
        return (-1);
    }

    /* 提交改变 */
    rc = OCITransCommit(svchp, errhp, OCI_DEFAULT);

    if (rc != OCI_SUCCESS) {
        OCIErrorGet(errhp, 1, NULL, &ec, errbuf, 512, OCI_HTYPE_ERROR);
        fprintf(stderr, "OCITransCommit() - [%d] %s\n", ec, errbuf);
        return (-1);
    }

    /* 释放LOB定位符 */
    rc = OCIDescriptorFree((void *)locp, OCI_DTYPE_LOB);
    if (rc != OCI_SUCCESS) {
        OCIErrorGet(errhp, 1, NULL, &ec, errbuf, 512, OCI_HTYPE_ERROR);
        fprintf(stderr, "OCIDescriptorFree() - [%d] %s\n", ec, errbuf);
        return (-1);
    }

    return (0);
}

在上面的写入中使用了流模式,在第一次写入时使用OCI_FIRST_PIECE,最后一次写入使用OCI_LAST_PIECE,其他的写入使用OCI_NEXT_PIECE,只需要在第一次写入时指定写入偏移量1,就可以从头写入数据,后续写操作的偏移量都被忽略了。写入函数的csid设置为0,数据使用环境变量NLS_LANG定义的字符集。

如果不使用流模式写入,那么写入操作使用OCI_ONE_PIECE,每次写入一片数据,每次要自己计算偏移量的位置,好处是可以随机向任意位置写入数据。

在写数据前要打开LOB,写完后要关闭LOB,跟写一个文件相似。如果不打开LOB也可以直接调用写函数,不过每次写操作Oracle还是会隐式的打开LOB,写完一次后隐式关闭LOB,这样在大量多次写入时会影响效率,所以在写之前打开LOB是一个好习惯。

相关推荐
fen_fen13 小时前
Oracle建表语句示例
数据库·oracle
此刻你17 小时前
常用的 SQL 语句
数据库·sql·oracle
海心焱1 天前
从零开始构建 AI 插件生态:深挖 MCP 如何打破 LLM 与本地数据的连接壁垒
jvm·人工智能·oracle
德彪稳坐倒骑驴1 天前
MySQL Oracle面试题
数据库·mysql·oracle
吕司1 天前
MySQL库的操作
数据库·mysql·oracle
dishugj1 天前
【Oracle】 rac的一些问题以及解决方案
数据库·oracle
eWidget1 天前
面向信创环境的Oracle兼容型数据库解决方案
数据库·oracle·kingbase·数据库平替用金仓·金仓数据库
熊文豪1 天前
关系数据库替换用金仓——Oracle兼容性深度解析
数据库·oracle·金仓数据库·电科金仓·kes
eWidget1 天前
面向Oracle生态的国产高兼容数据库解决方案
数据库·oracle·kingbase·数据库平替用金仓·金仓数据库
A懿轩A1 天前
【MySQL 数据库】MySQL 数据库核心概念详解:库、表、字段、主键与关系型模型一文读懂
数据库·mysql·oracle