Updating Rows
IRowsetChange enables consumers to change the values of columns in a row of data. If the consumer wants to change the data, it must first construct an accessor for the columns to be changed. IRowsetChange contains three methods: DeleteRows, InsertRow, and SetData. The sample provider does not implement the InsertRow method.
Setting the New Data Values
IRowsetChange::SetData sets new data values for columns in a row. SetData cannot be used to change values in a deleted row. When SetData updates a row, it overwrites the row data in the provider's data cache and in the underlying data source with at signs (@), causing the row to appear deleted. Then it appends the new row to the end of the file. Changes made through SetData are applied to the data source immediately. The source code for IRowsetChange::SetData follows; you can find the complete source code for IRowsetChange in RowChng.cpp.
// CImpIRowsetChange::SetData ------------------------------------------------
//
// @mfunc Sets new data values into fields of a row.
//
// @rdesc HRESULT
// @flag S_OK | The method succeeded
// @flag E_OUTOFMEMORY | Out of memory
// @flag DB_E_BADACCESSORHANDLE | Bad accessor handle
// @flag DB_E_READONLYACCESSOR | Tried to write through a read-only accessor
// @flag DB_E_BADROWHANDLE | Invalid row handle
// @flag E_INVALIDARG | pData was NULL
// @flag E_FAIL | Provider-specific error
// @flag OTHER | Other HRESULTs returned by called functions
//
STDMETHODIMP CImpIRowsetChange::SetData
(
HROW hRow, //@parm IN | Handle of the row in which to set the data
HACCESSOR hAccessor, //@parm IN | Handle to the accessor to use
void* pData //@parm IN | Pointer to the data
)
{
DBORDINAL cCols;
DBCOUNTITEM cBind;
BYTE* pbProvRow;
HRESULT hr;
DBCOUNTITEM cBindings;
DBBINDING* pBinding;
DBCOUNTITEM cErrorCount;
DBTYPE dwSrcType;
DBTYPE dwDstType;
DWORD dwPart;
DBLENGTH dwSrcLength;
DWORD dwSrcStatus;
void* pSrc;
void* pDst;
DBLENGTH* pdwDstLength;
DBLENGTH cbDstMaxLength;
DWORD* pdwDstStatus;
PCOLUMNDATA pColumnData;
PACCESSOR paccessor = NULL;
BYTE* rgbRowDataSave = NULL;
DBCOLUMNINFO * rgdbcolumninfo = NULL;
// Is row handle right?
assert( m_pObj->m_prowbitsIBuffer );
if( (m_pObj->m_prowbitsIBuffer)->IsSlotSet( (ULONG)hRow ) != S_OK )
return( DB_E_BADROWHANDLE );
// Check the Accessor Handle
assert( m_pObj->m_pextbufferAccessor );
if( FAILED(m_pObj->m_pextbufferAccessor->GetItemOfExtBuffer(hAccessor, &paccessor)))
return( DB_E_BADACCESSORHANDLE );
// Ensure a source of data.
assert( paccessor );
if( paccessor->cBindings && !pData )
return( E_INVALIDARG );
pbProvRow = (BYTE *)(m_pObj->GetRowBuff((DBCOUNTITEM) hRow, TRUE ));
// Is row handle deleted?
if( m_pObj->m_pFileio->IsDeleted((DBBKMARK) ((PROWBUFF) pbProvRow)->pbBmk ) == S_OK )
return( DB_E_DELETEDROW );
rgbRowDataSave = (BYTE *) malloc( m_pObj->m_cbTotalRowSize );
if( !rgbRowDataSave )
return( E_OUTOFMEMORY );
// Save the row.
memcpy( rgbRowDataSave, pbProvRow, m_pObj->m_cbTotalRowSize );
cBindings = paccessor->cBindings;
pBinding = paccessor->rgBindings;
// Apply accessor to data.
rgdbcolumninfo = m_pObj->m_pFileio->GetColInfo();
for (cBind = 0, cErrorCount = 0; cBind < cBindings; cBind++)
{
cCols = pBinding[cBind].iOrdinal;
// make sure column number is in range
if( cCols < 1 || cCols > m_pObj->m_cCols )
return( DB_E_BADORDINAL );
pColumnData = m_pObj->m_pFileio->GetColumnData(cCols, (ROWBUFF *)pbProvRow);
dwSrcType = pBinding[cBind].wType;
pDst = &(pColumnData->bData);
pdwDstLength = (DBBYTEOFFSET *) &(pColumnData->dwLength);
pdwDstStatus = &(pColumnData->dwStatus);
cbDstMaxLength = pBinding[cBind].cbMaxLen;
dwPart = pBinding[cBind].dwPart;
dwDstType = rgdbcolumninfo[cCols].wType;
// Get the Length
if( dwPart & DBPART_LENGTH )
dwSrcLength = *(DBLENGTH *)((BYTE*)pData + pBinding[cBind].obLength);
else
dwSrcLength = 0;
// Get the Status
if( dwPart & DBPART_STATUS )
dwSrcStatus = *(ULONG *)((BYTE*)pData + pBinding[cBind].obStatus);
else
dwSrcStatus = DBSTATUS_S_OK;
// Check the Status for DBSTATUS_S_DEFAULT
if( dwSrcStatus == DBSTATUS_S_DEFAULT )
dwSrcStatus = DBSTATUS_S_ISNULL;
// Check the Status for DBSTATUS_S_IGNORE
if( dwSrcStatus == DBSTATUS_S_IGNORE ||
dwSrcStatus == DBSTATUS_S_ISNULL )
continue;
// Check the Status of the value being sent in
if( dwSrcStatus != DBSTATUS_S_OK )
{
*(ULONG *)((BYTE*)pData + pBinding[cBind].obStatus) = DBSTATUS_E_BADSTATUS;
cErrorCount++;
continue;
}
// Get the Value
if( (dwPart & DBPART_VALUE) == 0 )
{
if( dwPart & DBPART_STATUS )
*(ULONG *)((BYTE*)pData + pBinding[cBind].obStatus) = DBSTATUS_E_UNAVAILABLE;
cErrorCount++;
continue;
}
else
{
pSrc = (void *) ((BYTE*) pData + pBinding[cBind].obValue);
}
// Check to see if we have a DC
if( !g_pIDataConvert )
return( E_FAIL );
hr = g_pIDataConvert->DataConvert(
dwSrcType,
dwDstType,
dwSrcLength,
pdwDstLength,
pSrc,
pDst,
cbDstMaxLength,
dwSrcStatus,
pdwDstStatus,
pBinding[cBind].bPrecision, // bPrecision for conversion to DBNUMERIC
pBinding[cBind].bScale, // bScale for conversion to DBNUMERIC
(DBDATACONVERT_SETDATABEHAVIOR |
(!dwSrcLength ? DBDATACONVERT_LENGTHFROMNTS : 0)));
if( dwPart & DBPART_STATUS )
*(ULONG *)((BYTE*)pData + pBinding[cBind].obStatus) = *pdwDstStatus;
// fatal error
if( FAILED( hr ) && hr != DB_E_ERRORSOCCURRED )
return hr;
// rounding or truncation or can't coerce
if( hr != S_OK )
cErrorCount++;
}
// Carry out the update.
if( FAILED( m_pObj->m_pFileio->UpdateRow((DBBKMARK) ((PROWBUFF) pbProvRow)->pbBmk, pbProvRow, UPDATE )) )
{
// Restore the row to its previous state
memcpy( pbProvRow, rgbRowDataSave, m_pObj->m_cbTotalRowSize );
free( rgbRowDataSave );
return( E_FAIL );
}
free( rgbRowDataSave );
// If everything went OK except errors in rows use DB_S_ERRORSOCCURRED.
return cErrorCount ? ( cErrorCount < cBindings ) ?
( DB_S_ERRORSOCCURRED ) : ( DB_E_ERRORSOCCURRED ) : ( S_OK );
}