前面我们看到在LOB操作之前都要执行一个LOB SELECT操作,用于得到LOB定位符,尤其在插入一个空LOB,随后写入的情况,操作起来非常繁琐。那时我们希望有一种方法能简洁的得到LOB定位符,现在我们来看一下。
OCI在SQL语句INSERT,UPDATE,DELETE后面可以跟随一个RETURNING字句,相当于一个查询语句跟一个DML语句合并在一起执行,先执行完DML语句,然后执行一个查询语句,把查询的内容返回给程序。
这里仿照前面插入空LOB然后写入数据的例子,看看怎样使用这个字句。我们插入一条数据,SQL语句是INSERT INTO test_clob_tab (ID, MESSAGE) VALUES (2, EMPTY_CLOB()),然后执行SELECT MESSAGE FROM test_clob_tab WHERE ID=2,定义一个LOB定位符的输出变量,得到LOB定位符。如果合并到一起SQL语句是这样的INSERT INTO test_clob_tab (ID, MESSAGE) VALUES (2, EMPTY_CLOB()) RETURNING MESSAGE INTO :1。然后通过绑定函数绑定一个LOB定位符的输出变量,只要执行一次,就能插入空LOB数据然后得到一个LOB定位符。是不是简单多了?尤其在一次插入多个空LOB然后分别写入的情况,使用这种方法,更简单,代码更简洁。
下面我们还是通过一个例子来看看完整的代码怎样实现。
OCIEnv *envhp = NULL;
OCIError *errhp = NULL;
OCIServer *svrhp = NULL;
OCISession *usrhp = NULL;
OCISvcCtx *svchp = NULL;
OCIStmt *smthp = NULL;
int insert_one_lob(void)
{
int i;
sword rc;
sb4 ec;
int slen;
sb2 ind_msg;
ub2 alen_msg;
ub1 piece;
oraub8 amt;
OCIBind *bndp;
OCILobLocator *locp;
char sqltxt[1024];
text errbuf[512];
strcpy(sqltxt,
"INSERT INTO test_clob_tab (ID, MESSAGE) values (2, EMPTY_CLOB()) RETURNING MESSAGE INTO :1");
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 = 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定位符 */
rc = OCIBindByPos((OCIStmt *)smthp,
(OCIBind **)&bndp,
errhp,
(ub4)1,
(void *)&locp,
(sb4)sizeof(OCILobLocator *),
(ub2)SQLT_CLOB,
(void *)&ind_msg, /* indp */
(ub2 *)&alen_msg, /* alenp */
(ub2 *)NULL, /* column return code pointer */
(ub4)0, /* maxarr_len */
(ub4 *)NULL, /* curelep */
(ub4)OCI_DEFAULT); /* mode */
if (rc != OCI_SUCCESS) {
OCIErrorGet(errhp, 1, NULL, &ec, errbuf, 512, OCI_HTYPE_ERROR);
fprintf(stderr, "OCIBindByPos() - [%d] %s\n", ec, errbuf);
return (-1);
}
ind_msg = 0;
alen_msg = sizeof(OCILobLocator *);
/* 执行语句 */
rc = OCIStmtExecute(svchp,
smthp, /* stmthp */
errhp, /* errhp */
1, /* 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);
}
/* 打开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, 'B');
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);
}
这种方法也有局限性,只能用于插入或更新LOB的操作中,其他的操作还是要先执行LOB SELECT操作得到LOB定位符才行。