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是一个好习惯。

相关推荐
c萱3 小时前
软件测试错题笔记
软件测试·数据库·笔记·测试工具·oracle·测试用例
烟雨归来4 小时前
3 个 ASM 磁盘故障案例,从故障诊断到解决方案
数据库·oracle
烟雨归来9 小时前
23ai数据库通过SQLcl生成AWR报告
oracle
川石课堂软件测试10 小时前
Oracle 数据库:视图与索引
数据库·网络协议·nginx·http·oracle·grafana·prometheus
li357416 小时前
从“内存操作”到“原子更新”:一次代码思维的跃迁
数据库·oracle
小白考证进阶中16 小时前
终于赶在考试券过期前把Oracle OCP证书考下来了!
运维·数据库·oracle·dba·开闭原则·数据库管理员
川石课堂软件测试18 小时前
Oracle 数据库如何查询列
linux·数据库·sql·功能测试·oracle·grafana·prometheus
NineData20 小时前
NineData发布 Oracle 到 MySQL 双向实时复制,助力去 O 战略与数据回流
mysql·阿里云·oracle·ninedata·数据库迁移·数据复制·双向复制
川石课堂软件测试21 小时前
Oracle 数据库使用事务确保数据的安全
数据库·python·功能测试·docker·oracle·单元测试·prometheus
皆过客,揽星河21 小时前
mysql初学者练习题(从基础到进阶,相关数据sql脚本在最后)
数据库·sql·mysql·oracle·mysql基础练习·mysql基础语法·数据库练习题