562 lines
19 KiB
C++
562 lines
19 KiB
C++
//--------------------------------------------------------------------
|
|
// Microsoft OLE DB Sample Provider
|
|
// (C) Copyright 1991 - 1999 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
// @doc
|
|
//
|
|
// @module IROWSET.CPP | IRowset interface implementation
|
|
//
|
|
//
|
|
|
|
// Includes ------------------------------------------------------------------
|
|
|
|
#include "headers.h"
|
|
|
|
// Code ----------------------------------------------------------------------
|
|
|
|
// IRowset specific interface methods
|
|
|
|
// CImpIRowset::GetData --------------------------------------------------
|
|
//
|
|
// @mfunc Retrieves data from the rowset's cache
|
|
//
|
|
// @rdesc Returns one of the following values:
|
|
// @flag S_OK | Method Succeeded
|
|
// @flag DB_S_ERRORSOCCURED | Could not coerce a column value
|
|
// @flag DB_E_BADACCESSORHANDLE | Invalid Accessor given
|
|
// @flag DB_E_BADROWHANDLE | Invalid row handle given
|
|
// @flag E_INVALIDARG | pData was NULL
|
|
// @flag OTHER | Other HRESULTs returned by called functions
|
|
//
|
|
STDMETHODIMP CImpIRowset::GetData
|
|
(
|
|
HROW hRow, //@parm IN | Row Handle
|
|
HACCESSOR hAccessor, //@parm IN | Accessor to use
|
|
void *pData //@parm OUT | Pointer to buffer where data should go.
|
|
)
|
|
{
|
|
PACCESSOR pAccessor;
|
|
DBORDINAL cCols;
|
|
DBORDINAL cBind;
|
|
ROWBUFF *pRowBuff;
|
|
COLUMNDATA *pColumnData;
|
|
DBBINDING *pBinding;
|
|
DBCOUNTITEM cBindings;
|
|
DBORDINAL cErrorCount;
|
|
DBTYPE dwSrcType;
|
|
DBTYPE dwDstType;
|
|
void *pSrc;
|
|
void *pDst;
|
|
DBBYTEOFFSET ulSrcLength;
|
|
DBLENGTH* pulDstLength;
|
|
DBLENGTH cbDstMaxLength;
|
|
ULONG dwSrcStatus;
|
|
ULONG *pdwDstStatus;
|
|
DWORD dwPart;
|
|
HRESULT hr;
|
|
|
|
DBCOLUMNINFO * rgdbcolumninfo = NULL;
|
|
|
|
// Coerce data for row 'hRow', according to hAccessor.
|
|
// Put in location 'pData'. Offsets and types are in hAccessor's bindings.
|
|
//
|
|
// Return S_OK if all columns retrieved with successful status
|
|
// Return DB_S_ERRORSOCCURED on failure to retrieve one or more column values
|
|
// Return DB_E_ERRORSOCCURED on failure to retrieve all column values
|
|
|
|
// GetItemOfExtBuffer is basically operator[].
|
|
// It takes an index (or handle) (referenced from 1...n),
|
|
// and a ptr for where to write the data.
|
|
//
|
|
// It holds ptrs to a variable-length ACCESSOR struct.
|
|
// So we get the ACCESSOR ptr for the client's accessor handle.
|
|
|
|
assert( m_pObj->m_pextbufferAccessor );
|
|
m_pObj->m_pextbufferAccessor->GetItemOfExtBuffer(hAccessor, &pAccessor);
|
|
if( !pAccessor )
|
|
return DB_E_BADACCESSORHANDLE;
|
|
|
|
assert( pAccessor );
|
|
cBindings = pAccessor->cBindings;
|
|
pBinding = pAccessor->rgBindings;
|
|
|
|
// IsSlotSet returns S_OK if row is marked.
|
|
// S_FALSE if row is not marked.
|
|
// The "mark" means that there is data present in the row.
|
|
// Rows are [1...n], slot marks are [0...n-1].
|
|
if (m_pObj->m_prowbitsIBuffer->IsSlotSet( (ULONG) hRow ) != S_OK)
|
|
return ResultFromScode( DB_E_BADROWHANDLE );
|
|
|
|
// Ensure a place to put data, unless the accessor is the null accessor then
|
|
// a NULL pData is okay.
|
|
if ( pData == NULL && cBindings != 0 )
|
|
return ResultFromScode( E_INVALIDARG );
|
|
|
|
// Check to see if we have a DC
|
|
if( !g_pIDataConvert)
|
|
return (E_FAIL);
|
|
|
|
// Internal error for a 0 reference count on this row,
|
|
// since we depend on the slot-set stuff.
|
|
pRowBuff = m_pObj->GetRowBuff( (DBCOUNTITEM) hRow, TRUE );
|
|
assert( pRowBuff->ulRefCount );
|
|
|
|
// Check for the Deleted Row
|
|
if ( m_pObj->m_pFileio->IsDeleted( (DBBKMARK) pRowBuff->pbBmk ) == S_OK )
|
|
return ResultFromScode( DB_E_DELETEDROW );
|
|
|
|
cErrorCount = 0;
|
|
rgdbcolumninfo = m_pObj->m_pFileio->GetColInfo();
|
|
for (cBind=0; cBind < cBindings; cBind++)
|
|
{
|
|
cCols = pBinding[cBind].iOrdinal;
|
|
|
|
// make sure column number is in range
|
|
if ( !(0 < cCols && cCols <= m_pObj->m_cCols) )
|
|
return ResultFromScode( DB_E_BADORDINAL );
|
|
|
|
pColumnData = m_pObj->m_pFileio->GetColumnData(cCols, pRowBuff);
|
|
|
|
dwSrcType = rgdbcolumninfo[cCols].wType;
|
|
pSrc = &(pColumnData->bData);
|
|
ulSrcLength = pColumnData->uLength;
|
|
dwSrcStatus = pColumnData->dwStatus;
|
|
cbDstMaxLength = pBinding[cBind].cbMaxLen;
|
|
dwDstType = pBinding[cBind].wType;
|
|
dwPart = pBinding[cBind].dwPart;
|
|
|
|
pDst = dwPart & DBPART_VALUE ? ((BYTE *) pData + pBinding[cBind].obValue) : NULL;
|
|
pulDstLength = dwPart & DBPART_LENGTH ? (DBLENGTH *) ((BYTE*) pData + pBinding[cBind].obLength) : NULL;
|
|
pdwDstStatus = dwPart & DBPART_STATUS ? (ULONG *) ((BYTE*) pData + pBinding[cBind].obStatus) : NULL;
|
|
|
|
hr = g_pIDataConvert->DataConvert(
|
|
dwSrcType,
|
|
dwDstType,
|
|
ulSrcLength,
|
|
pulDstLength,
|
|
pSrc,
|
|
pDst,
|
|
cbDstMaxLength,
|
|
dwSrcStatus,
|
|
pdwDstStatus,
|
|
pBinding[cBind].bPrecision, // bPrecision for conversion to DBNUMERIC
|
|
pBinding[cBind].bScale, // bScale for conversion to DBNUMERIC
|
|
DBDATACONVERT_DEFAULT);
|
|
|
|
// rounding or truncation or can't coerce
|
|
if (hr != S_OK)
|
|
cErrorCount++;
|
|
}
|
|
|
|
// We report any lossy conversions with a special status.
|
|
// Note that DB_S_ERRORSOCCURED is a success, rather than failure.
|
|
return cErrorCount ? ( cErrorCount < cBindings ) ?
|
|
( DB_S_ERRORSOCCURRED ) : ( DB_E_ERRORSOCCURRED ) : ( S_OK );
|
|
}
|
|
|
|
|
|
|
|
// CImpIRowset::GetNextRows --------------------------------------------------
|
|
//
|
|
// @mfunc Fetches rows in a sequential style, remembering the previous position
|
|
//
|
|
// @rdesc Returns one of the following values:
|
|
// @flag S_OK | Method Succeeded
|
|
// @flag DB_S_ENDOFROWSET | Reached end of rowset
|
|
// @flag DB_E_CANTFETCHBACKWARDS | cRows was negative and we can't fetch backwards
|
|
// @flag DB_E_ROWSNOTRELEASED | Must release all HROWs before calling GetNextRows
|
|
// @flag E_FAIL | Provider-specific error
|
|
// @flag E_INVALIDARG | pcRowsObtained or prghRows was NULL
|
|
// @flag E_OUTOFMEMORY | Out of Memory
|
|
// @flag OTHER | Other HRESULTs returned by called functions
|
|
//
|
|
STDMETHODIMP CImpIRowset::GetNextRows
|
|
(
|
|
HCHAPTER hChapter, //@parm IN | The Chapter handle.
|
|
DBROWOFFSET lRowOffset, //@parm IN | Rows to skip before reading
|
|
DBROWCOUNT cRows, //@parm IN | Number of rows to fetch
|
|
DBCOUNTITEM *pcRowsObtained, //@parm OUT | Number of rows obtained
|
|
HROW **prghRows //@parm OUT | Array of Hrows obtained
|
|
)
|
|
{
|
|
ULONG cRowsTmp;
|
|
ULONG cSlotAlloc =0;
|
|
DBROWCOUNT irow, ih;
|
|
ULONG cRowFirst, cRowLast;
|
|
PROWBUFF prowbuff;
|
|
HRESULT hr;
|
|
BOOL fCanHoldRows = FALSE;
|
|
DBPROPIDSET rgPropertyIDSets[1];
|
|
ULONG cPropertySets;
|
|
DBPROPSET* prgPropertySets;
|
|
DBPROPID rgPropId[1];
|
|
|
|
|
|
// init out-params
|
|
if ( pcRowsObtained )
|
|
*pcRowsObtained = 0;
|
|
|
|
// Check validity of arguments.
|
|
if ( pcRowsObtained == NULL || prghRows == NULL )
|
|
return ResultFromScode( E_INVALIDARG );
|
|
|
|
// No-op case always succeeds.
|
|
if ( cRows == 0 )
|
|
return ResultFromScode( S_OK );
|
|
|
|
// This implementation doesn't support scrolling backward.
|
|
if ( cRows < 0 )
|
|
return ResultFromScode( DB_E_CANTFETCHBACKWARDS );
|
|
|
|
// This implementation doesn't support scrolling backward.
|
|
if ( lRowOffset < 0 )
|
|
return ResultFromScode( DB_E_CANTSCROLLBACKWARDS );
|
|
|
|
// Get the value of the DBPROP_CANHOLDROWS property
|
|
rgPropertyIDSets[0].guidPropertySet = DBPROPSET_ROWSET;
|
|
rgPropertyIDSets[0].rgPropertyIDs = rgPropId;
|
|
rgPropertyIDSets[0].cPropertyIDs = 1;
|
|
rgPropId[0] = DBPROP_CANHOLDROWS;
|
|
|
|
m_pObj->m_pUtilProp->GetProperties(
|
|
PROPSET_ROWSET,
|
|
1,
|
|
rgPropertyIDSets,
|
|
&cPropertySets,
|
|
&prgPropertySets );
|
|
|
|
// Get the Prompt value
|
|
if( V_BOOL(&prgPropertySets->rgProperties->vValue) == VARIANT_TRUE )
|
|
fCanHoldRows = TRUE;
|
|
|
|
// Free the memory
|
|
SAFE_FREE(prgPropertySets[0].rgProperties);
|
|
SAFE_FREE(prgPropertySets);
|
|
|
|
// Are there any unreleased rows?
|
|
if( ((m_pObj->m_prowbitsIBuffer)->ArrayEmpty() != S_OK) && (!fCanHoldRows) )
|
|
return ResultFromScode( DB_E_ROWSNOTRELEASED );
|
|
|
|
// Is the cursor fully materialized (end-of-cursor condition)?
|
|
if (m_pObj->m_dwStatus & STAT_ENDOFCURSOR)
|
|
return ResultFromScode( DB_S_ENDOFROWSET );
|
|
|
|
assert( m_pObj->m_rgbRowData );
|
|
if (FAILED( m_pObj->Rebind((BYTE *) m_pObj->GetRowBuff( m_pObj->m_irowMin, TRUE ))))
|
|
return ResultFromScode( E_FAIL );
|
|
|
|
//
|
|
// Fetch Data
|
|
//
|
|
if (lRowOffset)
|
|
{
|
|
// Calculate the new position
|
|
m_pObj->m_irowFilePos += lRowOffset;
|
|
|
|
// Check if skip causes END_OF_ROWSET
|
|
if (m_pObj->m_irowFilePos > m_pObj->m_pFileio->GetRowCnt() ||
|
|
m_pObj->m_irowFilePos <= 0)
|
|
{
|
|
m_pObj->m_dwStatus |= STAT_ENDOFCURSOR;
|
|
return ResultFromScode( DB_S_ENDOFROWSET );
|
|
}
|
|
}
|
|
|
|
if (FAILED( hr = GetNextSlots( m_pObj->m_pIBuffer, (ULONG)cRows, &cRowFirst )))
|
|
return hr;
|
|
|
|
cSlotAlloc = (ULONG)cRows;
|
|
|
|
for (irow =1; irow <= cRows; irow++)
|
|
{
|
|
// Setup the row
|
|
prowbuff = m_pObj->GetRowBuff( cRowFirst + irow - 1, TRUE );
|
|
memset(prowbuff->cdData, 0, m_pObj->m_cbRowSize);
|
|
if (FAILED( m_pObj->Rebind((BYTE *) prowbuff)))
|
|
return ResultFromScode( E_FAIL );
|
|
|
|
// Get the Data from the File into the row buffer
|
|
if (S_FALSE == ( hr = m_pObj->m_pFileio->Fetch( m_pObj->m_irowFilePos + irow )))
|
|
{
|
|
m_pObj->m_dwStatus |= STAT_ENDOFCURSOR;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (FAILED( hr ))
|
|
return ResultFromScode( E_FAIL );
|
|
}
|
|
}
|
|
|
|
cRowsTmp = (ULONG)(irow - 1); //Irow will be +1 because of For Loop
|
|
m_pObj->m_irowLastFilePos = m_pObj->m_irowFilePos;
|
|
m_pObj->m_irowFilePos += cRowsTmp;
|
|
|
|
|
|
//
|
|
// Through fetching many rows of data
|
|
//
|
|
// Allocate row handles for client.
|
|
// Note that we need to use IMalloc for this.
|
|
//
|
|
// Should only malloc cRowsTmp, instead of cRows.
|
|
//
|
|
// Modified to use IMalloc.
|
|
// Should malloc cRows, since client will assume it's that big.
|
|
//
|
|
|
|
*pcRowsObtained = cRowsTmp;
|
|
|
|
if ( *prghRows == NULL && cRowsTmp )
|
|
*prghRows = (HROW *) PROVIDER_ALLOC( cRows*sizeof( HROW ));
|
|
|
|
if ( *prghRows == NULL && cRowsTmp )
|
|
return ResultFromScode( E_OUTOFMEMORY );
|
|
|
|
//
|
|
// Fill in the status information: Length, IsNull
|
|
// May be able to wait until first call to GetData,
|
|
// but have to do it sometime.
|
|
//
|
|
// Suggest keeping an array of structs of accessor info.
|
|
// One element is whether accessor requires any status info or length info.
|
|
// Then we could skip this whole section.
|
|
//
|
|
// Added check for cRowsTmp to MarkRows call.
|
|
// Don't want to call if cRowsTmp==0.
|
|
// (Range passed to MarkRows is inclusive, so can't specify marking 0 rows.)
|
|
//
|
|
// Note that SetSlots is a CBitArray member function -- not an IBuffer function.
|
|
//
|
|
// Bits are [0...n-1], row handles are [1...n].
|
|
//
|
|
// Cleanup. Row handles, bits, indices are the same [m....(m+n)], where m is some # >0,
|
|
//
|
|
// Added row-wise reference counts and cursor-wise reference counts.
|
|
//
|
|
|
|
// Set row handles, fix data length field and compute data status field.//
|
|
m_pObj->m_cRows = cRowsTmp;
|
|
cRowLast = cRowFirst + cRowsTmp - 1;
|
|
|
|
// Cleanup extra slots where no hRow actually was put..
|
|
// ** Because of less rows than asked for
|
|
// ** Because of temporary for for data transfer.
|
|
if (cSlotAlloc > (cRowsTmp))
|
|
if (FAILED( hr = ReleaseSlots( m_pObj->m_pIBuffer, cRowFirst + cRowsTmp, (cSlotAlloc - cRowsTmp))))
|
|
return hr;
|
|
|
|
for (irow = (LONG) (cRowFirst), ih =0; irow <= (LONG) cRowLast; irow++, ih++)
|
|
{
|
|
// Increment the rows-read count,
|
|
// then store it as the bookmark in the very first DWORD of the row.
|
|
prowbuff = m_pObj->GetRowBuff( irow, TRUE );
|
|
|
|
// Insert the bookmark and its row number (from 1...n) into a hash table.
|
|
// This allows us to quickly determine the presence of a row in mem, given the bookmark.
|
|
// The bookmark is contained in the row buffer, at the very beginning.
|
|
// Bookmark is the row number within the entire result set [1...num_rows_read].
|
|
|
|
// This was a new Bookmark, not in memory,
|
|
// so return to user (in *prghRows) the hRow we stored.
|
|
prowbuff->ulRefCount++;
|
|
prowbuff->pbBmk = /*(BYTE*)*/ m_pObj->m_irowLastFilePos + ih + 1;
|
|
m_pObj->m_ulRowRefCount++;
|
|
|
|
(*prghRows)[ih] = (HROW) ( irow );
|
|
}
|
|
|
|
if (m_pObj->m_dwStatus & STAT_ENDOFCURSOR)
|
|
return ResultFromScode( DB_S_ENDOFROWSET );
|
|
else
|
|
return ResultFromScode( S_OK );
|
|
}
|
|
|
|
|
|
|
|
// CImpIRowset::ReleaseRows ---------------------------------------
|
|
//
|
|
// @mfunc Releases row handles
|
|
//
|
|
// @rdesc Returns one of the following values:
|
|
// @flag S_OK | success
|
|
// @flag DB_S_ERRORSOCCURRED | some elements of rghRows were invalid
|
|
// @flag DB_E_ERRORSOCCURRED | all elements of rghRows were invalid
|
|
// @flag E_INVALIDARG | rghRows was a NULL pointer and crow > 0
|
|
//
|
|
STDMETHODIMP CImpIRowset::ReleaseRows
|
|
(
|
|
DBCOUNTITEM cRows, //@parm IN | Number of rows to release
|
|
const HROW rghRows[], //@parm IN | Array of handles of rows to be released
|
|
DBROWOPTIONS rgRowOptions[], //@parm IN | Additional Options
|
|
DBREFCOUNT rgRefCounts[], //@parm OUT | array of ref counts of released rows
|
|
DBROWSTATUS rgRowStatus[] //@parm OUT | status array of for input rows
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DBCOUNTITEM chRow = 0L;
|
|
DBCOUNTITEM cErrors = 0L;
|
|
ROWBUFF *pRowBuff = NULL;
|
|
|
|
// check params
|
|
if ( cRows && !rghRows )
|
|
return ResultFromScode( E_INVALIDARG );
|
|
|
|
while ( chRow < cRows )
|
|
{
|
|
// check the row handle
|
|
hr = (m_pObj->m_prowbitsIBuffer)->IsSlotSet((ULONG) rghRows[chRow]);
|
|
if ( (hr == S_OK) && (m_pObj->m_ulRowRefCount) &&
|
|
(pRowBuff=m_pObj->GetRowBuff((DBCOUNTITEM) rghRows[chRow], TRUE)) &&
|
|
(pRowBuff->ulRefCount) )
|
|
{
|
|
// Found valid row, so decrement reference counts.
|
|
// (Internal error for refcount to be 0 here, since slot set.)
|
|
--pRowBuff->ulRefCount;
|
|
--m_pObj->m_ulRowRefCount;
|
|
|
|
// stuff new refcount into caller's array
|
|
if ( rgRefCounts )
|
|
rgRefCounts[chRow] = pRowBuff->ulRefCount;
|
|
|
|
if ( rgRowStatus )
|
|
rgRowStatus[chRow] = DBROWSTATUS_S_OK;
|
|
|
|
if ( pRowBuff->ulRefCount == 0 )
|
|
ReleaseSlots( m_pObj->m_pIBuffer, (ULONG) rghRows[chRow], 1 );
|
|
}
|
|
else
|
|
{
|
|
// It is an error for client to try to release a row
|
|
// for which "IsSetSlot" is false. Client gave us an invalid handle.
|
|
// Ignore it (we can't release it...) and report error when done.
|
|
if ( rgRefCounts )
|
|
rgRefCounts[chRow] = 0;
|
|
|
|
if ( rgRowStatus )
|
|
rgRowStatus[chRow] = DBROWSTATUS_E_INVALID;
|
|
|
|
++ cErrors;
|
|
}
|
|
|
|
chRow++;
|
|
}
|
|
|
|
// If everything went OK except errors in rows use DB_S_ERRORSOCCURRED.
|
|
return cErrors ? ( cErrors < cRows ) ?
|
|
ResultFromScode( DB_S_ERRORSOCCURRED ) :
|
|
ResultFromScode( DB_E_ERRORSOCCURRED ) :
|
|
ResultFromScode( S_OK );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// CImpIRowset::ResartPosition ---------------------------------------
|
|
//
|
|
// @mfunc Repositions the next fetch position to the start of the rowset
|
|
//
|
|
// - all rows must be released before calling this method
|
|
// - it is not expensive to Restart us, because we are from a single table
|
|
//
|
|
//
|
|
// @rdesc Returns one of the following values:
|
|
// @flag S_OK | Method Succeeded
|
|
// @flag DB_E_ROWSNOTRELEASED | All HROWs must be released before calling
|
|
//
|
|
STDMETHODIMP CImpIRowset::RestartPosition
|
|
(
|
|
HCHAPTER hChapter //@parm IN | The Chapter handle.
|
|
)
|
|
{
|
|
// make sure all rows have been released
|
|
// Fail even if CANHOLDROWS is true
|
|
if( ((m_pObj->m_prowbitsIBuffer)->ArrayEmpty() != S_OK) )
|
|
return ResultFromScode( DB_E_ROWSNOTRELEASED );
|
|
|
|
// set "next fetch" position to the start of the rowset
|
|
m_pObj->m_irowFilePos = 0;
|
|
|
|
// clear "end of cursor" flag
|
|
m_pObj->m_dwStatus &= ~STAT_ENDOFCURSOR;
|
|
|
|
return ResultFromScode( S_OK );
|
|
}
|
|
|
|
|
|
// CImpIRowset::AddRefRows --------------------------------------------------
|
|
//
|
|
// @mfunc Adds a reference count to an existing row handle
|
|
//
|
|
// @rdesc Returns one of the following values:
|
|
// @flag S_OK | success
|
|
// @flag DB_S_ERRORSOCCURRED | some elements of rghRows were invalid
|
|
// @flag DB_E_ERRORSOCCURRED | all elements of rghRows were invalid
|
|
// @flag E_INVALIDARG | rghRows was a NULL pointer and crow > 0
|
|
|
|
STDMETHODIMP CImpIRowset::AddRefRows
|
|
(
|
|
DBCOUNTITEM cRows, // @parm IN | Number of rows to refcount
|
|
const HROW rghRows[], // @parm IN | Array of row handles to refcount
|
|
DBREFCOUNT rgRefCounts[], // @parm OUT | Array of refcounts
|
|
DBROWSTATUS rgRowStatus[] // @parm OUT | Array of row status
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DBCOUNTITEM chRow = 0L;
|
|
DBCOUNTITEM cErrors = 0L;
|
|
ROWBUFF *pRowBuff = NULL;
|
|
|
|
// check params
|
|
if ( cRows && !rghRows )
|
|
return ResultFromScode( E_INVALIDARG );
|
|
|
|
// for each of the HROWs the caller provided...
|
|
for (chRow = 0; chRow < cRows; chRow++)
|
|
{
|
|
// check the row handle
|
|
if( ((m_pObj->m_prowbitsIBuffer)->IsSlotSet((ULONG) rghRows[chRow]) == S_OK) &&
|
|
(pRowBuff=m_pObj->GetRowBuff((DBCOUNTITEM) rghRows[chRow], TRUE )) &&
|
|
(m_pObj->m_pFileio->IsDeleted((DBBKMARK) pRowBuff->pbBmk) != S_OK) )
|
|
{
|
|
// bump refcount
|
|
pRowBuff = m_pObj->GetRowBuff((DBCOUNTITEM) rghRows[chRow], TRUE );
|
|
assert( pRowBuff->ulRefCount != 0 );
|
|
assert( m_pObj->m_ulRowRefCount != 0 );
|
|
++pRowBuff->ulRefCount;
|
|
++m_pObj->m_ulRowRefCount;
|
|
|
|
// stuff new refcount into caller's array
|
|
if ( rgRefCounts )
|
|
rgRefCounts[chRow] = pRowBuff->ulRefCount;
|
|
|
|
if ( rgRowStatus )
|
|
rgRowStatus[chRow] = DBROWSTATUS_S_OK;
|
|
}
|
|
else
|
|
{
|
|
if ( rgRefCounts )
|
|
rgRefCounts[chRow] = 0;
|
|
|
|
if ( rgRowStatus )
|
|
{
|
|
if ( pRowBuff && m_pObj->m_pFileio->IsDeleted((DBBKMARK) pRowBuff->pbBmk) == S_OK )
|
|
rgRowStatus[chRow] = DBROWSTATUS_E_DELETED;
|
|
else
|
|
rgRowStatus[chRow] = DBROWSTATUS_E_INVALID;
|
|
}
|
|
|
|
++ cErrors;
|
|
}
|
|
}
|
|
|
|
// If everything went OK except errors in rows use DB_S_ERRORSOCCURRED.
|
|
return cErrors ? ( cErrors < cRows ) ?
|
|
ResultFromScode( DB_S_ERRORSOCCURRED ) :
|
|
ResultFromScode( DB_E_ERRORSOCCURRED ) :
|
|
ResultFromScode( S_OK );
|
|
}
|
|
|