当插入表中的一个字段,数据量比较大时怎么处理呢?我们在前面介绍OCIBindByPos()函数时看到,指示数据实际长度的参数alenp是一个ub2类型的整数,也就是说,绑定的数据量最大只有16K字节。当然大部分的字段长度都达不到这么大,但有两个特殊类型LONG和LONG RAW字段的长度最大可以存储2G的数据,如果向这样的字段中插入超过16K的数据,还有没有办法呢?OCI提供了一种叫做分片操作的方法,可以应对这种状况。分片操作顾名思义就是把大的数据分割成小的数据片,一片接一片的插入字段中。
要使用OCI的分片操作,首先在字段变量绑定时要用到OCI_DATA_AT_EXEC模式,就是在运行时提供数据,这里的绑定操作不关联具体的变量。操作步骤如下。
1. 分配OCI语句句柄。
2. 准备SQL语句文本,分析语法。
3. 绑定占位符变量,OCIBindByPos()使用OCI_DATA_AT_EXEC模式。
4. 执行语句,OCIStmtExecute(),这次不会插入任何数据,返回码OCI_NEED_DATA。
5. 提供数据,调用OCIStmtSetPieceInfo()函数,设置数据缓冲区地址和数据大小,指示分片的类型,第一片为OCI_FIRST_PIECE,下一片为OCI_NEXT_PIECE,最后一片为OCI_LAST_PIECE。
6. 执行语句,调用OCIStmtExecute()函数,这次会真正的插入数据片,如果上一步分片数据为OCI_LAST_PIECE,那么执行后返回码为OCI_SUCCESS,程序插入数据完毕。否则返回码为OCI_NEED_DATA,需要返回第五步,设置下一片数据地址和长度,继续这一步的执行操作,直到最后一片数据,退出循环。
这里用到一个函数OCIStmtSetPieceInfo(),设置数据片信息函数,看一下它的原型和参数。
sword OCIStmtSetPieceInfo ( void *hndlp,
ub4 type,
OCIError *errhp,
const void *bufp,
ub4 *alenp,
ub1 piece,
const void *indp,
ub2 *rcodep );
hndlp是一个输入/输出参数,要设置信息的句柄。可以是绑定句柄,也可以是定义句柄。查询的分片操作也要用到这个函数。
type是一个输入参数,是上面句柄的类型,OCI_HTYPE_BIND或者OCI_HTYPE_DEFINE。
errhp是一个输出参数,错误句柄,用于返回函数的错误码和错误信息文本。
bufp是一个输入/输出参数,数据缓冲区地址,绑定句柄是提供数据的地址,定义句柄是接收数据的地址。
alenp是一个输入/输出参数,数据的实际长度,绑定句柄是提供数据的长度,定义句柄是返回数据的长度。
indp是一个输入/输出参数,是指示变量,与OCIBindByPos()函数中一样。
rcodep是一个输入/输出参数,是字段级返回码,与OCIBindByPos()函数中一样。
假如我们创建一张表叫test_long_tab,然后向表中插入数据。建表语句为CREATE TABLE test_long_tab (ID NUMBER, NAME VARCHAR2(100), INFO LONG)。
我们下面看一个完整的例子,向LONG字段INFO中分片插入数据,一次数据片插入4000字符,插入10次。
OCIEnv *envhp = NULL;
OCIError *errhp = NULL;
OCIServer *svrhp = NULL;
OCISession *usrhp = NULL;
OCISvcCtx *svchp = NULL;
OCIStmt *smthp = NULL;
/* 插入分片的LONG数据 */
int insert_long_piece(void)
{
sword rc;
int slen;
sb2 ind_info;
ub4 alen_info;
ub2 rcode_info;
ub1 piece;
OCIBind *bndp;
char sqltxt[1024];
char long_buf[4096];
/* 分配OCI语句句柄 */
rc = OCIHandleAlloc(
(void *)envhp,
(void **)&smthp,
OCI_HTYPE_STMT,
0,
(void **)NULL
);
if (rc != OCI_SUCCESS) {
fprintf(stderr, "OCIHandleAlloc() - allocate statement handle error !\n");
return (-1);
}
/* 生成SQL语句文本 */
strcpy(sqltxt,
"INSERT INTO test_long_tab (ID, NAME, INFO) VALUES (1, 'LONG DATA', :1)");
slen = strlen(sqltxt);
/* 准备语句 */
if (check_oci_error(errhp,
OCIStmtPrepare(smthp, errhp, (const OraText *)sqltxt, slen,
OCI_NTV_SYNTAX, OCI_DEFAULT)) < 0)
return (-1);
/* 绑定第一个占位符INFO,使用OCI_DATA_AT_EXEC模式,value_sz要设置成插入数据的最大值 */
if (check_oci_error(errhp,
OCIBindByPos((OCIStmt *)smthp,
(OCIBind **)&bndp,
errhp,
(ub4)1, /* position */
(void *)NULL, /* valuep */
(sb4)40000, /* value_sz */
(ub2)SQLT_LNG, /* dty */
(void *)NULL, /* indp */
(ub2 *)NULL, /* alenp */
(ub2 *)NULL, /* column return code pointer */
(ub4)0, /* maxarr_len */
(ub4 *)NULL, /* curelep */
(ub4)OCI_DATA_AT_EXEC)
) < 0)
return (-1);
for (i=0; ; i++) {
/* 执行OCI语句 */
if ((rc = check_oci_error(errhp,
OCIStmtExecute(svchp,
smthp, /* stmthp */
errhp, /* errhp */
1, /* iters */
0, /* rowoff */
NULL, /* snap_in */
NULL, /* snap_out */
OCI_DEFAULT) /* mode */
)) < 0)
return (-1);
if (rc == OCI_SUCCESS) {
/* 插入数据片完毕,退出循环 */
break;
}
if (rc != OCI_NEED_DATA) {
fprintf(stderr, "data not completed !\n");
return (-1);
}
/* 设置数据片类型 */
if (i == 0)
piece = OCI_FIRST_PIECE;
else if (i == 9)
piece = OCI_LAST_PIECE;
else
piece = OCI_NEXT_PIECE;
/* 产生插入的数据,设置缓冲区和数据长度 */
sprintf(long_buf, "%d", i);
memset(&long_buf[1], 'a', 3999);
alen_info = 4000;
if (check_oci_error(errhp,
OCIStmtSetPieceInfo((void *)bndp, OCI_HTYPE_BIND,
errhp, (const void *)long_buf, &alen_info,
piece, (const void *)&ind_info, &rcode_info)
) < 0)
return (-1);
}
/* 提交改变的数据 */
if (check_oci_error(errhp,
OCITransCommit(svchp, errhp, OCI_DEFAULT)) < 0)
return (-1);
return (0);
}