手中有一套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<int, OCIStmt *> m_outStmt;
map<int, DBOutBuf *> 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被重新指向新的位址,不了解。