2025-11-28 00:35:46 +09:00

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 );
}