515 lines
15 KiB
C++
515 lines
15 KiB
C++
//--------------------------------------------------------------------
|
|
// Microsoft OLE DB Sample Provider
|
|
// (C) Copyright 1991 - 1999 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
// @doc
|
|
//
|
|
// @module ROWCHNG.CPP | IRowsetChange interface implementation
|
|
//
|
|
//
|
|
|
|
// Includes ------------------------------------------------------------------
|
|
|
|
#include "headers.h"
|
|
|
|
// Code ----------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
// IRowsetChange specific methods
|
|
|
|
// 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;
|
|
ULONG 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 = &(pColumnData->uLength);
|
|
pdwDstStatus = &(pColumnData->dwStatus);
|
|
cbDstMaxLength = pBinding[cBind].cbMaxLen;
|
|
|
|
dwPart = pBinding[cBind].dwPart;
|
|
dwDstType = rgdbcolumninfo[cCols].wType;
|
|
|
|
// Get the Length
|
|
if( dwPart & DBPART_LENGTH )
|
|
dwSrcLength = *(ULONG *)((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 );
|
|
}
|
|
|
|
|
|
|
|
// CImpIRowsetChange::DeleteRows ---------------------------------------
|
|
//
|
|
// @mfunc Deletes rows from the provider. If Errors on individual rows
|
|
// occur, the DBERRORINFO array is updated to reflect the error and S_FALSE
|
|
// is returned instead of S_OK.
|
|
//
|
|
// @rdesc HRESULT indicating the status of the method
|
|
// @Flag S_OK | All row handles deleted
|
|
// @Flag DB_S_ERRORSOCCURRED | Some, but not all, row handles deleted
|
|
// @Flag E_INVALIDARG | Arguments did not match spec.
|
|
// @Flag E_OUTOFMEMORY | Could not allocated error array
|
|
//
|
|
STDMETHODIMP CImpIRowsetChange::DeleteRows
|
|
(
|
|
HCHAPTER hChapter, //@parm IN | The Chapter handle.
|
|
DBCOUNTITEM cRows, //@parm IN | Number of rows to delete
|
|
const HROW rghRows[], //@parm IN | Array of handles to delete
|
|
DBROWSTATUS rgRowStatus[] //@parm OUT | Error information
|
|
)
|
|
{
|
|
DBCOUNTITEM chRow = 0L;
|
|
DBCOUNTITEM cErrors = 0L;
|
|
BYTE* pbProvRow = NULL;
|
|
|
|
// If No Row handle, just return.
|
|
if( cRows == 0 )
|
|
return( S_OK );
|
|
|
|
// Check for Invalid Arguments
|
|
if ( !rghRows )
|
|
return( E_INVALIDARG );
|
|
|
|
// Process row handles
|
|
while (chRow < cRows)
|
|
{
|
|
if( rgRowStatus )
|
|
rgRowStatus[chRow] = DBROWSTATUS_S_OK;
|
|
|
|
// Is row handle valid
|
|
if( (m_pObj->m_prowbitsIBuffer)->IsSlotSet( (ULONG)rghRows[chRow]) != S_OK )
|
|
{
|
|
// Log Error
|
|
if( rgRowStatus )
|
|
rgRowStatus[chRow]= DBROWSTATUS_E_INVALID;
|
|
|
|
cErrors++;
|
|
chRow++;
|
|
continue;
|
|
}
|
|
|
|
// Get RowBuffer to look at which row this applies to
|
|
pbProvRow = (BYTE *) (m_pObj->GetRowBuff((DBCOUNTITEM) rghRows[chRow], TRUE ));
|
|
|
|
// Has row already been deleted
|
|
// S_OK means deleted
|
|
if( m_pObj->m_pFileio->IsDeleted((DBBKMARK) ((PROWBUFF) pbProvRow)->pbBmk ) == S_OK )
|
|
{
|
|
if( rgRowStatus )
|
|
rgRowStatus[chRow] = DBROWSTATUS_E_DELETED;
|
|
cErrors++;
|
|
chRow++;
|
|
continue;
|
|
}
|
|
|
|
// Delete the Row,
|
|
if( m_pObj->m_pFileio->DeleteRow((DBBKMARK) ((PROWBUFF) pbProvRow)->pbBmk) != S_OK )
|
|
{
|
|
// Some better decision as to what rowstatus to set could be done here..
|
|
if( rgRowStatus )
|
|
rgRowStatus[chRow] = DBROWSTATUS_E_FAIL;
|
|
cErrors++;
|
|
chRow++;
|
|
continue;
|
|
}
|
|
|
|
// Delete worked correctly
|
|
chRow++;
|
|
|
|
} //while
|
|
|
|
|
|
// If everything went OK except errors in rows use DB_S_ERRORSOCCURRED.
|
|
return cErrors ? ( cErrors < cRows ) ?
|
|
( DB_S_ERRORSOCCURRED ) : ( DB_E_ERRORSOCCURRED ) : ( S_OK );
|
|
}
|
|
|
|
// CImpIRowsetChange::InsetRow --------------------------------------------
|
|
//
|
|
// @mfunc Insert row into provider
|
|
//
|
|
// Returns: S_OK if data changed successfully
|
|
// E_FAIL if Catch all (NULL pData, etc.)
|
|
// E_INVALIDARG if pcErrors!=NULL and paErrors==NULL
|
|
// E_OUTOFMEMORY if output error array couldn't be allocated
|
|
// DB_E_BADACCESSORHANDLE if invalid accessor
|
|
//
|
|
STDMETHODIMP CImpIRowsetChange::InsertRow
|
|
(
|
|
HCHAPTER hChapter, //@parm IN | The Chapter handle.
|
|
HACCESSOR hAccessor,
|
|
void* pData,
|
|
HROW* phRow
|
|
)
|
|
{
|
|
DBORDINAL cCols;
|
|
DBCOUNTITEM cBind;
|
|
BYTE* pbProvRow;
|
|
HRESULT hr;
|
|
ULONG irow;
|
|
DBCOUNTITEM cBindings;
|
|
DBBINDING* pBinding;
|
|
DBCOUNTITEM cErrorCount;
|
|
DBTYPE dwSrcType;
|
|
DBTYPE dwDstType;
|
|
void* pSrc;
|
|
void* pDst;
|
|
ULONG dwSrcLength;
|
|
DBLENGTH* pdwDstLength;
|
|
DBLENGTH cbDstMaxLength;
|
|
DWORD dwSrcStatus;
|
|
DWORD* pdwDstStatus;
|
|
DWORD dwPart;
|
|
PCOLUMNDATA pColumnData;
|
|
PACCESSOR paccessor = NULL;
|
|
BYTE* rgbRowDataSave = NULL;
|
|
BOOL fCanHoldRows = FALSE;
|
|
DBPROPIDSET rgPropertyIDSets[1];
|
|
ULONG cPropertySets;
|
|
DBPROPSET* prgPropertySets;
|
|
DBPROPID rgPropId[1];
|
|
DBCOLUMNINFO * rgdbcolumninfo = NULL;
|
|
|
|
// Initialize values
|
|
if( phRow )
|
|
*phRow = NULL;
|
|
|
|
// 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 );
|
|
|
|
// 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;
|
|
|
|
assert( m_pObj->m_pUtilProp );
|
|
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( DB_E_ROWSNOTRELEASED );
|
|
|
|
if( FAILED( hr = GetNextSlots( m_pObj->m_pIBuffer, 1, &irow )) )
|
|
return hr;
|
|
|
|
if( FAILED( m_pObj->Rebind((BYTE *) m_pObj->GetRowBuff( irow, TRUE ))) )
|
|
return( E_FAIL );
|
|
|
|
// Get the rowbuffer and set the new bookmark
|
|
pbProvRow = (BYTE *) (m_pObj->GetRowBuff( irow, TRUE ));
|
|
((PROWBUFF) pbProvRow)->ulRefCount++;
|
|
((PROWBUFF) pbProvRow)->pbBmk = /*(BYTE*)*/ m_pObj->m_pFileio->GetRowCnt()+1;
|
|
m_pObj->m_ulRowRefCount++;
|
|
|
|
cBindings = paccessor->cBindings;
|
|
pBinding = paccessor->rgBindings;
|
|
|
|
// NULL Accessor (set Status to NULL)
|
|
if( !cBindings )
|
|
{
|
|
for (cCols = 1; cCols <= m_pObj->m_cCols; cCols++)
|
|
{
|
|
pColumnData = m_pObj->m_pFileio->GetColumnData(cCols, (ROWBUFF *) pbProvRow);
|
|
pColumnData->dwStatus = DBSTATUS_S_ISNULL;
|
|
}
|
|
}
|
|
|
|
// 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 = &(pColumnData->uLength);
|
|
pdwDstStatus = &(pColumnData->dwStatus);
|
|
cbDstMaxLength = pBinding[cBind].cbMaxLen;
|
|
|
|
dwPart = pBinding[cBind].dwPart;
|
|
dwDstType = rgdbcolumninfo[cCols].wType;
|
|
|
|
// Get the Length
|
|
if( dwPart & DBPART_LENGTH )
|
|
dwSrcLength = *(ULONG *)((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)));
|
|
|
|
// fatal error
|
|
if( FAILED( hr ) )
|
|
return hr;
|
|
|
|
// rounding or truncation or can't coerce
|
|
if( hr != S_OK )
|
|
cErrorCount++;
|
|
}
|
|
|
|
// If all bindings are bad and not a NULL Accessor
|
|
if( !cBindings || cErrorCount < cBindings )
|
|
{
|
|
// Carry out the insert.
|
|
if( FAILED(m_pObj->m_pFileio->UpdateRow((DBBKMARK) ((PROWBUFF) pbProvRow)->pbBmk, pbProvRow, INSERT )) )
|
|
return( E_FAIL );
|
|
|
|
// Set the RowHandle
|
|
if( phRow )
|
|
*phRow = irow;
|
|
}
|
|
|
|
// If everything went OK except errors in rows use DB_S_ERRORSOCCURRED.
|
|
return cErrorCount ? ( cErrorCount < cBindings ) ?
|
|
( DB_S_ERRORSOCCURRED ) : ( DB_E_ERRORSOCCURRED ) : ( S_OK );
|
|
}
|
|
|