📄 oraclient.cpp
字号:
m_bPiecewiseFetchPending = false;
}
void Iora7Cursor::CheckPiecewiseNull(bool bAfterExecute)
{
// to check if piecewise fetched column (output param) is null (or not)
// we have to read at least one byte from piecewise stream
ub1 piece;
dvoid *ctxp;
ub4 iter, index;
sword rc;
((Iora7Connection*)m_pISAConnection)->Check(g_ora7API.ogetpi(
&m_handles.m_cda,
&piece, &ctxp, &iter, &index), &m_handles.m_cda);
LongContext_t *pLongContext = (LongContext_t *)ctxp;
pLongContext->Len = sizeof(m_PiecewiseNullCheckPreFetch);
((Iora7Connection*)m_pISAConnection)->Check(g_ora7API.osetpi(
&m_handles.m_cda,
piece, &m_PiecewiseNullCheckPreFetch[0], &pLongContext->Len), &m_handles.m_cda);
if(bAfterExecute)
rc = g_ora7API.oexec(&m_handles.m_cda);
else
rc = g_ora7API.ofetch(&m_handles.m_cda);
if(m_handles.m_cda.rc != 3130)
{
((Iora7Connection*)m_pISAConnection)->Check(rc, &m_handles.m_cda);
// cloumn was null or whole Long* was read
m_bPiecewiseFetchPending = false;
}
// set null indicator manually
// because Oracle set it only if null or whole read
*(sb2*)pLongContext->pInd = (sb2)(pLongContext->Len? 0 : -1);
// if column was not null then fetched piece is saved
// in m_PiecewiseNullCheckPreFetch, it's len in pLongContext->Len
// and we will use it when actual read request happen
}
/*virtual */
bool Iora7Cursor::FetchNext()
{
if(m_bPiecewiseFetchPending)
DiscardPiecewiseFetch();
sword rc = g_ora7API.ofetch(&m_handles.m_cda);
switch(m_handles.m_cda.rc)
{
case 1403:
m_bResultSetExist = false;
return false;
case 3130: // the buffer for the next Piece to be fetched is required
assert(m_bPiecewiseFetchAllowed);
m_bPiecewiseFetchPending = true;
CheckPiecewiseNull();
break;
default:
((Iora7Connection*)m_pISAConnection)->Check(rc, &m_handles.m_cda);
}
// use default helpers
ConvertSelectBufferToFields(0);
return true;
}
void Iora8Cursor::DestroyLobsReturnBinding()
{
while(m_nLobReturnBindsColCount)
{
while(m_nBLobBindsRowCount)
{
OCILobLocator *&pLoc = m_pppBLobReturningLocs[m_nLobReturnBindsColCount-1][m_nBLobBindsRowCount-1];
if(pLoc)
{
Iora8Connection::Check(g_ora8API.OCIDescriptorFree(
pLoc, OCI_DTYPE_LOB), m_handles.m_pOCIError, OCI_HTYPE_ERROR);
pLoc = NULL;
}
--m_nBLobBindsRowCount;
}
delete m_pppBLobReturningLocs[--m_nLobReturnBindsColCount];
delete m_ppLobReturningLens[m_nLobReturnBindsColCount];
}
if(m_ppLobReturnPlaceHolders)
{
free(m_ppLobReturnPlaceHolders);
m_ppLobReturnPlaceHolders = NULL;
}
delete m_pppBLobReturningLocs;
m_pppBLobReturningLocs = NULL;
delete m_ppLobReturningLens;
m_ppLobReturningLens = NULL;
}
// There are can be several reasons to reparse (Prepare again)
// already prepared statement:
// 1) if we bind Lobs for update or insert and
// temporary Lobs are not supported by client or server
// then we have to add 'returning' clause
// 2) if statement was executed and we rebind with
// different bind variable datatype (see ORA-01475 error)
void Iora8Cursor::CheckForReparseBeforeBinding(
int nPlaceHolderCount,
saPlaceHolder **ppPlaceHolders)
{
// first check if we have to modify SQL statement and reparse
// due to Lobs operations and lack of temporary Lobs support
// quick check, if stmt type is not
// Insert or Update then nothing to check
if(m_nOraStmtType == OCI_STMT_UPDATE ||
m_nOraStmtType == OCI_STMT_INSERT)
{
SAString sModifiedStmt;
SAString sReturning;
SAString sInto;
saPlaceHolder **ppLobReturnPlaceHolders = NULL;
int nLobReturnBindsColCount = 0;
int nPos = 0;
for(int i = 0; i < nPlaceHolderCount; ++i)
{
sModifiedStmt += m_sOriginalStmt.Mid(nPos, ppPlaceHolders[i]->getStart()-nPos);
bool bNull = ppPlaceHolders[i]->getParam()->isNull();
if(!bNull
&& ppPlaceHolders[i]->getParam()->DataType() == SA_dtBLob
&& !((Iora8Connection*)m_pISAConnection)->IsTemporaryLobSupported())
{
// replace param marker with empty_blob() function
sModifiedStmt += "empty_blob()";
if(!sReturning.IsEmpty())
sReturning += ", ";
sReturning += ppPlaceHolders[i]->getParam()->Name();
if(!sInto.IsEmpty())
sInto += ", ";
sInto += ":";
sInto += ppPlaceHolders[i]->getParam()->Name();
ppLobReturnPlaceHolders = (saPlaceHolder **)realloc(ppLobReturnPlaceHolders, sizeof(SAParam*)*(nLobReturnBindsColCount+1));
ppLobReturnPlaceHolders[nLobReturnBindsColCount] = ppPlaceHolders[i];
++nLobReturnBindsColCount;
}
else if(!bNull
&& ppPlaceHolders[i]->getParam()->DataType() == SA_dtCLob
&& !((Iora8Connection*)m_pISAConnection)->IsTemporaryLobSupported())
{
// replace param marker with empty_clob() function
sModifiedStmt += "empty_clob()";
if(!sReturning.IsEmpty())
sReturning += ", ";
sReturning += ppPlaceHolders[i]->getParam()->Name();
if(!sInto.IsEmpty())
sInto += ", ";
sInto += ":";
sInto += ppPlaceHolders[i]->getParam()->Name();
ppLobReturnPlaceHolders = (saPlaceHolder **)realloc(ppLobReturnPlaceHolders, sizeof(SAParam*)*(nLobReturnBindsColCount+1));
ppLobReturnPlaceHolders[nLobReturnBindsColCount] = ppPlaceHolders[i];
++nLobReturnBindsColCount;
}
else // remain unmodified
sModifiedStmt += m_sOriginalStmt.Mid(ppPlaceHolders[i]->getStart(), ppPlaceHolders[i]->getEnd()-ppPlaceHolders[i]->getStart()+1);
nPos = ppPlaceHolders[i]->getEnd() + 1;
}
// copy tail
if(nPos < m_sOriginalStmt.GetLength())
sModifiedStmt += m_sOriginalStmt.Mid(nPos);
if(nLobReturnBindsColCount)
{
// add/alter returning into clauses
bool bAdd = true;
if(bAdd) // add
{
sModifiedStmt += " returning ";
sModifiedStmt += sReturning;
sModifiedStmt += " into ";
sModifiedStmt += sInto;
}
else // modify
{
}
}
if(nLobReturnBindsColCount // we have returning clause
|| m_nLobReturnBindsColCount) // we had returning clause
{
try
{
InternalPrepare(sModifiedStmt);
}
catch(SAException &)
{
if(ppLobReturnPlaceHolders)
free(ppLobReturnPlaceHolders);
throw;
}
}
if(nLobReturnBindsColCount)
{
m_ppLobReturnPlaceHolders = ppLobReturnPlaceHolders;
m_pppBLobReturningLocs = new OCILobLocator**[nLobReturnBindsColCount];
memset(m_pppBLobReturningLocs, 0, sizeof(OCILobLocator**)*nLobReturnBindsColCount);
m_ppLobReturningLens = new ub4*[nLobReturnBindsColCount];
memset(m_ppLobReturningLens, 0, sizeof(ub4*)*nLobReturnBindsColCount);
m_nLobReturnBindsColCount = nLobReturnBindsColCount;
}
}
// then check if we need to reparse to avoid ORA-01475
if(m_pDTY) // we bound before
{
for(int i = 0; i < m_pCommand->ParamCount(); ++i)
{
SAParam &Param = m_pCommand->ParamByIndex(i);
SADataType_t eDataType = Param.DataType();
ub2 dty = (ub2)(eDataType == SA_dtUnknown?
SQLT_CHR : // VARCHAR2, some type should be set
CnvtStdToNative(eDataType));
if(m_pDTY[i] != dty)
{
InternalPrepare(m_sOriginalStmt);
break;
}
}
}
}
void Iora8Cursor::InternalPrepare(
const SAString &sStmt)
{
// save because (extraction from Oracle docs):
// "The pointer to the text of the statement must be available
// as long as the statement is
// executed, or data is fetched from it."
m_sInternalPrepareStmt = sStmt;
Iora8Connection::Check(g_ora8API.OCIStmtPrepare(
m_handles.m_pOCIStmt,
m_handles.m_pOCIError,
(CONST text*)(const char*)m_sInternalPrepareStmt,
(ub4)m_sInternalPrepareStmt.GetLength(),
OCI_NTV_SYNTAX,
OCI_DEFAULT), m_handles.m_pOCIError, OCI_HTYPE_ERROR);
m_nOraStmtType = 0; // unknown
if(m_pDTY)
{
::free(m_pDTY);
m_pDTY = NULL;
}
DestroyLobsReturnBinding();
// readStmtType
Iora8Connection::Check(g_ora8API.OCIAttrGet(
m_handles.m_pOCIStmt, OCI_HTYPE_STMT, &m_nOraStmtType, NULL, OCI_ATTR_STMT_TYPE, m_handles.m_pOCIError), m_handles.m_pOCIError, OCI_HTYPE_ERROR);
}
/*virtual */
void Iora8Cursor::Prepare(
const SAString &sStmt,
SACommandType_t eCmdType,
int /*nPlaceHolderCount*/,
saPlaceHolder** /*ppPlaceHolders*/)
{
SAString sStmtOra;
switch(eCmdType)
{
case SA_CmdSQLStmt:
sStmtOra = sStmt;
// save original, we will probably need to reprepare modified stmt if BLOBs will be bound in 8.0.x (not in 8i)
m_sOriginalStmt = sStmt;
break;
case SA_CmdStoredProc:
sStmtOra = CallSubProgramSQL();
break;
default:
assert(false);
}
// always compile original to check errors
InternalPrepare(sStmtOra);
}
void Iora8Cursor::CreateTemporaryLob(
OCILobLocator **ppLob,
SAParam &Param)
{
ub1 lobtype;
switch(Param.DataType())
{
case SA_dtBLob:
lobtype = OCI_TEMP_BLOB;
break;
case SA_dtCLob:
lobtype = OCI_TEMP_CLOB;
break;
default:
assert(false);
return;
}
Iora8Connection::Check(g_ora8API.OCIDescriptorAlloc(
((Iora8Connection*)m_pISAConnection)->m_handles.m_pOCIEnv,
(dvoid **)ppLob,
OCI_DTYPE_LOB, 0,
NULL), ((Iora8Connection*)m_pISAConnection)->m_handles.m_pOCIEnv, OCI_HTYPE_ENV);
Iora8Connection::Check(g_ora8API.OCILobCreateTemporary(
((Iora8Connection*)m_pISAConnection)->m_handles.m_pOCISvcCtx,
m_handles.m_pOCIError,
*ppLob,
OCI_DEFAULT,
OCI_DEFAULT,
lobtype,
OCI_ATTR_NOCACHE,
OCI_DURATION_SESSION), m_handles.m_pOCIError, OCI_HTYPE_ERROR);
++m_cTempLobs;
m_ppTempLobs = (OCILobLocator **)::realloc(m_ppTempLobs, m_cTempLobs * sizeof(OCILobLocator *));
m_ppTempLobs[m_cTempLobs-1] = *ppLob;
BindLob(*ppLob, Param);
}
void Iora8Cursor::Bind(
int nPlaceHolderCount,
saPlaceHolder** ppPlaceHolders)
{
CheckForReparseBeforeBinding(nPlaceHolderCount, ppPlaceHolders);
m_pDTY = (ub2*)::realloc(m_pDTY, sizeof(ub2) * m_pCommand->ParamCount());
// we should bind all params, not place holders in Oracle
AllocBindBuffer(sizeof(sb2), sizeof(ub2));
void *pBuf = m_pParamBuffer;
ub4 nLobReturnBindsColCount = 0;
for(int i = 0; i < m_pCommand->ParamCount(); ++i)
{
SAParam &Param = m_pCommand->ParamByIndex(i);
void *pInd;
void *pSize;
unsigned int nDataBufSize;
void *pValue;
IncParamBuffer(pBuf, pInd, pSize, nDataBufSize, pValue);
SADataType_t eDataType = Param.DataType();
ub2 dty = (ub2)(eDataType == SA_dtUnknown?
SQLT_CHR : // VARCHAR2, some type should be set
CnvtStdToNative(eDataType));
m_pDTY[i] = dty;
sb2 *indp = (sb2 *)pInd; // bind null indicator
dvoid *valuep = pValue;
sb4 value_sz = nDataBufSize; // allocated
ub2 *alenp = (ub2 *)pSize;
// this will be adjusted if Param is input
*alenp = (ub2)nDataBufSize;
// special code for ref cursors
if(eDataType == SA_dtCursor)
{
*indp = OCI_IND_NOTNULL; // field is not null
assert(nDataBufSize == sizeof(OCIStmt *));
memset(valuep, 0, nDataBufSize);
Iora8Connection::Check(
g_ora8API.OCIHandleAlloc(((Iora8Connection*)m_pISAConnection)->m_handles.m_pOCIEnv, (dvoid**)valuep, OCI_HTYPE_STMT, 0, NULL),
((Iora8Connection*)m_pISAConnection)->m_handles.m_pOCIEnv, OCI_HTYPE_ENV);
if(!Param.isNull() && isInputParam(Param))
{
SACommand *pCursor = Param.asCursor();
assert(pCursor);
const ora8CommandHandles *pH = (ora8CommandHandles *)pCursor->NativeHandles();
memcpy(valuep, &pH->m_pOCIStmt, sizeof(OCIStmt *));
*alenp = (ub2)InputBufferSize(Param);
}
}
else
{
*indp = OCI_IND_NULL;
if(isInputParam(Param))
{
if(Param.isNull())
*indp = OCI_IND_NULL; // field is null
else
*indp = OCI_IND_NOTNULL; // field is not null
*alenp = (ub2)InputBufferSize(Param);
assert(value_sz >= *alenp);
if(!Param.isNull())
{
switch(eDataType)
{
case SA_dtUnknown:
throw SAException(SA_Library_Error, -1, -1, IDS_UNKNOWN_PARAMETER_TYPE, (const char*)Param.Name());
case SA_dtBool:
// there is no "native" boolean type in Oracle,
// so treat boolean as 16-bit signed INTEGER in Oracle
assert(*alenp == sizeof(short));
*(short*)valuep = (short)Param.asBool();
break;
case SA_dtShort:
assert(*alenp == sizeof(short));
*(short*)valuep = Param.asShort();
break;
case SA_dtLong:
assert(*alenp == sizeof(long));
*(long*)valuep = Param.asLong();
break;
case SA_dtDouble:
assert(*alenp == sizeof(double));
*(double*)valuep = Param.asDouble();
break;
case SA_dtDateTime:
assert(*alenp == sizeof(OraDate_t)); // Oracle internal date/time representation
IoraConnection::CnvtDateTimeToInternal(
Param.asDateTime(),
(OraDate_t*)valuep);
break;
case SA_dtString:
assert(*alenp == (ub2)Param.asString().GetLength());
memcpy(valuep, (const char*)Param.asString(), *alenp);
break;
case SA_dtBytes:
assert(*alenp == (ub2)Param.asBytes().GetLength());
memcpy(valuep, (const char*)Param.asBytes(), *alenp);
break;
case SA_dtLongBinary:
assert(*alenp == sizeof(LongContext_t));
break;
case SA_dtLongChar:
assert(*alenp == sizeof(LongContext_t));
break;
case SA_dtBLob:
case SA_dtCLob:
assert(*alenp == sizeof(OCILobLocator *)
|| *alenp == sizeof(BlobReturningContext_t));
break;
case SA_dtCursor:
assert(false); // already handled
break;
default:
assert(false);
}
}
}
}
bool bLongPiecewise = isLong(eDataType);
bool bLob = !Param.isNull() && isLob(eDataType);
bool bLobReturning = bLob && !((Iora8Connection*)m_pISAConnection)->IsTemporaryLobSupported();
OCIBind* pOCIBind = NULL;
if(bLongPiecewise)
{
Iora8Connection::Check(g_ora8API.OCIBindByName(
m_handles.m_pOCIStmt,
&pOCIBind,
m_handles.m_pOCIError,
(CONST text*)(const char*)Param.Name(),
Param.Name().GetLength(),
valuep, SB4MAXVAL, dty,
indp, NULL,
NULL, 0, NULL, OCI_DATA_AT_EXEC), m_handles.m_pOCIError, OCI_HTYPE_ERROR);
LongContext_t *pLongContext = (LongContext_t *)pValue;
pLongContext->pReader = &Param;
pLongContext->pWriter = &Param;
pLongContext->pInd = indp;
OCICallbackInBind icbfp = LongInBind; // always assign InBind, otherwise OutBind is not used and Oracle do piecewise instead of callbacks
OCICallbackOutBind ocbfp = NULL;
if(isOutputParam(Param))
ocbfp = LongOutBind;
Iora8Connection::Check(
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -