手中有一套OCI操作的类似于JDBC的封装,要添加存储过程中对输出参数的支持,通过查看手册,只要调用OCIBindByName或者ByPos进行绑定即可,改好后代码大概是这样的:
class OciStatement : public DBStatement
{
public :
//...
void regOutParam(int idx, int type, int max);
protected:
void doExecute(int times);
protected:
OCIEnv * m_hEnv ;
OCIError * m_hError;
OCISvcCtx * m_hSvc ;
OCIStmt * m_stmtp;
map m_outStmt;
map m_outData;
};
void OciStatement::doExecute(int times)
{
sword swResult;
swResult = OCIStmtExecute(m_hSvc, m_stmtp, m_hError
, times, 0, NULL, NULL, OCI_DEFAULT);
if (!checkResult(swResult))
makeException(m_hError, swResult, m_sql.c_str());
}
void OciStatement::regOutParam(int idx, int type, int max)
{
sword swResult;
OCIBind * bindp = NULL;
if (type == DBCT_RESULT)
{
OCIStmt * stmtp = NULL;
swResult = OCIHandleAlloc(m_hEnv, (void **)&stmtp
, OCI_HTYPE_STMT, 0, NULL);
if (!checkResult(swResult))
{
makeException(m_hError, swResult, m_sql.c_str());
}
else
{
m_outStmt[idx] = stmtp;
swResult = OCIBindByPos(m_stmtp
, &bindp
, m_hError
, idx
, (void *)&stmtp
, (sb4) 0
, SQLT_RSET
, (dvoid *)0
, (ub2 *)0
, (ub2 *)0
, (ub4)0
, (ub4 *)0
, (ub4)OCI_DEFAULT);
}
}
else if (type == DBCT_INTEGER)
{
DBOutBuf * out = new DBOutBuf(max);
m_outData[idx] = out;
swResult = OCIBindByPos(m_stmtp
, &bindp
, m_hError
, idx
, (void *)out->buf
, out->BUFFERLEN
, SQLT_INT
, (dvoid *)&out->flag
, (ub2 *)&out->datalen
, (ub2 *)0
, (ub4)0
, (ub4 *)0
, OCI_DEFAULT );
}
else
{
//...
}
if (!checkResult(swResult))
makeException(m_hError, swResult, m_sql.c_str());
}
绑定其它类型的都没有问题,唯独只有当输出类型为游标对象时,程序直接崩溃,通过core分析,在执行OCIStmtExecute时,在oracle的库里崩溃了,访问非法内存。而直接使用OCI创建的测试程序则没有问题,仔细分析了才找到了问题,绑定其它类型时,传入的是内存地址,而绑定游标时,传入的是OCIStmt对象的指针的指针,如下:
OCIStmt * stmtp = NULL;
swResult = OCIHandleAlloc(m_hEnv, (void **)&stmtp
, OCI_HTYPE_STMT, 0, NULL);
if (!checkResult(swResult))
{
makeException(m_hError, swResult, m_sql.c_str());
}
else
{
m_outStmt[idx] = stmtp;
swResult = OCIBindByPos(m_stmtp
, &bindp
, m_hError
, idx
, (void *)&stmtp //注意,这里传入的是OCIStmt**
//栈上的对象stmtp在函数结束后就没有了
//再执行doExecute中调用OCIStmtExecute就非法访问了
//做OCI的测试程序时bind和exec全在main中,就不会出错
, (sb4) 0
, SQLT_RSET
, (dvoid *)0
, (ub2 *)0
, (ub2 *)0
, (ub4)0
, (ub4 *)0
, (ub4)OCI_DEFAULT);
}
找到问题就好处理了,记录在这儿吧,对指针的使用真是需要小心再小心。
不知道为什么OCI库会采用这样的设计,使用中也未发现stmtp被重新指向新的地址,不理解。