Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

5205 lines
167 KiB

//---------------------------------------------------------------------------
// Cursor.cpp : Cursor implementation
//
// Copyright (c) 1996 Microsoft Corporation, All Rights Reserved
// Developed by Sheridan Software Systems, Inc.
//---------------------------------------------------------------------------
#include "stdafx.h"
#include "Notifier.h"
#include "RSColumn.h"
#include "RSSource.h"
#include "CursMain.h"
#include "ColUpdat.h"
#include "CursPos.h"
#include "enumcnpt.h"
#include "CursBase.h"
#include "Cursor.h"
#include "CursMeta.h"
#include "EntryID.h"
#include "Stream.h"
#include "fastguid.h"
#include "resource.h"
#include "NConnPt.h"
#include "NConnPtC.h"
#include "FromVar.h"
#include "timeconv.h"
SZTHISFILE
//=--------------------------------------------------------------------------=
// CVDCursor - Constructor
//
CVDCursor::CVDCursor()
{
m_hAccessor = 0;
m_hVarHelper = 0;
m_ulVarBindings = 0;
m_rghVarAccessors = NULL;
m_rghAdjustAccessors = NULL;
m_pdwAdjustFlags = NULL;
m_ppColumns = NULL;
m_pCursorPosition = NULL;
m_pConnPtContainer = NULL;
#ifdef _DEBUG
g_cVDCursorCreated++;
#endif
}
//=--------------------------------------------------------------------------=
// ~CVDCursor - Destructor
//
CVDCursor::~CVDCursor()
{
DestroyAccessors();
DestroyColumns();
if (m_pCursorPosition->GetSameRowClone())
m_pCursorPosition->ReleaseSameRowClone();
LeaveFamily(); // leave m_pCursorPosition's notification family
if (m_pConnPtContainer)
m_pConnPtContainer->Destroy();
if (m_pCursorPosition)
((CVDNotifier*)m_pCursorPosition)->Release(); // release associated cursor position object
#ifdef _DEBUG
g_cVDCursorDestroyed++;
#endif
}
//=--------------------------------------------------------------------------=
// GetRowsetColumn - Get rowset column from ordinal
//=--------------------------------------------------------------------------=
// This function retrieves the rowset column with the specified rowset ordinal
//
// Parameters:
// ulOrdinal - [in] rowset ordinal
//
// Output:
// CVDRowsetColumn pointer
//
// Notes:
//
CVDRowsetColumn * CVDCursor::GetRowsetColumn(ULONG ulOrdinal)
{
CVDRowsetColumn * pRowsetColumn = NULL;
ULONG ulColumns = GetCursorMain()->GetColumnsCount();
CVDRowsetColumn * pColumn = GetCursorMain()->InternalGetColumns();
for (ULONG ulCol = 0; ulCol < ulColumns && !pRowsetColumn; ulCol++)
{
if (pColumn->GetOrdinal() == ulOrdinal)
pRowsetColumn = pColumn;
pColumn++;
}
return pRowsetColumn;
}
//=--------------------------------------------------------------------------=
// GetRowsetColumn - Get rowset column from cursor column identifier
//=--------------------------------------------------------------------------=
// This function retrieves the rowset column associated with the specified
// cursor column identifier
//
// Parameters:
// cursorColumnID - [in] a reference to cursor column identifier
//
// Output:
// CVDRowsetColumn pointer
//
// Notes:
//
CVDRowsetColumn * CVDCursor::GetRowsetColumn(CURSOR_DBCOLUMNID& cursorColumnID)
{
CVDRowsetColumn * pRowsetColumn = NULL;
ULONG ulColumns = GetCursorMain()->GetColumnsCount();
CVDRowsetColumn * pColumn = GetCursorMain()->InternalGetColumns();
for (ULONG ulCol = 0; ulCol < ulColumns && !pRowsetColumn; ulCol++)
{
if (IsEqualCursorColumnID(cursorColumnID, pColumn->GetCursorColumnID()))
pRowsetColumn = pColumn;
pColumn++;
}
return pRowsetColumn;
}
//=--------------------------------------------------------------------------=
// GetOrdinal - Get ordinal from cursor column identifier
//=--------------------------------------------------------------------------=
// This function converts a cursor column identifier its ordinal eqivalent
//
// Parameters:
// cursorColumnID - [in] a reference to cursor column identifier
// pulOrdinal - [out] a pointer to memory in which to return ordinal
//
// Output:
// HRESULT - S_OK if successful
// E_FAIL bad cursor column identifier
//
// Notes:
//
HRESULT CVDCursor::GetOrdinal(CURSOR_DBCOLUMNID& cursorColumnID, ULONG * pulOrdinal)
{
HRESULT hr = E_FAIL;
ULONG ulColumns = GetCursorMain()->GetColumnsCount();
CVDRowsetColumn * pColumn = GetCursorMain()->InternalGetColumns();
for (ULONG ulCol = 0; ulCol < ulColumns && FAILED(hr); ulCol++)
{
if (IsEqualCursorColumnID(cursorColumnID, pColumn->GetCursorColumnID()))
{
*pulOrdinal = pColumn->GetOrdinal();
hr = S_OK;
}
pColumn++;
}
return hr;
}
//=--------------------------------------------------------------------------=
// StatusToCursorInfo - Get cursor info from rowset status field
//=--------------------------------------------------------------------------=
// This function converts a rowset status to its cursor information field
// eqivalent
//
// Parameters:
// dwStatus - [in] rowset status
//
// Output:
// DWORD - cursor information
//
// Notes:
// There are more rowset statuses than cursor information values
//
DWORD CVDCursor::StatusToCursorInfo(DBSTATUS dwStatus)
{
DWORD dwCursorInfo = CURSOR_DB_UNKNOWN;
switch (dwStatus)
{
case DBSTATUS_S_OK:
dwCursorInfo = CURSOR_DB_NOINFO;
break;
case DBSTATUS_E_CANTCONVERTVALUE:
dwCursorInfo = CURSOR_DB_CANTCOERCE;
break;
case DBSTATUS_S_ISNULL:
dwCursorInfo = CURSOR_DB_NULL;
break;
case DBSTATUS_S_TRUNCATED:
dwCursorInfo = CURSOR_DB_TRUNCATED;
break;
}
return dwCursorInfo;
}
//=--------------------------------------------------------------------------=
// CursorInfoToStatus - Get rowset status from cursor info field
//=--------------------------------------------------------------------------=
// This function converts a cursor information field to its rowset status
// eqivalent
//
// Parameters:
// dwInfo - [in] rowset status
//
// Output:
// DWORD - cursor information
//
// Notes:
// This function only converts successful cursor information fields for
// the purpose of setting data
//
DBSTATUS CVDCursor::CursorInfoToStatus(DWORD dwCursorInfo)
{
DBSTATUS dwStatus;
switch (dwCursorInfo)
{
case CURSOR_DB_NULL:
dwStatus = DBSTATUS_S_ISNULL;
break;
case CURSOR_DB_EMPTY:
dwStatus = DBSTATUS_S_ISNULL;
break;
case CURSOR_DB_TRUNCATED:
dwStatus = DBSTATUS_S_TRUNCATED;
break;
case CURSOR_DB_NOINFO:
dwStatus = DBSTATUS_S_OK;
break;
}
return dwStatus;
}
//=--------------------------------------------------------------------------=
// ValidateCursorBindParams - Validate cursor column binding parameters
//=--------------------------------------------------------------------------=
// This function makes sure the specified column binding parameters are
// acceptable and then returns a pointer to the corresponding rowset column
//
// Parameters:
// pCursorColumnID - [in] a pointer to column identifier of the
// column to bind
// pCursorBindParams - [in] a pointer to binding structure
// ppRowsetColumn - [out] a pointer to memory in which to return
// a pointer to the rowset column to bind
//
// Output:
// HRESULT - S_OK if successful
// CURSOR_DB_E_BADBINDINFO bad binding information
// CURSOR_DB_E_BADCOLUMNID columnID is not available
//
// Notes:
//
HRESULT CVDCursor::ValidateCursorBindParams(CURSOR_DBCOLUMNID * pCursorColumnID, CURSOR_DBBINDPARAMS * pCursorBindParams,
CVDRowsetColumn ** ppRowsetColumn)
{
ASSERT_POINTER(pCursorColumnID, CURSOR_DBCOLUMNID)
ASSERT_POINTER(pCursorBindParams, CURSOR_DBBINDPARAMS)
ASSERT_POINTER(ppRowsetColumn, CVDRowsetColumn*)
// make sure we have all necessary pointers
if (!pCursorColumnID || !pCursorBindParams || !ppRowsetColumn)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorUpdateARow, m_pResourceDLL);
return E_INVALIDARG;
}
// init out parameter
*ppRowsetColumn = NULL;
// make sure column identifier is available
BOOL fColumnIDAvailable = FALSE;
DWORD dwCursorType;
ULONG ulColumns = GetCursorMain()->GetColumnsCount();
CVDRowsetColumn * pColumns = GetCursorMain()->InternalGetColumns();
CVDRowsetColumn * pColumn = pColumns;
// iterate through rowset columns looking for match
for (ULONG ulCol = 0; ulCol < ulColumns && !fColumnIDAvailable; ulCol++)
{
if (IsEqualCursorColumnID(*pCursorColumnID, pColumn->GetCursorColumnID()))
{
dwCursorType = pColumn->GetCursorType();
*ppRowsetColumn = pColumn;
fColumnIDAvailable = TRUE;
}
pColumn++;
}
// get out if not found
if (!fColumnIDAvailable)
{
VDSetErrorInfo(IDS_ERR_BADCOLUMNID, IID_ICursorUpdateARow, m_pResourceDLL);
return CURSOR_DB_E_BADCOLUMNID;
}
// make sure caller supplied a maximum length if a default binding was specified
// for the cursor types CURSOR_DBTYPE_CHARS, CURSOR_DBTYPE_WCHARS or CURSOR_DBTYPE_BYTES
if (pCursorBindParams->cbMaxLen == CURSOR_DB_NOMAXLENGTH &&
pCursorBindParams->dwBinding == CURSOR_DBBINDING_DEFAULT)
{
if (pCursorBindParams->dwDataType == CURSOR_DBTYPE_CHARS ||
pCursorBindParams->dwDataType == CURSOR_DBTYPE_WCHARS ||
pCursorBindParams->dwDataType == CURSOR_DBTYPE_BYTES)
{
VDSetErrorInfo(IDS_ERR_BADCURSORBINDINFO, IID_ICursorUpdateARow, m_pResourceDLL);
return CURSOR_DB_E_BADBINDINFO;
}
}
// check binding bit mask for possible values
if (pCursorBindParams->dwBinding != CURSOR_DBBINDING_DEFAULT &&
pCursorBindParams->dwBinding != CURSOR_DBBINDING_VARIANT &&
pCursorBindParams->dwBinding != CURSOR_DBBINDING_ENTRYID &&
pCursorBindParams->dwBinding != (CURSOR_DBBINDING_VARIANT | CURSOR_DBBINDING_ENTRYID))
{
VDSetErrorInfo(IDS_ERR_BADCURSORBINDINFO, IID_ICursorUpdateARow, m_pResourceDLL);
return CURSOR_DB_E_BADBINDINFO;
}
// check for valid cursor type
if (!IsValidCursorType(pCursorBindParams->dwDataType))
{
VDSetErrorInfo(IDS_ERR_BADCURSORBINDINFO, IID_ICursorUpdateARow, m_pResourceDLL);
return CURSOR_DB_E_BADBINDINFO;
}
// if a variant binding was specified make sure the cursor type is not CURSOR_DBTYPE_CHARS,
// CURSOR_DBTYPE_WCHARS or CURSOR_DBTYPE_BYTES
if (pCursorBindParams->dwBinding & CURSOR_DBBINDING_VARIANT)
{
if (pCursorBindParams->dwDataType == CURSOR_DBTYPE_CHARS ||
pCursorBindParams->dwDataType == CURSOR_DBTYPE_WCHARS ||
pCursorBindParams->dwDataType == CURSOR_DBTYPE_BYTES)
{
VDSetErrorInfo(IDS_ERR_BADCURSORBINDINFO, IID_ICursorUpdateARow, m_pResourceDLL);
return CURSOR_DB_E_BADBINDINFO;
}
}
// if its not a variant binding make sure the cursor type is not CURSOR_DBTYPE_ANYVARIANT
if (!(pCursorBindParams->dwBinding & CURSOR_DBBINDING_VARIANT) &&
pCursorBindParams->dwDataType == CURSOR_DBTYPE_ANYVARIANT)
{
VDSetErrorInfo(IDS_ERR_BADCURSORBINDINFO, IID_ICursorUpdateARow, m_pResourceDLL);
return CURSOR_DB_E_BADBINDINFO;
}
return S_OK;
}
//=--------------------------------------------------------------------------=
// ValidateEntryID - Validate entry identifier
//=--------------------------------------------------------------------------=
// This function makes sure the specified enrty identifier is acceptable,
// if it is, then return rowset column and hRow associated with it
//
// Parameters:
// cbEntryID - [in] the size of the entryID
// pEntryID - [in] a pointer to the entryID
// ppColumn - [out] a pointer to memory in which to return rowset column pointer
// phRow - [out] a pointer to memory in which to return row handle
//
// Output:
// HRESULT - S_OK if successful
// E_INVALIDARG bad parameter
// CURSOR_DB_E_BADENTRYID bad entry identifier
//
HRESULT CVDCursor::ValidateEntryID(ULONG cbEntryID, BYTE * pEntryID, CVDRowsetColumn ** ppColumn, HROW * phRow)
{
ASSERT_POINTER(pEntryID, BYTE)
ASSERT_POINTER(ppColumn, CVDRowsetColumn*)
ASSERT_POINTER(phRow, HROW)
IRowsetLocate * pRowsetLocate = GetRowsetLocate();
// make sure we have a valid rowset locate pointer
if (!pRowsetLocate || !IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_IEntryID, m_pResourceDLL);
return E_FAIL;
}
// make sure we have all necessary pointers
if (!pEntryID || !ppColumn || !phRow)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_IEntryID, m_pResourceDLL);
return E_INVALIDARG;
}
// init out parameters
*ppColumn = NULL;
*phRow = NULL;
// check length of enrtyID
if (cbEntryID != sizeof(ULONG) + sizeof(ULONG) + GetCursorMain()->GetMaxBookmarkLen())
{
VDSetErrorInfo(IDS_ERR_BADENTRYID, IID_IEntryID, m_pResourceDLL);
return CURSOR_DB_E_BADENTRYID;
}
// extract column ordinal
ULONG ulOrdinal = *(ULONG*)pEntryID;
// make sure column ordinal is okay
BOOL fColumnOrdinalOkay = FALSE;
ULONG ulColumns = GetCursorMain()->GetColumnsCount();
CVDRowsetColumn * pColumn = GetCursorMain()->InternalGetColumns();
// iterate through rowset columns looking for match
for (ULONG ulCol = 0; ulCol < ulColumns && !fColumnOrdinalOkay; ulCol++)
{
if (ulOrdinal == pColumn->GetOrdinal())
fColumnOrdinalOkay = TRUE;
else
pColumn++;
}
// if not found, get out
if (!fColumnOrdinalOkay)
{
VDSetErrorInfo(IDS_ERR_BADENTRYID, IID_IEntryID, m_pResourceDLL);
return CURSOR_DB_E_BADENTRYID;
}
// set column pointer
*ppColumn = pColumn;
// extract row bookmark
ULONG cbBookmark = *(ULONG*)(pEntryID + sizeof(ULONG));
BYTE * pBookmark = (BYTE*)pEntryID + sizeof(ULONG) + sizeof(ULONG);
// attempt to retrieve hRow from bookmark
HRESULT hr = pRowsetLocate->GetRowsByBookmark(0, 1, &cbBookmark, (const BYTE**)&pBookmark, phRow, NULL);
if (FAILED(hr))
VDSetErrorInfo(IDS_ERR_BADENTRYID, IID_IEntryID, m_pResourceDLL);
return hr;
}
//=--------------------------------------------------------------------------=
// QueryEntryIDInterface - Get specified interface for entry identifier
//=--------------------------------------------------------------------------=
// This function attempts to get the requested interface from the specified
// column row
//
// Parameters:
// pColumn - [in] rowset column pointer
// hRow - [in] the row handle
// dwFlags - [in] interface specific flags
// riid - [in] interface identifier requested
// ppUnknown - [out] a pointer to memory in which to return interface pointer
//
// Output:
// HRESULT - S_OK if successful
// E_FAIL error occured
// E_INVALIDARG bad parameter
// E_OUTOFMEMORY not enough memory
// E_NOINTERFACE interface not available
//
HRESULT CVDCursor::QueryEntryIDInterface(CVDRowsetColumn * pColumn, HROW hRow, DWORD dwFlags, REFIID riid,
IUnknown ** ppUnknown)
{
ASSERT_POINTER(pColumn, CVDRowsetColumn)
ASSERT_POINTER(ppUnknown, IUnknown*)
IRowset * pRowset = GetRowset();
IAccessor * pAccessor = GetAccessor();
// make sure we have valid rowset and accessor pointers
if (!pRowset || !pAccessor || !IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_IEntryID, m_pResourceDLL);
return E_FAIL;
}
// make sure we have all necessay pointers
if (!pColumn || !ppUnknown)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_IEntryID, m_pResourceDLL);
return E_INVALIDARG;
}
// init out parameters
*ppUnknown = NULL;
DBOBJECT object;
DBBINDING binding;
// clear out binding
memset(&binding, 0, sizeof(DBBINDING));
// create interface binding
binding.iOrdinal = pColumn->GetOrdinal();
binding.pObject = &object;
binding.pObject->dwFlags = dwFlags;
binding.pObject->iid = riid;
binding.dwPart = DBPART_VALUE;
binding.dwMemOwner = DBMEMOWNER_CLIENTOWNED;
binding.cbMaxLen = sizeof(IUnknown*);
binding.wType = DBTYPE_IUNKNOWN;
HACCESSOR hAccessor;
// create interface accessor
HRESULT hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &binding, 0, &hAccessor, NULL);
if (FAILED(hr))
return E_NOINTERFACE;
IUnknown * pUnknown = NULL;
// try to get interface
hr = pRowset->GetData(hRow, hAccessor, &pUnknown);
// release interface accessor
pAccessor->ReleaseAccessor(hAccessor, NULL);
if (FAILED(hr))
return E_NOINTERFACE;
// return pointer
*ppUnknown = pUnknown;
return hr;
}
#ifndef VD_DONT_IMPLEMENT_ISTREAM
//=--------------------------------------------------------------------------=
// CreateEntryIDStream - Create stream for entry identifier
//=--------------------------------------------------------------------------=
// This function retrieves supplied column row's data and create a
// stream containing this data
//
// Parameters:
// pColumn - [in] rowset column pointer
// hRow - [in] the row handle
// ppStream - [out] a pointer to memory in which to return stream pointer
//
// Output:
// HRESULT - S_OK if successful
// E_FAIL error occured
// E_INVALIDARG bad parameter
// E_OUTOFMEMORY not enough memory
//
HRESULT CVDCursor::CreateEntryIDStream(CVDRowsetColumn * pColumn, HROW hRow, IStream ** ppStream)
{
ASSERT_POINTER(pColumn, CVDRowsetColumn)
ASSERT_POINTER(ppStream, IStream*)
IRowset * pRowset = GetRowset();
IAccessor * pAccessor = GetAccessor();
// make sure we have valid rowset and accessor pointers
if (!pRowset || !pAccessor || !IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_IEntryID, m_pResourceDLL);
return E_FAIL;
}
// make sure we have all necessay pointers
if (!pColumn || !ppStream)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_IEntryID, m_pResourceDLL);
return E_INVALIDARG;
}
// init out parameters
*ppStream = NULL;
DBBINDING binding;
// clear out binding
memset(&binding, 0, sizeof(DBBINDING));
// create length binding
binding.iOrdinal = pColumn->GetOrdinal();
binding.obLength = 0;
binding.dwPart = DBPART_LENGTH;
binding.dwMemOwner = DBMEMOWNER_CLIENTOWNED;
binding.wType = DBTYPE_BYTES;
HACCESSOR hAccessor;
// create length accessor
HRESULT hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &binding, 0, &hAccessor, NULL);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_CREATEACCESSORFAILED, IID_IEntryID, pAccessor, IID_IAccessor, m_pResourceDLL);
if (FAILED(hr))
return hr;
ULONG cbData;
// get size of data
hr = pRowset->GetData(hRow, hAccessor, &cbData);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETDATAFAILED, IID_IEntryID, pRowset, IID_IRowset, m_pResourceDLL);
// release length accessor
pAccessor->ReleaseAccessor(hAccessor, NULL);
if (FAILED(hr))
return hr;
// create value binding
binding.iOrdinal = pColumn->GetOrdinal();
binding.obValue = 0;
binding.dwPart = DBPART_VALUE;
binding.dwMemOwner = DBMEMOWNER_CLIENTOWNED;
binding.cbMaxLen = cbData;
binding.wType = DBTYPE_BYTES;
// create value accessor
hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &binding, 0, &hAccessor, NULL);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_CREATEACCESSORFAILED, IID_IEntryID, pAccessor, IID_IAccessor, m_pResourceDLL);
if (FAILED(hr))
return hr;
// create data buffer
HGLOBAL hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD, cbData);
if (!hData)
{
// release value accessor
pAccessor->ReleaseAccessor(hAccessor, NULL);
VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_IEntryID, m_pResourceDLL);
return E_OUTOFMEMORY;
}
// get pointer to data buffer
BYTE * pData = (BYTE*)GlobalLock(hData);
// get data value
hr = pRowset->GetData(hRow, hAccessor, pData);
// release pointer to data buffer
GlobalUnlock(hData);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETDATAFAILED, IID_IEntryID, pRowset, IID_IRowset, m_pResourceDLL);
// release value accessor
pAccessor->ReleaseAccessor(hAccessor, NULL);
if (FAILED(hr))
{
GlobalFree(hData);
return hr;
}
// create stream containing data
hr = CreateStreamOnHGlobal(hData, TRUE, ppStream);
if (FAILED(hr))
GlobalFree(hData);
return hr;
}
#endif //VD_DONT_IMPLEMENT_ISTREAM
//=--------------------------------------------------------------------------=
// MakeAdjustments - Make adjustments to fixed length buffer accessor bindings
//=--------------------------------------------------------------------------=
// This function makes adjustments to the fixed length buffer accessor
// bindings, after a call to CreateAccessor fails, to try and make the
// binding more suitable
//
// Parameters:
// ulBindings - [in] number of fixed length buffer bindings
// pBindings - [in] a pointer to fixed length buffer bindings
// pulIndex - [in] a pointer to an array of indices, which
// specify which cursor binding each fixed
// length buffer binding applies
// ulTotalBindings - [in] number of cursor bindings
// prghAdjustAccessors - [out] a pointer to memory in which to return
// a pointer to adjusted fixed length buffer
// accessors
// ppdwAdjustFlags - [out] a pointer to memory in which to return
// a pointer to adjusted fixed length buffer
// accessors flags
// fBefore - [in] a flag which indicated whether this call
// has been made before or after the call
// to CreateAccessor
//
// Output:
// S_OK - if adjustments were made
// E_FAIL - failed to make any adjustments
// E_OUTOFMEMORY - not enough memory
// E_INVALIDARG - bad parameter
//
// Notes:
// Specifically, this function can make the following adjustments...
// (1) Change variant binding byte field -> byte binding (fails in GetData)
// (2) Change variant binding date field -> wide string binding (fails in CreateAccessor)
// (3) Change variant binding memo field -> string binding (fails in CreateAccessor)
//
HRESULT CVDCursor::MakeAdjustments(ULONG ulBindings, DBBINDING * pBindings, ULONG * pulIndex, ULONG ulTotalBindings,
HACCESSOR ** prghAdjustAccessors, DWORD ** ppdwAdjustFlags, BOOL fBefore)
{
ASSERT_POINTER(pBindings, DBBINDING)
ASSERT_POINTER(pulIndex, ULONG)
ASSERT_POINTER(prghAdjustAccessors, HACCESSOR*)
ASSERT_POINTER(ppdwAdjustFlags, DWORD*)
IAccessor * pAccessor = GetAccessor();
// make sure we have a valid accessor pointer
if (!pAccessor || !IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursor, m_pResourceDLL);
return E_FAIL;
}
// make sure we have all necessary pointers
if (!pBindings || !pulIndex || !prghAdjustAccessors || !ppdwAdjustFlags)
return E_INVALIDARG;
BOOL fWeAllocatedMemory = FALSE;
// try to get storage for adjusted accessors and flags
HACCESSOR * rghAdjustAccessors = *prghAdjustAccessors;
DWORD * pdwAdjustFlags = *ppdwAdjustFlags;
// if not supplied, then create storage
if (!rghAdjustAccessors || !pdwAdjustFlags)
{
rghAdjustAccessors = new HACCESSOR[ulTotalBindings];
pdwAdjustFlags = new DWORD[ulTotalBindings];
// make sure we got the requested memory
if (!rghAdjustAccessors || !pdwAdjustFlags)
{
delete [] rghAdjustAccessors;
delete [] pdwAdjustFlags;
VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL);
return E_OUTOFMEMORY;
}
// clear adjusted accessors and flags
memset(rghAdjustAccessors, 0, ulTotalBindings * sizeof(HACCESSOR));
memset(pdwAdjustFlags, 0, ulTotalBindings * sizeof(DWORD));
fWeAllocatedMemory = TRUE;
}
// initialize variables
DBBINDING * pBinding = pBindings;
CVDRowsetColumn * pColumn;
DBTYPE wType;
ULONG cbMaxLength;
DBBINDING binding;
HRESULT hr;
HACCESSOR hAccessor;
HRESULT hrAdjust = E_FAIL;
// iterate through fixed length buffer bindings
for (ULONG ulBind = 0; ulBind < ulBindings; ulBind++)
{
// first check for a variant binding, where value is to be returned
if (pBinding->wType == DBTYPE_VARIANT && (pBinding->dwPart & DBPART_VALUE))
{
// get rowset column associated with this binding
pColumn = GetRowsetColumn(pBinding->iOrdinal);
if (pColumn)
{
// get attributes of this column
wType = pColumn->GetType();
cbMaxLength = pColumn->GetMaxLength();
// check for a byte field
if (fBefore && wType == DBTYPE_UI1)
{
// make adjustments to fixed length buffer binding
pBinding->wType = DBTYPE_UI1;
// store associated flag
pdwAdjustFlags[pulIndex[ulBind]] = VD_ADJUST_VARIANT_TO_BYTE;
// we succeeded
hrAdjust = S_OK;
}
// check for a date field
if (!fBefore && wType == DBTYPE_DBTIMESTAMP)
{
// clear binding
memset(&binding, 0, sizeof(DBBINDING));
// create adjusted accessor binding
binding.iOrdinal = pBinding->iOrdinal;
binding.dwPart = DBPART_VALUE;
binding.dwMemOwner = DBMEMOWNER_CLIENTOWNED;
binding.cbMaxLen = 0x7FFFFFFF;
binding.wType = DBTYPE_WSTR;
// try to create adjusted accessor
hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &binding, 0, &hAccessor, NULL);
if (SUCCEEDED(hr))
{
// make adjustments to fixed length buffer binding
pBinding->obLength = pBinding->obValue;
pBinding->dwPart &= ~DBPART_VALUE;
pBinding->dwPart |= DBPART_LENGTH;
pBinding->wType = DBTYPE_WSTR;
// store adjusted accessor and associated flag
rghAdjustAccessors[pulIndex[ulBind]] = hAccessor;
pdwAdjustFlags[pulIndex[ulBind]] = VD_ADJUST_VARIANT_TO_WSTR;
// we succeeded
hrAdjust = S_OK;
}
}
// check for a memo field
if (!fBefore && wType == DBTYPE_STR && cbMaxLength >= 0x40000000)
{
// clear binding
memset(&binding, 0, sizeof(DBBINDING));
// create adjusted accessor binding
binding.iOrdinal = pBinding->iOrdinal;
binding.dwPart = DBPART_VALUE;
binding.dwMemOwner = DBMEMOWNER_CLIENTOWNED;
binding.cbMaxLen = 0x7FFFFFFF;
binding.wType = DBTYPE_STR;
// try to create adjusted accessor
hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &binding, 0, &hAccessor, NULL);
if (SUCCEEDED(hr))
{
// make adjustments to fixed length buffer binding
pBinding->obLength = pBinding->obValue;
pBinding->dwPart &= ~DBPART_VALUE;
pBinding->dwPart |= DBPART_LENGTH;
pBinding->wType = DBTYPE_STR;
// store adjusted accessor and associated flag
rghAdjustAccessors[pulIndex[ulBind]] = hAccessor;
pdwAdjustFlags[pulIndex[ulBind]] = VD_ADJUST_VARIANT_TO_STR;
// we succeeded
hrAdjust = S_OK;
}
}
}
}
pBinding++;
}
if (SUCCEEDED(hrAdjust))
{
// if we made any adjustments, return accessors and flags
*prghAdjustAccessors = rghAdjustAccessors;
*ppdwAdjustFlags = pdwAdjustFlags;
}
else if (fWeAllocatedMemory)
{
// destroy allocated memory
delete [] rghAdjustAccessors;
delete [] pdwAdjustFlags;
}
return hrAdjust;
}
//=--------------------------------------------------------------------------=
// ReCreateAccessors - Re-create accessors
//=--------------------------------------------------------------------------=
// This function attempts to recreate accessors based on old and new bindings
//
// Parameters:
// ulNewCursorBindings - [in] the number of new cursor column bindings
// pNewCursorBindings - [in] an array of new cursor column bindings
// dwFlags - [in] a flag that specifies whether to replace the
// existing column bindings or add to them
//
// Output:
// HRESULT - S_OK if successful
// E_OUTOFMEMORY not enough memory to create object
//
// Notes:
//
HRESULT CVDCursor::ReCreateAccessors(ULONG ulNewCursorBindings, CURSOR_DBCOLUMNBINDING * pNewCursorBindings, DWORD dwFlags)
{
IAccessor * pAccessor = GetAccessor();
// make sure we have a valid accessor pointer
if (!pAccessor || !IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursor, m_pResourceDLL);
return E_FAIL;
}
ULONG ulOldCursorBindings = 0;
CURSOR_DBCOLUMNBINDING * pOldCursorBindings = NULL;
// if we're adding bindings include old bindings
if (dwFlags == CURSOR_DBCOLUMNBINDOPTS_ADD)
{
ulOldCursorBindings = m_ulCursorBindings;
pOldCursorBindings = m_pCursorBindings;
}
// get total binding count (sum of old and new)
ULONG ulTotalBindings = ulOldCursorBindings + ulNewCursorBindings;
ULONG * pulIndex = NULL;
DBBINDING * pBindings = NULL;
DBBINDING * pHelperBindings = NULL;
DBBINDING * pVarBindings = NULL;
if (ulTotalBindings)
{
// create storage for new rowset bindings
pulIndex = new ULONG[ulTotalBindings];
pBindings = new DBBINDING[ulTotalBindings];
pHelperBindings = new DBBINDING[ulTotalBindings];
pVarBindings = new DBBINDING[ulTotalBindings];
// make sure we got all requested memory
if (!pulIndex || !pBindings || !pHelperBindings || !pVarBindings)
{
delete [] pulIndex;
delete [] pBindings;
delete [] pHelperBindings;
delete [] pVarBindings;
VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL);
return E_OUTOFMEMORY;
}
// clear rowset bindings
memset(pulIndex, 0, ulTotalBindings * sizeof(ULONG));
memset(pBindings, 0, ulTotalBindings * sizeof(DBBINDING));
memset(pHelperBindings, 0, ulTotalBindings * sizeof(DBBINDING));
memset(pVarBindings, 0, ulTotalBindings * sizeof(DBBINDING));
}
// set adjustments to null
HACCESSOR * rghAdjustAccessors = NULL;
DWORD * pdwAdjustFlags = NULL;
HRESULT hr;
WORD wType;
ULONG ulBindings = 0;
ULONG ulHelperBindings = 0;
ULONG ulVarBindings = 0;
ULONG obVarDataInfo = 0;
DBBINDING * pBinding = pBindings;
DBBINDING * pHelperBinding = pHelperBindings;
DBBINDING * pVarBinding = pVarBindings;
CURSOR_DBCOLUMNBINDING * pCursorBinding = pOldCursorBindings;
CVDRowsetColumn * pColumn;
BOOL fEntryIDBinding;
// iterate through cursor bindings and set rowset bindings
for (ULONG ulCol = 0; ulCol < ulTotalBindings; ulCol++)
{
// if necessary, switch to new bindings
if (ulCol == ulOldCursorBindings)
pCursorBinding = pNewCursorBindings;
// get rowset column for this binding
pColumn = GetRowsetColumn(pCursorBinding->columnID);
// get desired rowset datatype
wType = CVDRowsetColumn::CursorTypeToType((CURSOR_DBVARENUM)pCursorBinding->dwDataType);
// set entryID binding flag
fEntryIDBinding = (pCursorBinding->dwBinding & CURSOR_DBBINDING_ENTRYID);
// check for datatypes which require variable length buffer
if (DoesCursorTypeNeedVarData(pCursorBinding->dwDataType))
{
// create fixed length buffer binding
pBinding->iOrdinal = pColumn->GetOrdinal();
pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED;
pBinding->wType = wType;
// determine offset to the length part
if (pCursorBinding->obVarDataLen != CURSOR_DB_NOVALUE)
{
pBinding->obLength = pCursorBinding->obVarDataLen;
pBinding->dwPart |= DBPART_LENGTH;
}
// determine offset to the status part
if (pCursorBinding->obInfo != CURSOR_DB_NOVALUE)
{
pBinding->obStatus = pCursorBinding->obInfo;
pBinding->dwPart |= DBPART_STATUS;
}
// BLOBs always require the length part
if (pCursorBinding->dwDataType == CURSOR_DBTYPE_BLOB &&
pCursorBinding->obData != CURSOR_DB_NOVALUE && !fEntryIDBinding)
{
pBinding->obLength = pCursorBinding->obData;
pBinding->dwPart |= DBPART_LENGTH;
}
// bookmark columns require native type
if (!pColumn->GetDataColumn())
pBinding->wType = pColumn->GetType();
// create variable length helper buffer binding
if (!pColumn->GetFixed() && !fEntryIDBinding)
{
// if column contains variable length data, then create binding
pHelperBinding->iOrdinal = pColumn->GetOrdinal();
pHelperBinding->obLength = obVarDataInfo;
pHelperBinding->obStatus = obVarDataInfo + sizeof(ULONG);
pHelperBinding->dwPart = DBPART_LENGTH | DBPART_STATUS;
pHelperBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED;
pHelperBinding->wType = wType;
}
// always increase offset in helper buffer
obVarDataInfo += sizeof(ULONG) + sizeof(DBSTATUS);
// create variable length buffer binding
pVarBinding->iOrdinal = pColumn->GetOrdinal();
pVarBinding->dwPart = DBPART_VALUE;
pVarBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED;
pVarBinding->cbMaxLen = pCursorBinding->cbMaxLen;
pVarBinding->wType = wType;
// adjust for no maximum length
if (pVarBinding->cbMaxLen == CURSOR_DB_NOMAXLENGTH)
pVarBinding->cbMaxLen = 0x7FFFFFFF;
}
else // datatype requires only fixed length buffer
{
// create fixed length buffer binding
pBinding->iOrdinal = pColumn->GetOrdinal();
pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED;
pBinding->cbMaxLen = pCursorBinding->cbMaxLen;
pBinding->wType = wType;
// determine offset to the value part
if (pCursorBinding->obData != CURSOR_DB_NOVALUE && !fEntryIDBinding)
{
pBinding->obValue = pCursorBinding->obData;
pBinding->dwPart |= DBPART_VALUE;
}
// determine offset to the length part
if (pCursorBinding->obVarDataLen != CURSOR_DB_NOVALUE)
{
pBinding->obLength = pCursorBinding->obVarDataLen;
pBinding->dwPart |= DBPART_LENGTH;
}
// determine offset to the status part
if (pCursorBinding->obInfo != CURSOR_DB_NOVALUE)
{
pBinding->obStatus = pCursorBinding->obInfo;
pBinding->dwPart |= DBPART_STATUS;
}
// BYTES always require the length part
if (pCursorBinding->dwDataType == CURSOR_DBTYPE_BYTES &&
pCursorBinding->obData != CURSOR_DB_NOVALUE && !fEntryIDBinding)
{
pBinding->obLength = pCursorBinding->obData;
pBinding->obValue += sizeof(ULONG);
pBinding->dwPart |= DBPART_LENGTH;
}
// check for variant binding, in which case ask for variant
if (pCursorBinding->dwBinding & CURSOR_DBBINDING_VARIANT)
pBinding->wType = DBTYPE_VARIANT;
}
// if any parts needed, increment fixed buffer binding
if (pBinding->dwPart)
{
pulIndex[ulBindings] = ulCol;
ulBindings++;
pBinding++;
}
// if any parts needed, increment variable buffer helper binding
if (pHelperBinding->dwPart)
{
ulHelperBindings++;
pHelperBinding++;
}
// if any parts needed, increment variable buffer binding count
if (pVarBinding->dwPart)
{
// entryID bindings do not need value part
if (fEntryIDBinding)
pVarBinding->dwPart &= ~DBPART_VALUE;
ulVarBindings++;
}
// however, always increment variable buffer binding
pVarBinding++;
// get next cursor binding
pCursorBinding++;
}
hr = S_OK;
// try to create fixed length buffer accessor
HACCESSOR hAccessor = 0;
if (ulBindings)
{
// make adjustments that can cause failure in GetData
MakeAdjustments(ulBindings, pBindings, pulIndex, ulTotalBindings, &rghAdjustAccessors, &pdwAdjustFlags, TRUE);
hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, ulBindings, pBindings, 0, &hAccessor, NULL);
}
if (FAILED(hr))
{
// make other known adjustments that can cause CreateAccessor to fail
hr = MakeAdjustments(ulBindings, pBindings, pulIndex, ulTotalBindings, &rghAdjustAccessors, &pdwAdjustFlags, FALSE);
if (SUCCEEDED(hr))
hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, ulBindings, pBindings, 0, &hAccessor, NULL);
}
delete [] pulIndex;
delete [] pBindings;
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_CREATEACCESSORFAILED, IID_ICursor, pAccessor, IID_IAccessor, m_pResourceDLL);
if (FAILED(hr))
{
delete [] pHelperBindings;
delete [] pVarBindings;
ReleaseAccessorArray(rghAdjustAccessors);
delete [] rghAdjustAccessors;
delete [] pdwAdjustFlags;
return hr;
}
// try to create variable length buffer accessors helper
HACCESSOR hVarHelper = 0;
if (ulHelperBindings)
hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, ulHelperBindings, pHelperBindings, 0, &hVarHelper, NULL);
delete [] pHelperBindings;
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_CREATEACCESSORFAILED, IID_ICursor, pAccessor, IID_IAccessor, m_pResourceDLL);
if (FAILED(hr))
{
pAccessor->ReleaseAccessor(hAccessor, NULL);
delete [] pVarBindings;
ReleaseAccessorArray(rghAdjustAccessors);
delete [] rghAdjustAccessors;
delete [] pdwAdjustFlags;
return hr;
}
// try to create variable length buffer accessors
HACCESSOR * rghVarAccessors = NULL;
if (ulTotalBindings)
{
rghVarAccessors = new HACCESSOR[ulTotalBindings];
if (!rghVarAccessors)
hr = E_OUTOFMEMORY;
else
{
pVarBinding = pVarBindings;
memset(rghVarAccessors, 0, ulTotalBindings * sizeof(HACCESSOR));
// iterate through rowset bindings and create accessor for one which have a part
for (ULONG ulBind = 0; ulBind < ulTotalBindings && SUCCEEDED(hr); ulBind++)
{
if (pVarBinding->dwPart)
hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, pVarBinding, 0, &rghVarAccessors[ulBind], NULL);
pVarBinding++;
}
}
}
delete [] pVarBindings;
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_CREATEACCESSORFAILED, IID_ICursor, pAccessor, IID_IAccessor, m_pResourceDLL);
if (FAILED(hr))
{
if (rghVarAccessors)
{
// iterate through rowset bindings and destroy any created accessors
for (ULONG ulBind = 0; ulBind < ulTotalBindings; ulBind++)
{
if (rghVarAccessors[ulBind])
pAccessor->ReleaseAccessor(rghVarAccessors[ulBind], NULL);
}
delete [] rghVarAccessors;
}
pAccessor->ReleaseAccessor(hAccessor, NULL);
pAccessor->ReleaseAccessor(hVarHelper, NULL);
ReleaseAccessorArray(rghAdjustAccessors);
delete [] rghAdjustAccessors;
delete [] pdwAdjustFlags;
return hr;
}
// destroy old accessors
DestroyAccessors();
// store new accessors
m_hAccessor = hAccessor;
m_hVarHelper = hVarHelper;
m_ulVarBindings = ulVarBindings;
m_rghVarAccessors = rghVarAccessors;
m_rghAdjustAccessors = rghAdjustAccessors;
m_pdwAdjustFlags = pdwAdjustFlags;
return hr;
}
//=--------------------------------------------------------------------------=
// ReleaseAccessorArray - Release all accessors in specified array
//
void CVDCursor::ReleaseAccessorArray(HACCESSOR * rghAccessors)
{
IAccessor * pAccessor = GetAccessor();
if (pAccessor && rghAccessors)
{
for (ULONG ulBind = 0; ulBind < m_ulCursorBindings; ulBind++)
{
if (rghAccessors[ulBind])
{
pAccessor->ReleaseAccessor(rghAccessors[ulBind], NULL);
rghAccessors[ulBind] = NULL;
}
}
}
}
//=--------------------------------------------------------------------------=
// DestroyAccessors - Destroy all rowset accessors
//
void CVDCursor::DestroyAccessors()
{
IAccessor * pAccessor = GetAccessor();
if (pAccessor && m_hAccessor)
{
pAccessor->ReleaseAccessor(m_hAccessor, NULL);
m_hAccessor = 0;
}
if (pAccessor && m_hVarHelper)
{
pAccessor->ReleaseAccessor(m_hVarHelper, NULL);
m_hVarHelper = 0;
}
m_ulVarBindings = 0;
ReleaseAccessorArray(m_rghVarAccessors);
delete [] m_rghVarAccessors;
m_rghVarAccessors = NULL;
ReleaseAccessorArray(m_rghAdjustAccessors);
delete [] m_rghAdjustAccessors;
m_rghAdjustAccessors = NULL;
delete [] m_pdwAdjustFlags;
m_pdwAdjustFlags = NULL;
}
//=--------------------------------------------------------------------------=
// ReCreateColumns - Re-create rowset columns associated with current bindings
//
HRESULT CVDCursor::ReCreateColumns()
{
DestroyColumns();
if (m_ulCursorBindings)
{
m_ppColumns = new CVDRowsetColumn*[m_ulCursorBindings];
if (!m_ppColumns)
{
VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL);
return E_OUTOFMEMORY;
}
CURSOR_DBCOLUMNBINDING * pCursorBinding = m_pCursorBindings;
for (ULONG ulBind = 0; ulBind < m_ulCursorBindings; ulBind++)
{
m_ppColumns[ulBind] = GetRowsetColumn(pCursorBinding->columnID);
pCursorBinding++;
}
}
return S_OK;
}
//=--------------------------------------------------------------------------=
// DestroyColumns - Destroy rowset column pointers
//
void CVDCursor::DestroyColumns()
{
delete [] m_ppColumns;
m_ppColumns = NULL;
}
//=--------------------------------------------------------------------------=
// InsertNewRow - Insert a new row and set in cursor position object
//
HRESULT CVDCursor::InsertNewRow()
{
IRowset * pRowset = GetRowset();
IAccessor * pAccessor = GetAccessor();
IRowsetChange * pRowsetChange = GetRowsetChange();
// make sure we have valid rowset, accessor and change pointers
if (!pRowset || !pAccessor || !pRowsetChange || !IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorUpdateARow, m_pResourceDLL);
return E_FAIL;
}
HACCESSOR hAccessor;
// create null accessor
HRESULT hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 0, NULL, 0, &hAccessor, NULL);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_CREATEACCESSORFAILED, IID_ICursorUpdateARow, pAccessor, IID_IAccessor,
m_pResourceDLL);
if (FAILED(hr))
return hr;
HROW hRow;
// insert an empty row using null accessor (set/clear internal insert row flag)
GetCursorMain()->SetInternalInsertRow(TRUE);
hr = pRowsetChange->InsertRow(0, hAccessor, NULL, &hRow);
GetCursorMain()->SetInternalInsertRow(FALSE);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_INSERTROWFAILED, IID_ICursorUpdateARow, pRowsetChange, IID_IRowsetChange,
m_pResourceDLL);
// release null accessor
pAccessor->ReleaseAccessor(hAccessor, NULL);
if (FAILED(hr))
return hr;
// set hRow in cursor position object
hr = m_pCursorPosition->SetAddHRow(hRow);
// release our reference on hRow
pRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL);
return hr;
}
//=--------------------------------------------------------------------------=
// GetOriginalColumn - Get original column data using same-row clone
//
HRESULT CVDCursor::GetOriginalColumn(CVDRowsetColumn * pColumn, CURSOR_DBBINDPARAMS * pBindParams)
{
ASSERT_POINTER(pColumn, CVDRowsetColumn)
ASSERT_POINTER(pBindParams, CURSOR_DBBINDPARAMS)
// make sure we have all necessary pointers
if (!pColumn || !pBindParams || !pBindParams->pData)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorUpdateARow, m_pResourceDLL);
return E_INVALIDARG;
}
// get hRow of the row currently being edited
HROW hRow = m_pCursorPosition->GetEditRow();
// see if we already have a same-row clone
ICursor * pSameRowClone = m_pCursorPosition->GetSameRowClone();
if (!pSameRowClone)
{
// if not, create new same-row clone
HRESULT hr = Clone(CURSOR_DBCLONEOPTS_SAMEROW, IID_ICursor, (IUnknown**)&pSameRowClone);
if (FAILED(hr))
return hr;
// set same-row clone in cursor position object
m_pCursorPosition->SetSameRowClone(pSameRowClone);
}
CURSOR_DBCOLUMNBINDING columnBinding;
// set common column binding members
columnBinding.columnID = pColumn->GetCursorColumnID();
columnBinding.obData = CURSOR_DB_NOVALUE;
columnBinding.cbMaxLen = pBindParams->cbMaxLen;
columnBinding.obVarDataLen = CURSOR_DB_NOVALUE;
columnBinding.obInfo = CURSOR_DB_NOVALUE;
columnBinding.dwBinding = CURSOR_DBBINDING_DEFAULT;
columnBinding.dwDataType = pBindParams->dwDataType;
// adjust column binding for variable length datatypes
if (DoesCursorTypeNeedVarData(pBindParams->dwDataType))
{
switch (pBindParams->dwDataType)
{
case CURSOR_DBTYPE_BLOB:
columnBinding.dwDataType = CURSOR_DBTYPE_BYTES;
break;
case CURSOR_DBTYPE_LPSTR:
columnBinding.dwDataType = CURSOR_DBTYPE_CHARS;
break;
case CURSOR_DBTYPE_LPWSTR:
columnBinding.dwDataType = CURSOR_DBTYPE_WCHARS;
break;
}
}
CURSOR_DBFETCHROWS fetchRows;
// set common fetch rows members
fetchRows.cRowsRequested = 1;
fetchRows.dwFlags = CURSOR_DBROWFETCH_DEFAULT;
fetchRows.pVarData = NULL;
fetchRows.cbVarData = 0;
// retrieve length and/or information field if requested
if (pBindParams->cbVarDataLen != CURSOR_DB_NOVALUE || pBindParams->dwInfo != CURSOR_DB_NOVALUE)
{
// set column binding offsets
if (pBindParams->cbVarDataLen != CURSOR_DB_NOVALUE)
columnBinding.obVarDataLen = offsetof(CURSOR_DBBINDPARAMS, cbVarDataLen);
if (pBindParams->dwInfo != CURSOR_DB_NOVALUE)
columnBinding.obInfo = offsetof(CURSOR_DBBINDPARAMS, dwInfo);
// set bindings on same-row clone
HRESULT hr = pSameRowClone->SetBindings(1, &columnBinding, 0, CURSOR_DBCOLUMNBINDOPTS_REPLACE);
if (FAILED(hr))
return hr;
// set fetch rows buffer
fetchRows.pData = pBindParams;
// retrieve length and/or information field from same-row clone
hr = ((CVDCursor*)pSameRowClone)->FillConsumersBuffer(S_OK, &fetchRows, 1, &hRow);
if (FAILED(hr))
return hr;
}
// set column binding offsets and bind-type
columnBinding.obData = 0;
columnBinding.obVarDataLen = CURSOR_DB_NOVALUE;
columnBinding.obInfo = CURSOR_DB_NOVALUE;
columnBinding.dwBinding = pBindParams->dwBinding;
// adjust offsets for variable length datatypes
if (DoesCursorTypeNeedVarData(pBindParams->dwDataType))
{
columnBinding.dwBinding = CURSOR_DBBINDING_DEFAULT;
if (pBindParams->dwBinding & CURSOR_DBBINDING_VARIANT)
{
if (pBindParams->dwDataType == CURSOR_DBTYPE_BLOB)
columnBinding.obVarDataLen = columnBinding.obData;
columnBinding.obData += sizeof(CURSOR_DBVARIANT);
}
else
{
switch (pBindParams->dwDataType)
{
case CURSOR_DBTYPE_BLOB:
columnBinding.obVarDataLen = columnBinding.obData;
columnBinding.obData += sizeof(ULONG) + sizeof(LPBYTE);
break;
case CURSOR_DBTYPE_LPSTR:
columnBinding.obData += sizeof(LPSTR);
break;
case CURSOR_DBTYPE_LPWSTR:
columnBinding.obData += sizeof(LPWSTR);
break;
}
}
}
// set bindings on same-row clone
HRESULT hr = pSameRowClone->SetBindings(1, &columnBinding, pBindParams->cbMaxLen, CURSOR_DBCOLUMNBINDOPTS_REPLACE);
if (FAILED(hr))
return hr;
// set fetch rows buffer
fetchRows.pData = pBindParams->pData;
// retrieve data value from same-row clone
hr = ((CVDCursor*)pSameRowClone)->FillConsumersBuffer(S_OK, &fetchRows, 1, &hRow);
if (FAILED(hr))
return hr;
// place data pointers in buffer for variable length datatypes
if (DoesCursorTypeNeedVarData(pBindParams->dwDataType))
{
BYTE * pData = (BYTE*)pBindParams->pData;
// first check for variant binding
if (pBindParams->dwBinding & CURSOR_DBBINDING_VARIANT)
{
CURSOR_BLOB cursorBlob;
CURSOR_DBVARIANT * pVariant = (CURSOR_DBVARIANT*)pBindParams->pData;
switch (pBindParams->dwDataType)
{
case CURSOR_DBTYPE_BLOB:
cursorBlob.cbSize = *(ULONG*)pVariant;
cursorBlob.pBlobData = pData + sizeof(CURSOR_DBVARIANT);
VariantInit((VARIANT*)pVariant);
pVariant->vt = CURSOR_DBTYPE_BLOB;
pVariant->blob = cursorBlob;
break;
case CURSOR_DBTYPE_LPSTR:
VariantInit((VARIANT*)pVariant);
pVariant->vt = CURSOR_DBTYPE_LPSTR;
pVariant->pszVal = (LPSTR)(pData + sizeof(CURSOR_DBVARIANT));
break;
case CURSOR_DBTYPE_LPWSTR:
VariantInit((VARIANT*)pVariant);
pVariant->vt = CURSOR_DBTYPE_LPSTR;
pVariant->pwszVal = (LPWSTR)(pData + sizeof(CURSOR_DBVARIANT));
break;
}
}
else // otherwise, default binding
{
switch (pBindParams->dwDataType)
{
case CURSOR_DBTYPE_BLOB:
*(LPBYTE*)(pData + sizeof(ULONG)) = pData + sizeof(ULONG) + sizeof(LPBYTE);
break;
case CURSOR_DBTYPE_LPSTR:
*(LPSTR*)pData = (LPSTR)(pData + sizeof(LPSTR));
break;
case CURSOR_DBTYPE_LPWSTR:
*(LPWSTR*)pData = (LPWSTR)(pData + sizeof(LPWSTR));
break;
}
}
}
return S_OK;
}
//=--------------------------------------------------------------------------=
// GetModifiedColumn - Get modified column data from column update object
//
HRESULT CVDCursor::GetModifiedColumn(CVDColumnUpdate * pColumnUpdate, CURSOR_DBBINDPARAMS * pBindParams)
{
ASSERT_POINTER(pColumnUpdate, CVDColumnUpdate)
ASSERT_POINTER(pBindParams, CURSOR_DBBINDPARAMS)
// make sure we have all necessary pointers
if (!pColumnUpdate || !pBindParams || !pBindParams->pData)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorUpdateARow, m_pResourceDLL);
return E_INVALIDARG;
}
// get source variant
CURSOR_DBVARIANT varSrc = pColumnUpdate->GetVariant();
// check for any variant binding
if (pBindParams->dwDataType == CURSOR_DBTYPE_ANYVARIANT)
pBindParams->dwDataType = varSrc.vt;
// determine which type the destination variant should be
VARTYPE vtDest = (VARTYPE)pBindParams->dwDataType;
switch (vtDest)
{
case CURSOR_DBTYPE_BYTES:
vtDest = CURSOR_DBTYPE_BLOB;
break;
case CURSOR_DBTYPE_CHARS:
case CURSOR_DBTYPE_WCHARS:
case CURSOR_DBTYPE_LPSTR:
case CURSOR_DBTYPE_LPWSTR:
vtDest = VT_BSTR;
break;
}
HRESULT hr = S_OK;
CURSOR_DBVARIANT varDest;
BOOL fVariantCreated = FALSE;
// init destination variant
VariantInit((VARIANT*)&varDest);
// get destination variant
if (varSrc.vt != vtDest)
{
// if the types do not match, then create a variant of the desired type
hr = VariantChangeType((VARIANT*)&varDest, (VARIANT*)&varSrc, 0, vtDest);
fVariantCreated = TRUE;
}
else
varDest = varSrc;
if (FAILED(hr))
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorUpdateARow, m_pResourceDLL);
return E_INVALIDARG;
}
// get pointer to data
BYTE * pData = (BYTE*)pBindParams->pData;
// return coerced data
if (pBindParams->dwBinding & CURSOR_DBBINDING_VARIANT)
{
// get pointer to variant data
CURSOR_DBVARIANT * pVariant = (CURSOR_DBVARIANT*)pData;
// return variant
*pVariant = varDest;
// adjust variant for variable length datatypes
if (pBindParams->dwDataType == CURSOR_DBTYPE_BLOB)
{
pVariant->blob.pBlobData = pData + sizeof(CURSOR_DBVARIANT);
memcpy(pData + sizeof(CURSOR_DBVARIANT), varDest.blob.pBlobData, varDest.blob.cbSize);
}
else if (pBindParams->dwDataType == CURSOR_DBTYPE_LPSTR)
{
ULONG cbLength = GET_MBCSLEN_FROMWIDE(varDest.bstrVal);
MAKE_MBCSPTR_FROMWIDE(psz, varDest.bstrVal);
pVariant->pszVal = (LPSTR)(pData + sizeof(CURSOR_DBVARIANT));
memcpy(pData + sizeof(CURSOR_DBVARIANT), psz, min(pBindParams->cbMaxLen, cbLength));
}
else if (pBindParams->dwDataType == CURSOR_DBTYPE_LPWSTR)
{
ULONG cbLength = (lstrlenW(varDest.bstrVal) + 1) * sizeof(WCHAR);
pVariant->pwszVal = (LPWSTR)(pData + sizeof(CURSOR_DBVARIANT));
memcpy(pData + sizeof(CURSOR_DBVARIANT), varDest.bstrVal, min(pBindParams->cbMaxLen, cbLength));
}
else if (pBindParams->dwDataType == VT_BSTR)
{
pVariant->bstrVal = SysAllocString(pVariant->bstrVal);
}
}
else // otherwise, default binding
{
// first check for variable length datatypes
if (pBindParams->dwDataType == CURSOR_DBTYPE_BYTES)
{
*(ULONG*)pData = varDest.blob.cbSize;
memcpy(pData + sizeof(ULONG), varDest.blob.pBlobData, varDest.blob.cbSize);
}
else if (pBindParams->dwDataType == CURSOR_DBTYPE_CHARS)
{
ULONG cbLength = GET_MBCSLEN_FROMWIDE(varDest.bstrVal);
MAKE_MBCSPTR_FROMWIDE(psz, varDest.bstrVal);
memcpy(pData, psz, min(pBindParams->cbMaxLen, cbLength));
}
else if (pBindParams->dwDataType == CURSOR_DBTYPE_WCHARS)
{
ULONG cbLength = (lstrlenW(varDest.bstrVal) + 1) * sizeof(WCHAR);
memcpy(pData, varDest.bstrVal, min(pBindParams->cbMaxLen, cbLength));
}
else if (pBindParams->dwDataType == CURSOR_DBTYPE_BLOB)
{
*(ULONG*)pData = varDest.blob.cbSize;
*(LPBYTE*)(pData + sizeof(ULONG)) = pData + sizeof(ULONG) + sizeof(LPBYTE);
memcpy(pData + sizeof(ULONG) + sizeof(LPBYTE), varDest.blob.pBlobData, varDest.blob.cbSize);
}
else if (pBindParams->dwDataType == CURSOR_DBTYPE_LPSTR)
{
ULONG cbLength = GET_MBCSLEN_FROMWIDE(varDest.bstrVal);
MAKE_MBCSPTR_FROMWIDE(psz, varDest.bstrVal);
*(LPSTR*)pData = (LPSTR)(pData + sizeof(LPSTR));
memcpy(pData + sizeof(LPSTR), psz, min(pBindParams->cbMaxLen, cbLength));
}
else if (pBindParams->dwDataType == CURSOR_DBTYPE_LPWSTR)
{
ULONG cbLength = (lstrlenW(varDest.bstrVal) + 1) * sizeof(WCHAR);
*(LPWSTR*)pData = (LPWSTR)(pData + sizeof(LPWSTR));
memcpy(pData + sizeof(LPWSTR), varDest.bstrVal, min(pBindParams->cbMaxLen, cbLength));
}
else // fixed length datatypes
{
ULONG cbLength = CVDCursorBase::GetCursorTypeLength(pBindParams->dwDataType, 0);
memcpy(pData, &varDest.cyVal, cbLength);
}
}
// if created, destroy variant
if (fVariantCreated)
VariantClear((VARIANT*)&varDest);
return S_OK;
}
//=--------------------------------------------------------------------------=
// Create - Create cursor object
//=--------------------------------------------------------------------------=
// This function creates and initializes a new cursor object
//
// Parameters:
// pCursorPosition - [in] backwards pointer to CVDCursorPosition object
// ppCursor - [out] a pointer in which to return pointer to cursor object
// pResourceDLL - [in] a pointer which keeps track of resource DLL
//
// Output:
// HRESULT - S_OK if successful
// E_OUTOFMEMORY not enough memory to create object
//
// Notes:
//
HRESULT CVDCursor::Create(CVDCursorPosition * pCursorPosition, CVDCursor ** ppCursor, CVDResourceDLL * pResourceDLL)
{
ASSERT_POINTER(pCursorPosition, CVDCursorPosition)
ASSERT_POINTER(ppCursor, CVDCursor*)
ASSERT_POINTER(pResourceDLL, CVDResourceDLL)
if (!pCursorPosition || !ppCursor)
return E_INVALIDARG;
*ppCursor = NULL;
CVDCursor * pCursor = new CVDCursor();
if (!pCursor)
return E_OUTOFMEMORY;
// create connection point container
HRESULT hr = CVDNotifyDBEventsConnPtCont::Create(pCursor, &pCursor->m_pConnPtContainer);
if (FAILED(hr))
{
delete pCursor;
return hr;
}
((CVDNotifier*)pCursorPosition)->AddRef(); // add reference to associated cursor position object
pCursor->m_pCursorPosition = pCursorPosition;
pCursor->m_pResourceDLL = pResourceDLL;
// add to pCursorPosition's notification family
pCursor->JoinFamily(pCursorPosition);
*ppCursor = pCursor;
return S_OK;
}
//=--------------------------------------------------------------------------=
// IUnknown methods implemented
//=--------------------------------------------------------------------------=
//=--------------------------------------------------------------------------=
// IUnknown QueryInterface
//
HRESULT CVDCursor::QueryInterface(REFIID riid, void **ppvObjOut)
{
ASSERT_POINTER(ppvObjOut, IUnknown*)
if (!ppvObjOut)
return E_INVALIDARG;
*ppvObjOut = NULL;
switch (riid.Data1)
{
QI_INTERFACE_SUPPORTED_IF(this, ICursorUpdateARow, GetRowsetChange());
QI_INTERFACE_SUPPORTED_IF(this, ICursorFind, GetRowsetFind());
QI_INTERFACE_SUPPORTED(this, IEntryID);
QI_INTERFACE_SUPPORTED(m_pConnPtContainer, IConnectionPointContainer);
}
if (NULL == *ppvObjOut)
return CVDCursorBase::QueryInterface(riid, ppvObjOut);
CVDNotifier::AddRef();
return S_OK;
}
//=--------------------------------------------------------------------------=
// IUnknown AddRef (this override is needed to instantiate class)
//
ULONG CVDCursor::AddRef(void)
{
return CVDNotifier::AddRef();
}
//=--------------------------------------------------------------------------=
// IUnknown Release (this override is needed to instantiate class)
//
ULONG CVDCursor::Release(void)
{
return CVDNotifier::Release();
}
//=--------------------------------------------------------------------------=
// ICursor methods implemented
//=--------------------------------------------------------------------------=
//=--------------------------------------------------------------------------=
// ICursor GetColumnsCursor
//=--------------------------------------------------------------------------=
// Creates a cursor containing information about the current cursor
//
// Parameters:
// riid - [in] the interface ID to which to return a pointer
// ppvColumnsCursor - [out] a pointer to memory in which to return the
// interface pointer
// pcRows - [out] a pointer to memory in which to return the
// number of rows in the metadata cursor
//
// Output:
// HRESULT - S_OK if successful
// E_FAIL can't create cursor
// E_INVALIDARG bad parameter
// E_OUTOFMEMORY not enough memory
// E_NOINTERFACE interface not available
//
// Notes:
//
HRESULT CVDCursor::GetColumnsCursor(REFIID riid, IUnknown **ppvColumnsCursor, ULONG *pcRows)
{
ASSERT_POINTER(ppvColumnsCursor, IUnknown*)
ASSERT_NULL_OR_POINTER(pcRows, ULONG)
if (!IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursor, m_pResourceDLL);
return E_FAIL;
}
if (!ppvColumnsCursor)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursor, m_pResourceDLL);
return E_INVALIDARG;
}
// init out parameters
*ppvColumnsCursor = NULL;
if (pcRows)
*pcRows = 0;
// make sure caller asked for an available interface
if (riid != IID_IUnknown && riid != IID_ICursor && riid != IID_ICursorMove && riid != IID_ICursorScroll)
{
VDSetErrorInfo(IDS_ERR_NOINTERFACE, IID_ICursor, m_pResourceDLL);
return E_NOINTERFACE;
}
// create metadata cursor
CVDMetadataCursor * pMetadataCursor;
ULONG ulColumns = GetCursorMain()->GetColumnsCount();
CVDRowsetColumn * pColumns = GetCursorMain()->InternalGetColumns();
ULONG ulMetaColumns = GetCursorMain()->GetMetaColumnsCount();
CVDRowsetColumn * pMetaColumns = GetCursorMain()->InternalGetMetaColumns();
if (!GetCursorMain()->IsColumnsRowsetSupported())
ulMetaColumns -= VD_COLUMNSROWSET_MAX_OPT_COLUMNS;
HRESULT hr = CVDMetadataCursor::Create(ulColumns,
pColumns,
ulMetaColumns,
pMetaColumns,
&pMetadataCursor,
m_pResourceDLL);
if (FAILED(hr)) // the only reason for failing here is an out of memory condition
{
VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL);
return hr;
}
*ppvColumnsCursor = (ICursor*)pMetadataCursor;
if (pcRows)
*pcRows = ulColumns;
return S_OK;
}
//=--------------------------------------------------------------------------=
// ICursor SetBindings
//=--------------------------------------------------------------------------=
// Replaces the existing column bindings or adds new column bindings to the
// existing ones
//
// Parameters:
// cCol - [in] the number of columns to bind
// rgBoundColumns - [in] an array of column bindings, one for each
// column for which data is to be returned
// cbRowLength - [in] the number of bytes of inline memory in a
// single row of data
// dwFlags - [in] a flag that specifies whether to replace the
// existing column bindings or add to them
//
// Output:
// HRESULT - S_OK if successful
// E_INVALIDARG bad parameter
// E_OUTOFMEMORY not enough memory
// CURSOR_DB_E_BADBINDINFO bad binding information
// CURSOR_DB_E_COLUMNUNAVAILABLE columnID is not available
// CURSOR_DB_E_ROWTOOSHORT cbRowLength was less than the minumum (and not zero)
//
// Notes:
//
HRESULT CVDCursor::SetBindings(ULONG cCol, CURSOR_DBCOLUMNBINDING rgBoundColumns[], ULONG cbRowLength, DWORD dwFlags)
{
ASSERT_NULL_OR_POINTER(rgBoundColumns, CURSOR_DBCOLUMNBINDING)
if (!IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursor, m_pResourceDLL);
return E_FAIL;
}
if (!cCol && dwFlags == CURSOR_DBCOLUMNBINDOPTS_ADD)
return S_OK;
if (cCol && !rgBoundColumns)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursor, m_pResourceDLL);
return E_INVALIDARG;
}
if (dwFlags != CURSOR_DBCOLUMNBINDOPTS_REPLACE && dwFlags != CURSOR_DBCOLUMNBINDOPTS_ADD)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursor, m_pResourceDLL);
return E_INVALIDARG;
}
// make sure the bindings are okay
ULONG ulColumns = GetCursorMain()->GetColumnsCount();
CVDRowsetColumn * pColumns = GetCursorMain()->InternalGetColumns();
ULONG cbNewRowLength;
ULONG cbNewVarRowLength;
HRESULT hr = ValidateCursorBindings(ulColumns, pColumns, cCol, rgBoundColumns, cbRowLength, dwFlags,
&cbNewRowLength, &cbNewVarRowLength);
if (SUCCEEDED(hr))
{
// if so, then try to create new accessors
hr = ReCreateAccessors(cCol, rgBoundColumns, dwFlags);
if (SUCCEEDED(hr))
{
// if all is okay, then set bindings in cursor
hr = CVDCursorBase::SetBindings(cCol, rgBoundColumns, cbRowLength, dwFlags);
if (SUCCEEDED(hr))
{
// store new row lengths computed during validation
m_cbRowLength = cbNewRowLength;
m_cbVarRowLength = cbNewVarRowLength;
// recreate column pointers
hr = ReCreateColumns();
}
}
}
return hr;
}
//=--------------------------------------------------------------------------=
// FilterNewRow - Filter addrow from fetch
//=--------------------------------------------------------------------------=
// This function determines if the last row fetch was an addrow, in which case
// it releases and removes this row from the block of fetched hRows
//
// Parameters:
// pcRowsObtained - [in/out] a pointer to the number of hRows
// rghRows - [in/out] an array of nRows fetched
// hr - [in] result of fetch
//
// Output:
// HRESULT - E_FAIL rowset is invalid
// E_INVALIDARG bad parameter
// DB_E_BADSTARTPOSITION no rows fetched
// DB_S_ENDOFROWSET reached end of rowset
//
// Notes:
// This function was added to assist in filtering out of add-rows which
// appear as part of the underlying rowset, however should not appear as
// part of the implemeted cursor.
//
HRESULT CVDCursor::FilterNewRow(ULONG * pcRowsObtained, HROW * rghRows, HRESULT hr)
{
ASSERT_POINTER(pcRowsObtained, ULONG)
ASSERT_NULL_OR_POINTER(rghRows, HROW)
IRowset * pRowset = GetRowset();
// make sure we have a valid rowset pointer
if (!pRowset || !IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursor, m_pResourceDLL);
return E_FAIL;
}
// make sure we have necessary pointers
if (!pcRowsObtained || *pcRowsObtained && !rghRows)
return E_INVALIDARG;
if (*pcRowsObtained == 0)
return hr;
// detemine if last row fetched is an addrow
if (GetCursorMain()->IsSameRowAsNew(rghRows[*pcRowsObtained - 1]))
{
// if so, release hRow
pRowset->ReleaseRows(1, &rghRows[*pcRowsObtained - 1], NULL, NULL, NULL);
// decrement fetch count
*pcRowsObtained -= 1;
// return appropriate result
return *pcRowsObtained == 0 ? DB_E_BADSTARTPOSITION : DB_S_ENDOFROWSET;
}
return hr;
}
//=--------------------------------------------------------------------------=
// ICursor GetNextRows
//=--------------------------------------------------------------------------=
// Fetches the specified number of rows starting with the row after the
// current one
//
// Parameters:
// udlRowsToSkip - [in] the number of rows to skip before fetching
// pFetchParams - [in, out] a pointer to fetch rows structure
//
// Output:
// HRESULT - S_OK if successful
// E_FAIL rowset is invalid
// CURSOR_DB_S_ENDOFCURSOR reached end of the cursor
//
// Notes:
//
HRESULT CVDCursor::GetNextRows(LARGE_INTEGER udlRowsToSkip, CURSOR_DBFETCHROWS *pFetchParams)
{
ASSERT_NULL_OR_POINTER(pFetchParams, CURSOR_DBFETCHROWS)
if (!IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursor, m_pResourceDLL);
return E_FAIL;
}
// return if caller doesn't supply fetch rows structure
if (!pFetchParams)
return S_OK;
// vaildate fetch params (implemented on CVDCursorBase
HRESULT hr = ValidateFetchParams(pFetchParams, IID_ICursor);
// return if fetch params are invalid
if (FAILED(hr))
return hr;
// return if caller didn't ask for any rows
if (!pFetchParams->cRowsRequested)
return S_OK;
HRESULT hrFetch;
IRowset * pRowset = GetRowset();
// notify other interested parties
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_CHANGED;
CURSOR_DBNOTIFYREASON rgReasons[1];
rgReasons[0].dwReason = CURSOR_DBREASON_MOVE;
rgReasons[0].arg1 = m_pCursorPosition->m_bmCurrent.GetBookmarkVariant();
VariantInit((VARIANT*)&rgReasons[0].arg2);
rgReasons[0].arg2.vt = VT_I8;
rgReasons[0].arg2.cyVal.Lo = udlRowsToSkip.LowPart;
rgReasons[0].arg2.cyVal.Hi = udlRowsToSkip.HighPart;
hrFetch = m_pCursorPosition->NotifyBefore(dwEventWhat, 1, rgReasons);
// make sure action was not cancelled
if (hrFetch != S_OK)
{
VDSetErrorInfo(IDS_ERR_ACTIONCANCELLED, IID_ICursor, m_pResourceDLL);
return E_FAIL;
}
// make sure that an update is not already in progress
if (m_pCursorPosition->GetEditMode() != CURSOR_DBEDITMODE_NONE)
{
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons);
VDSetErrorInfo(IDS_ERR_UPDATEINPROGRESS, IID_ICursor, m_pResourceDLL);
return CURSOR_DB_E_UPDATEINPROGRESS;
}
ULONG cRowsObtained = 0;
HROW * rghRows = NULL;
BYTE bSpecialBM;
ULONG cbBookmark;
BYTE * pBookmark;
switch (m_pCursorPosition->m_bmCurrent.GetStatus())
{
case VDBOOKMARKSTATUS_BEGINNING:
cbBookmark = sizeof(BYTE);
bSpecialBM = DBBMK_FIRST;
pBookmark = &bSpecialBM;
break;
case VDBOOKMARKSTATUS_END:
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons);
return CURSOR_DB_S_ENDOFCURSOR;
case VDBOOKMARKSTATUS_CURRENT:
cbBookmark = m_pCursorPosition->m_bmCurrent.GetBookmarkLen();
pBookmark = m_pCursorPosition->m_bmCurrent.GetBookmark();
udlRowsToSkip.LowPart++;
break;
default:
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons);
ASSERT_(FALSE);
VDSetErrorInfo(IDS_ERR_INVALIDBMSTATUS, IID_ICursor, m_pResourceDLL);
return E_FAIL;
}
hrFetch = GetRowsetLocate()->GetRowsAt(0, 0, cbBookmark, pBookmark,
udlRowsToSkip.LowPart,
pFetchParams->cRowsRequested,
&cRowsObtained, &rghRows);
hrFetch = FilterNewRow(&cRowsObtained, rghRows, hrFetch);
if (S_OK != hrFetch)
hrFetch = VDMapRowsetHRtoCursorHR(hrFetch, IDS_ERR_GETROWSATFAILED, IID_ICursor, GetRowsetLocate(), IID_IRowsetLocate, m_pResourceDLL);
if FAILED(hrFetch)
{
if (cRowsObtained)
{
// release hRows and associated memory
pRowset->ReleaseRows(cRowsObtained, rghRows, NULL, NULL, NULL);
g_pMalloc->Free(rghRows);
}
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons);
return hrFetch;
}
if (cRowsObtained)
{
HRESULT hrMove = S_OK;
// if got all rows requested then set current position to last row retrieved
if (SUCCEEDED(hrFetch) &&
cRowsObtained == pFetchParams->cRowsRequested)
hrMove = m_pCursorPosition->SetRowPosition(rghRows[cRowsObtained - 1]);
// only do this if succeeded
if (SUCCEEDED(hrMove))
{
// fill consumers buffer
hrFetch = FillConsumersBuffer(hrFetch, pFetchParams, cRowsObtained, rghRows);
// if got all rows requested then set current position to last row retrieved (internally)
if (SUCCEEDED(hrFetch) &&
cRowsObtained == pFetchParams->cRowsRequested)
m_pCursorPosition->SetCurrentHRow(rghRows[cRowsObtained - 1]);
}
// release hRows and associated memory
pRowset->ReleaseRows(cRowsObtained, rghRows, NULL, NULL, NULL);
g_pMalloc->Free(rghRows);
// report failure
if (FAILED(hrMove))
{
cRowsObtained = 0;
hrFetch = E_FAIL;
}
}
if (SUCCEEDED(hrFetch) &&
cRowsObtained < pFetchParams->cRowsRequested)
m_pCursorPosition->SetCurrentRowStatus(VDBOOKMARKSTATUS_END);
if SUCCEEDED(hrFetch)
{
rgReasons[0].arg1 = m_pCursorPosition->m_bmCurrent.GetBookmarkVariant();
m_pCursorPosition->NotifyAfter(dwEventWhat, 1, rgReasons);
}
else
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons);
return hrFetch;
}
//=--------------------------------------------------------------------------=
// UseAdjustments - Use adjustments to fix-up returned data
//=--------------------------------------------------------------------------=
// This uses adjustments to fix-up returned data, see MakeAdjustments function
//
// Parameters:
// hRow - [in] row handle
// pData - [in] a pointer to data
//
// Output:
// S_OK - if successful
// E_INVALIDARG - bad parameter
// E_OUTOFMEMORY - not enough memory
//
// Notes:
//
HRESULT CVDCursor::UseAdjustments(HROW hRow, BYTE * pData)
{
ASSERT_POINTER(m_rghAdjustAccessors, HACCESSOR)
ASSERT_POINTER(m_pdwAdjustFlags, DWORD)
ASSERT_POINTER(pData, BYTE)
IRowset * pRowset = GetRowset();
// make sure we have a valid rowset pointer
if (!pRowset || !IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursor, m_pResourceDLL);
return E_FAIL;
}
// make sure we have all neccessary pointers
if (!m_rghAdjustAccessors || !m_pdwAdjustFlags || !pData)
return E_INVALIDARG;
// iterate through cursor bindings, checking for adjustments
for (ULONG ulBind = 0; ulBind < m_ulCursorBindings; ulBind++)
{
// check for variant binding byte field -> byte binding
if (m_pdwAdjustFlags[ulBind] == VD_ADJUST_VARIANT_TO_BYTE)
{
// get variant pointer
VARIANT * pVariant = (VARIANT*)(pData + m_pCursorBindings[ulBind].obData);
// extract byte
BYTE value = *(BYTE*)pVariant;
// init byte variant
VariantInit(pVariant);
// fix-up returned data
pVariant->vt = VT_UI1;
pVariant->bVal = value;
}
// check for variant binding date field -> wide string binding
if (m_pdwAdjustFlags[ulBind] == VD_ADJUST_VARIANT_TO_WSTR)
{
// get variant pointer
VARIANT * pVariant = (VARIANT*)(pData + m_pCursorBindings[ulBind].obData);
// extract length of string
ULONG ulLength = *(ULONG*)pVariant;
// place length field in proper place, if originally requested
if (m_pCursorBindings[ulBind].obVarDataLen != CURSOR_DB_NOVALUE)
*(ULONG*)(pData + m_pCursorBindings[ulBind].obVarDataLen) = ulLength;
// init string variant
VariantInit(pVariant);
// create storage for string
BSTR bstr = SysAllocStringByteLen(NULL, ulLength);
if (!bstr)
{
VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL);
return E_OUTOFMEMORY;
}
// clear wide string
memset(bstr, 0, ulLength);
HRESULT hr = S_OK;
// get memo string value
if (ulLength)
{
hr = pRowset->GetData(hRow, m_rghAdjustAccessors[ulBind], bstr);
// ignore these return values
if (hr == DB_S_ERRORSOCCURRED || hr == DB_E_ERRORSOCCURRED)
hr = S_OK;
}
if (SUCCEEDED(hr))
{
// fix-up returned data
pVariant->vt = VT_BSTR;
pVariant->bstrVal = bstr;
}
else
SysFreeString(bstr);
}
// check for variant binding memo field -> string binding
if (m_pdwAdjustFlags[ulBind] == VD_ADJUST_VARIANT_TO_STR)
{
// get variant pointer
VARIANT * pVariant = (VARIANT*)(pData + m_pCursorBindings[ulBind].obData);
// extract length of string
ULONG ulLength = *(ULONG*)pVariant;
// place length field in proper place, if originally requested
if (m_pCursorBindings[ulBind].obVarDataLen != CURSOR_DB_NOVALUE)
*(ULONG*)(pData + m_pCursorBindings[ulBind].obVarDataLen) = ulLength;
// init string variant
VariantInit(pVariant);
// create temporary string buffer
CHAR * pszBuffer = new CHAR[ulLength + 1];
if (!pszBuffer)
{
VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL);
return E_OUTOFMEMORY;
}
// clear string buffer
memset(pszBuffer, 0, ulLength + 1);
HRESULT hr = S_OK;
// get memo string value
if (ulLength)
{
hr = pRowset->GetData(hRow, m_rghAdjustAccessors[ulBind], pszBuffer);
// ignore these return values
if (hr == DB_S_ERRORSOCCURRED || hr == DB_E_ERRORSOCCURRED)
hr = S_OK;
}
if (SUCCEEDED(hr))
{
// fix-up returned data
pVariant->vt = VT_BSTR;
pVariant->bstrVal = BSTRFROMANSI(pszBuffer);
}
delete [] pszBuffer;
}
}
return S_OK;
}
//=--------------------------------------------------------------------------=
// FillConsumersBuffer
//=--------------------------------------------------------------------------=
// Fills the ICursor consumer's buffer with data from the obtained rows.
// Called from our implementations of GetNextRows, Move, Find, Scroll etc.
//
// Notes:
// End of string characters are inserted into variable length buffer to resolve an
// apparent difference between ICursor and IRowset. ICursor places an empty string
// in variable length buffer for NULL data, but this does not seem to be the behavior
// with IRowset, because it does not touch the variable length buffer in this case.
//
// Likewise, all variants are initialized before they are fetched to resolve another
// apparent difference between ICursor and IRowset. ICursor returns a NULL variant
// in cases where the underlying data is NULL, however IRowset leaves the variant
// untouched similar to the above.
//
HRESULT CVDCursor::FillConsumersBuffer(HRESULT hrFetch,
CURSOR_DBFETCHROWS *pFetchParams,
ULONG cRowsObtained,
HROW * rghRows)
{
HRESULT hr;
ULONG ulRow;
ULONG ulBind;
BYTE * pVarLength = NULL;
BYTE * pVarHelperData = NULL;
CURSOR_DBCOLUMNBINDING * pCursorBinding;
BOOL fEntryIDBinding;
IRowset * pRowset = GetRowset();
// if caller requested callee allocated memory, then compute sizes and allocate memory
if (pFetchParams->dwFlags & CURSOR_DBROWFETCH_CALLEEALLOCATES)
{
// allocate inline memory
pFetchParams->pData = g_pMalloc->Alloc(cRowsObtained * m_cbRowLength);
if (!pFetchParams->pData)
{
VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL);
return E_OUTOFMEMORY;
}
// if needed, allocate out-of-line memory
if (m_ulVarBindings)
{
// create variable length data table
ULONG cbVarHelperData = cRowsObtained * m_ulVarBindings * (sizeof(ULONG) + sizeof(DBSTATUS));
pVarHelperData = new BYTE[cbVarHelperData];
if (!pVarHelperData)
{
g_pMalloc->Free(pFetchParams->pData);
pFetchParams->pData = NULL;
VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL);
return E_OUTOFMEMORY;
}
// clear table
memset(pVarHelperData, 0, cbVarHelperData);
ULONG cbVarData = 0;
pVarLength = pVarHelperData;
// determine necessary size of variable length buffer
for (ulRow = 0; ulRow < cRowsObtained; ulRow++)
{
hr = S_OK;
// if necessary, get variable length and status information
if (m_hVarHelper)
{
hr = pRowset->GetData(rghRows[ulRow], m_hVarHelper, pVarLength);
// ignore these errors
if (hr == DB_S_ERRORSOCCURRED || hr == DB_E_ERRORSOCCURRED)
hr = S_OK;
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETDATAFAILED, IID_ICursor, pRowset, IID_IRowset,
m_pResourceDLL);
}
if (FAILED(hr))
{
g_pMalloc->Free(pFetchParams->pData);
pFetchParams->pData = NULL;
delete [] pVarHelperData;
return hr;
}
pCursorBinding = m_pCursorBindings;
// calculate sizes of data returned in out-of-line memory
for (ulBind = 0; ulBind < m_ulCursorBindings; ulBind++)
{
// set entryID binding flag
fEntryIDBinding = (pCursorBinding->dwBinding & CURSOR_DBBINDING_ENTRYID);
if (m_rghVarAccessors[ulBind] || fEntryIDBinding && pCursorBinding->dwDataType == CURSOR_DBTYPE_BLOB)
{
// insert length entries for fixed datatypes
if (m_ppColumns[ulBind]->GetFixed())
{
*(ULONG*)pVarLength = m_ppColumns[ulBind]->GetMaxStrLen();
*(DBSTATUS*)(pVarLength + sizeof(ULONG)) = DBSTATUS_S_OK;
}
// insert length entries for entryID bindings
if (fEntryIDBinding)
{
*(ULONG*)pVarLength = sizeof(ULONG) + sizeof(ULONG) + GetCursorMain()->GetMaxBookmarkLen();
*(DBSTATUS*)(pVarLength + sizeof(ULONG)) = DBSTATUS_S_OK;
}
// allow for null-terminator
if (pCursorBinding->dwDataType == CURSOR_DBTYPE_LPSTR ||
pCursorBinding->dwDataType == CURSOR_DBTYPE_LPWSTR)
*((ULONG*)pVarLength) += 1;
// allow for wide characters
if (pCursorBinding->dwDataType == CURSOR_DBTYPE_LPWSTR)
*((ULONG*)pVarLength) *= sizeof(WCHAR);
cbVarData += *(ULONG*)pVarLength;
pVarLength += sizeof(ULONG) + sizeof(DBSTATUS);
}
pCursorBinding++;
}
}
// now, allocate out-of-line memory
pFetchParams->pVarData = g_pMalloc->Alloc(cbVarData);
if (!pFetchParams->pData)
{
g_pMalloc->Free(pFetchParams->pData);
pFetchParams->pData = NULL;
delete [] pVarHelperData;
VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL);
return E_OUTOFMEMORY;
}
}
else
pFetchParams->pVarData = NULL;
}
// fetch data
CURSOR_BLOB cursorBlob;
BYTE * pData = (BYTE*)pFetchParams->pData;
BYTE * pVarData = (BYTE*)pFetchParams->pVarData;
pVarLength = pVarHelperData;
// iterate through the returned hRows
for (ulRow = 0; ulRow < cRowsObtained; ulRow++)
{
hr = S_OK;
pCursorBinding = m_pCursorBindings;
// iterate through bindings and initialize variants
for (ulBind = 0; ulBind < m_ulCursorBindings; ulBind++)
{
if (pCursorBinding->dwBinding & CURSOR_DBBINDING_VARIANT)
{
if (pCursorBinding->obData != CURSOR_DB_NOVALUE)
VariantInit((VARIANT*)(pData + pCursorBinding->obData));
}
pCursorBinding++;
}
// if necessary get fixed length data
if (m_hAccessor)
{
hr = pRowset->GetData(rghRows[ulRow], m_hAccessor, pData);
// ignore these return values
if (hr == DB_S_ERRORSOCCURRED || hr == DB_E_ERRORSOCCURRED)
hr = S_OK;
// check to see if need to use adjustments
if (m_rghAdjustAccessors && SUCCEEDED(hr))
hr = UseAdjustments(rghRows[ulRow], pData);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETDATAFAILED, IID_ICursor, pRowset, IID_IRowset, m_pResourceDLL);
}
if (FAILED(hr))
{
hrFetch = hr;
pFetchParams->cRowsReturned = 0;
goto DoneFetchingData;
}
pCursorBinding = m_pCursorBindings;
// if necessary get fixed length entryIDs
for (ulBind = 0; ulBind < m_ulCursorBindings; ulBind++)
{
// set entryID binding flag
fEntryIDBinding = (pCursorBinding->dwBinding & CURSOR_DBBINDING_ENTRYID);
if (fEntryIDBinding && pCursorBinding->dwDataType == CURSOR_DBTYPE_BYTES)
{
// return entryID length
*(ULONG*)(pData + pCursorBinding->obData) =
sizeof(ULONG) + sizeof(ULONG) + GetCursorMain()->GetMaxBookmarkLen();
// return column ordinal
*(ULONG*)(pData + pCursorBinding->obData + sizeof(ULONG)) = m_ppColumns[ulBind]->GetOrdinal();
// return row bookmark
hr = pRowset->GetData(rghRows[ulRow], GetCursorMain()->GetBookmarkAccessor(),
pData + pCursorBinding->obData + sizeof(ULONG) + sizeof(ULONG));
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETDATAFAILED, IID_ICursor, pRowset, IID_IRowset,
m_pResourceDLL);
if (FAILED(hr))
{
hrFetch = hr;
pFetchParams->cRowsReturned = 0;
goto DoneFetchingData;
}
}
pCursorBinding++;
}
pCursorBinding = m_pCursorBindings;
// if necessary get variable length data
if (m_rghVarAccessors)
{
for (ulBind = 0; ulBind < m_ulCursorBindings; ulBind++)
{
// set entryID binding flag
fEntryIDBinding = (pCursorBinding->dwBinding & CURSOR_DBBINDING_ENTRYID);
if (m_rghVarAccessors[ulBind] || fEntryIDBinding && pCursorBinding->dwDataType == CURSOR_DBTYPE_BLOB)
{
// place end of string characters in variable length buffer
if (pCursorBinding->dwDataType == CURSOR_DBTYPE_LPSTR)
{
pVarData[0] = 0;
}
else if (pCursorBinding->dwDataType == CURSOR_DBTYPE_LPWSTR)
{
pVarData[0] = 0;
pVarData[1] = 0;
}
// get data if we have accessor
if (m_rghVarAccessors[ulBind])
{
// get variable length data
hr = pRowset->GetData(rghRows[ulRow], m_rghVarAccessors[ulBind], pVarData);
// ignore these return values
if (hr == DB_S_ERRORSOCCURRED || hr == DB_E_ERRORSOCCURRED)
hr = S_OK;
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETDATAFAILED, IID_ICursor, pRowset, IID_IRowset,
m_pResourceDLL);
}
else // otherwise, get variable length entryIDs
{
// return entryID length
*(ULONG*)(pData + pCursorBinding->obData) =
sizeof(ULONG) + sizeof(ULONG) + GetCursorMain()->GetMaxBookmarkLen();
// return column ordinal
*(ULONG*)(pVarData) = m_ppColumns[ulBind]->GetOrdinal();
// return row bookmark
hr = pRowset->GetData(rghRows[ulRow], GetCursorMain()->GetBookmarkAccessor(),
pVarData + sizeof(ULONG));
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETDATAFAILED, IID_ICursor, pRowset, IID_IRowset,
m_pResourceDLL);
}
if (FAILED(hr))
{
hrFetch = hr;
pFetchParams->cRowsReturned = 0;
goto DoneFetchingData;
}
// make adjustments in fixed length buffer for default bindings
if (!(pCursorBinding->dwBinding & CURSOR_DBBINDING_VARIANT))
{
switch (pCursorBinding->dwDataType)
{
case CURSOR_DBTYPE_BLOB:
*(LPBYTE*)(pData + pCursorBinding->obData + sizeof(ULONG)) = (LPBYTE)pVarData;
break;
case CURSOR_DBTYPE_LPSTR:
*(LPSTR*)(pData + pCursorBinding->obData) = (LPSTR)pVarData;
break;
case CURSOR_DBTYPE_LPWSTR:
*(LPWSTR*)(pData + pCursorBinding->obData) = (LPWSTR)pVarData;
break;
}
}
else // make adjustments in fixed length buffer for variant bindings
{
CURSOR_DBVARIANT * pVariant = (CURSOR_DBVARIANT*)(pData + pCursorBinding->obData);
switch (pCursorBinding->dwDataType)
{
case CURSOR_DBTYPE_BLOB:
cursorBlob.cbSize = *(ULONG*)pVariant;
cursorBlob.pBlobData = (LPBYTE)pVarData;
VariantInit((VARIANT*)pVariant);
pVariant->vt = CURSOR_DBTYPE_BLOB;
pVariant->blob = cursorBlob;
break;
case CURSOR_DBTYPE_LPSTR:
VariantInit((VARIANT*)pVariant);
pVariant->vt = CURSOR_DBTYPE_LPSTR;
pVariant->pszVal = (LPSTR)pVarData;
break;
case CURSOR_DBTYPE_LPWSTR:
VariantInit((VARIANT*)pVariant);
pVariant->vt = CURSOR_DBTYPE_LPSTR;
pVariant->pwszVal = (LPWSTR)pVarData;
break;
}
}
if (pVarLength)
{
pVarData += *(ULONG*)pVarLength;
pVarLength += sizeof(ULONG) + sizeof(DBSTATUS);
}
else
{
if (pCursorBinding->dwDataType == CURSOR_DBTYPE_BLOB)
pVarData += *(ULONG *) (pData + pCursorBinding->obData);
else
pVarData += pCursorBinding->cbMaxLen;
}
}
else
{
if (pCursorBinding->dwDataType == CURSOR_DBTYPE_FILETIME)
{
VDConvertToFileTime((DBTIMESTAMP*)(pData + pCursorBinding->obData),
(FILETIME*)(pData + pCursorBinding->obData));
}
}
pCursorBinding++;
}
}
pCursorBinding = m_pCursorBindings;
// make adjustments for status fields
for (ulBind = 0; ulBind < m_ulCursorBindings; ulBind++)
{
if (pCursorBinding->obInfo != CURSOR_DB_NOVALUE)
{
*(DWORD*)(pData + pCursorBinding->obInfo) =
StatusToCursorInfo(*(DBSTATUS*)(pData + pCursorBinding->obInfo));
}
pCursorBinding++;
}
// increment returned row count
pFetchParams->cRowsReturned++;
pData += m_cbRowLength;
}
DoneFetchingData:
delete [] pVarHelperData;
// cleanup memory allocations if we did not retrieve any rows
if (pFetchParams->dwFlags & CURSOR_DBROWFETCH_CALLEEALLOCATES && !pFetchParams->cRowsReturned)
{
if (pFetchParams->pData)
{
g_pMalloc->Free(pFetchParams->pData);
pFetchParams->pData = NULL;
}
if (pFetchParams->pVarData)
{
g_pMalloc->Free(pFetchParams->pVarData);
pFetchParams->pVarData = NULL;
}
}
return hrFetch;
}
//=--------------------------------------------------------------------------=
// ICursor Requery
//=--------------------------------------------------------------------------=
// Repopulates the cursor based on its original definition
//
// Parameters:
// none
//
// Output:
// HRESULT - S_OK if successful
//
// Notes:
//
HRESULT CVDCursor::Requery(void)
{
if (!IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursor, m_pResourceDLL);
return E_FAIL;
}
IRowset * pRowset = GetRowset();
IRowsetResynch * pRowsetResynch = NULL;
HRESULT hr = pRowset->QueryInterface(IID_IRowsetResynch, (void**)&pRowsetResynch);
if (SUCCEEDED(hr))
{
hr = pRowsetResynch->ResynchRows(0, NULL, NULL, NULL, NULL);
pRowsetResynch->Release();
if (FAILED(hr))
return VDMapRowsetHRtoCursorHR(hr, IDS_ERR_RESYNCHFAILED, IID_ICursor, pRowset, IID_IRowsetResynch, m_pResourceDLL);
}
hr = pRowset->RestartPosition(0);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_RESTARTPOSFAILED, IID_ICursor, pRowset, IID_IRowset, m_pResourceDLL);
m_pCursorPosition->PositionToFirstRow();
return hr;
}
//=--------------------------------------------------------------------------=
// FetchAtBookmark
//=--------------------------------------------------------------------------=
// Called from ICursorMove::Move, ICursorScroll::Scroll and ICursorFind::Find
//
// Parameters:
// cbBookmark [in] The length of the bookmark
// pBookmark [in] A pointer to the bookmarks data
// dlOffset [in] Offset from the bookmark position
// pFetchParams [in] A pointer to the CURSOR_DBFETCHROWS structure (optional)
//
// Output:
// HRESULT - S_OK if successful
//
// Notes:
//
//
HRESULT CVDCursor::FetchAtBookmark(ULONG cbBookmark,
void *pBookmark,
LARGE_INTEGER dlOffset,
CURSOR_DBFETCHROWS *pFetchParams)
{
HRESULT hr = S_OK;
// vaildate fetch params (implemented on CVDCursorBase
if (pFetchParams)
hr = ValidateFetchParams(pFetchParams, IID_ICursorMove);
// return if fetch params are invalid
if (FAILED(hr))
return hr;
IRowset * pRowset = GetRowset();
// make sure that an update is not already in progress
if (m_pCursorPosition->GetEditMode() != CURSOR_DBEDITMODE_NONE)
{
VDSetErrorInfo(IDS_ERR_UPDATEINPROGRESS, IID_ICursor, m_pResourceDLL);
return CURSOR_DB_E_UPDATEINPROGRESS;
}
ULONG cRowsObtained = 0;
HROW * rghRows = NULL;
HRESULT hrFetch = S_OK;
BYTE bSpecialBM;
BOOL fFetchData = TRUE;
WORD wSpecialBMStatus = 0;
if (CURSOR_DB_BMK_SIZE == cbBookmark)
{
if (memcmp(&CURSOR_DBBMK_BEGINNING, pBookmark, CURSOR_DB_BMK_SIZE) == 0)
{
if ((long)dlOffset.LowPart < 0)
{
m_pCursorPosition->SetCurrentRowStatus(VDBOOKMARKSTATUS_BEGINNING);
return CURSOR_DB_S_ENDOFCURSOR;
}
bSpecialBM = DBBMK_FIRST;
pBookmark = &bSpecialBM;
// make sure we properly handle situation when caller move before the first,
// and does not fetch any rows
if ((!pFetchParams || !pFetchParams->cRowsRequested) && (long)dlOffset.LowPart < 1)
{
fFetchData = FALSE;
wSpecialBMStatus = VDBOOKMARKSTATUS_BEGINNING;
}
else
dlOffset.LowPart--;
}
else
if (memcmp(&CURSOR_DBBMK_END, pBookmark, CURSOR_DB_BMK_SIZE) == 0)
{
if ((long)dlOffset.LowPart > 0)
{
m_pCursorPosition->SetCurrentRowStatus(VDBOOKMARKSTATUS_END);
return CURSOR_DB_S_ENDOFCURSOR;
}
bSpecialBM = DBBMK_LAST;
pBookmark = &bSpecialBM;
// make sure we properly handle situation when caller move after the last
if ((!pFetchParams || !pFetchParams->cRowsRequested) && (long)dlOffset.LowPart > -1)
{
fFetchData = FALSE;
wSpecialBMStatus = VDBOOKMARKSTATUS_END;
}
else
dlOffset.LowPart++;
}
else
if (memcmp(&CURSOR_DBBMK_CURRENT, pBookmark, CURSOR_DB_BMK_SIZE) == 0)
{
switch (m_pCursorPosition->m_bmCurrent.GetStatus())
{
case VDBOOKMARKSTATUS_BEGINNING:
cbBookmark = sizeof(BYTE);
bSpecialBM = DBBMK_FIRST;
pBookmark = &bSpecialBM;
dlOffset.LowPart--;
break;
case VDBOOKMARKSTATUS_END:
cbBookmark = sizeof(BYTE);
bSpecialBM = DBBMK_LAST;
pBookmark = &bSpecialBM;
dlOffset.LowPart++;
break;
case VDBOOKMARKSTATUS_CURRENT:
cbBookmark = m_pCursorPosition->m_bmCurrent.GetBookmarkLen();
pBookmark = m_pCursorPosition->m_bmCurrent.GetBookmark();
break;
default:
ASSERT_(FALSE);
VDSetErrorInfo(IDS_ERR_INVALIDBMSTATUS, IID_ICursor, m_pResourceDLL);
return E_FAIL;
}
}
}
ULONG cRowsToFetch = 1;
// if caller requested rows, fetch that count
if (pFetchParams && pFetchParams->cRowsRequested > 0)
cRowsToFetch = pFetchParams->cRowsRequested;
if (fFetchData)
{
// fetch hRows
hrFetch = GetRowsetLocate()->GetRowsAt(0, 0, cbBookmark, (const BYTE *)pBookmark,
dlOffset.LowPart,
cRowsToFetch,
&cRowsObtained, &rghRows);
if (hrFetch == E_UNEXPECTED)
{
// set rowset released flag, since original rowset is zombie'd
m_pCursorPosition->GetRowsetSource()->SetRowsetReleasedFlag();
}
hrFetch = FilterNewRow(&cRowsObtained, rghRows, hrFetch);
// check for before the first or after the last
if (hrFetch == DB_E_BADSTARTPOSITION)
{
if ((long)dlOffset.LowPart < 0)
wSpecialBMStatus = VDBOOKMARKSTATUS_BEGINNING;
else
wSpecialBMStatus = VDBOOKMARKSTATUS_END;
hrFetch = DB_S_ENDOFROWSET;
fFetchData = FALSE;
}
}
hrFetch = VDMapRowsetHRtoCursorHR(hrFetch, IDS_ERR_GETROWSATFAILED, IID_ICursorMove, GetRowsetLocate(),
IID_IRowsetLocate, m_pResourceDLL);
if (FAILED(hrFetch))
{
if (cRowsObtained)
{
// release hRows and associated memory
pRowset->ReleaseRows(cRowsObtained, rghRows, NULL, NULL, NULL);
g_pMalloc->Free(rghRows);
}
return hrFetch;
}
if (cRowsObtained)
{
HRESULT hrMove = S_OK;
// if got all rows requested then set current position to last row retrieved
if (SUCCEEDED(hrFetch) &&
cRowsObtained == cRowsToFetch)
hrMove = m_pCursorPosition->SetRowPosition(rghRows[cRowsObtained - 1]);
// only do this if succeeded
if (SUCCEEDED(hrMove))
{
// fill consumers buffer
if (pFetchParams && pFetchParams->cRowsRequested > 0)
hrFetch = FillConsumersBuffer(hrFetch, pFetchParams, cRowsObtained, rghRows);
// if got all rows requested then set current position to last row retrieved (internally)
if (SUCCEEDED(hrFetch) &&
cRowsObtained == cRowsToFetch)
m_pCursorPosition->SetCurrentHRow(rghRows[cRowsObtained - 1]);
}
// release hRows and associated memory
pRowset->ReleaseRows(cRowsObtained, rghRows, NULL, NULL, NULL);
g_pMalloc->Free(rghRows);
// report failure
if (FAILED(hrMove))
{
cRowsObtained = 0;
hrFetch = E_FAIL;
}
}
else if (wSpecialBMStatus)
{
m_pCursorPosition->SetCurrentRowStatus(wSpecialBMStatus);
hrFetch = CURSOR_DB_S_ENDOFCURSOR;
}
if (SUCCEEDED(hrFetch) &&
cRowsObtained < cRowsToFetch &&
!wSpecialBMStatus)
m_pCursorPosition->SetCurrentRowStatus(VDBOOKMARKSTATUS_END);
return hrFetch;
}
//=--------------------------------------------------------------------------=
// ICursorMove methods implemented
//=--------------------------------------------------------------------------=
//=--------------------------------------------------------------------------=
// ICursorMove Move
//=--------------------------------------------------------------------------=
// Moves the current row to a new row within the cursor and optionally fetches
// rows from that new position
//
// Parameters:
// cbBookmark - [in] length in bytes of the bookmark
// pBookmark - [in] a pointer to a bookmark which serves as the
// origin for the calculation that determines the
// target row
// dlOffset - [in] a signed count of the rows from the origin
// bookmark to the target row
// pFetchParams - [in, out] a pointer to fetch rows structure
//
// Output:
// HRESULT - S_OK if successful
// E_INVALIDARG bad parameter
//
// Notes:
//
HRESULT CVDCursor::Move(ULONG cbBookmark, void *pBookmark, LARGE_INTEGER dlOffset, CURSOR_DBFETCHROWS *pFetchParams)
{
ASSERT_POINTER(pBookmark, BYTE)
ASSERT_NULL_OR_POINTER(pFetchParams, CURSOR_DBFETCHROWS)
if (!IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorMove, m_pResourceDLL);
return E_FAIL;
}
if (!cbBookmark || !pBookmark)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorMove, m_pResourceDLL);
return E_INVALIDARG;
}
HRESULT hr = S_OK;
BOOL fNotifyOthers = TRUE;
// get current bookmark
ULONG cbCurrent = m_pCursorPosition->m_bmCurrent.GetBookmarkLen();
BYTE * pCurrent = m_pCursorPosition->m_bmCurrent.GetBookmark();
// check to see if caller is moving to the current row using the standard bookmark
if (CURSOR_DB_BMK_SIZE == cbBookmark && memcmp(&CURSOR_DBBMK_CURRENT, pBookmark, CURSOR_DB_BMK_SIZE) == 0 &&
dlOffset.HighPart == 0 && dlOffset.LowPart == 0)
{
// if caller is not fetching any rows, then get out
if (!pFetchParams || pFetchParams->cRowsRequested == 0)
return S_OK;
// if caller is only fetching one row, then don't generate notifications
if (pFetchParams && pFetchParams->cRowsRequested == 1)
fNotifyOthers = FALSE;
}
CURSOR_DBNOTIFYREASON rgReasons[1];
rgReasons[0].dwReason = CURSOR_DBREASON_MOVE;
rgReasons[0].arg1 = m_pCursorPosition->m_bmCurrent.GetBookmarkVariant();
VariantInit((VARIANT*)&rgReasons[0].arg2);
// notify other interested parties
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_CHANGED;
if (fNotifyOthers)
hr = m_pCursorPosition->NotifyBefore(dwEventWhat, 1, rgReasons);
// make sure action was not cancelled
if (hr != S_OK)
{
VDSetErrorInfo(IDS_ERR_ACTIONCANCELLED, IID_ICursorMove, m_pResourceDLL);
return E_FAIL;
}
hr = FetchAtBookmark(cbBookmark, pBookmark, dlOffset, pFetchParams);
if (SUCCEEDED(hr))
{
rgReasons[0].arg1 = m_pCursorPosition->m_bmCurrent.GetBookmarkVariant();
if (fNotifyOthers)
m_pCursorPosition->NotifyAfter(dwEventWhat, 1, rgReasons);
}
else
{
if (fNotifyOthers)
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons);
}
return hr;
}
//=--------------------------------------------------------------------------=
// ICursorMove GetBookmark
//=--------------------------------------------------------------------------=
// Returns the bookmark of the current row
//
// Parameters:
// pBookmarkType - [in] a pointer to the type of bookmark desired
// cbMaxSize - [in] length in bytes of the client buffer to put the
// returned bookmark into
// pcbBookmark - [out] a pointer to memory in which to return the actual
// length of the returned bookmark
// pBookmark - [out] a pointer to client buffer to put the returned
// bookmark into
//
// Output:
// HRESULT - S_OK if successful
// E_INVALIDARG bad parameter
//
// Notes:
//
HRESULT CVDCursor::GetBookmark(CURSOR_DBCOLUMNID *pBookmarkType,
ULONG cbMaxSize,
ULONG *pcbBookmark,
void *pBookmark)
{
ASSERT_POINTER(pBookmarkType, CURSOR_DBCOLUMNID);
ASSERT_POINTER(pcbBookmark, ULONG);
ASSERT_POINTER(pBookmark, BYTE);
ASSERT_(cbMaxSize > 0)
if (!IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorMove, m_pResourceDLL);
return E_FAIL;
}
if (!pcbBookmark || !pBookmark)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorMove, m_pResourceDLL);
return E_INVALIDARG;
}
// verify bookmark type
if (memcmp(&CURSOR_COLUMN_BMKTEMPORARY, pBookmarkType, sizeof(CURSOR_DBCOLUMNID)) != 0 &&
memcmp(&CURSOR_COLUMN_BMKTEMPORARYREL, pBookmarkType, sizeof(CURSOR_DBCOLUMNID)) != 0)
{
VDSetErrorInfo(IDS_ERR_BADCOLUMNID, IID_ICursorMove, m_pResourceDLL);
return DB_E_BADCOLUMNID;
}
HRESULT hr = S_OK;
if (0 == cbMaxSize)
{
VDSetErrorInfo(IDS_ERR_BUFFERTOOSMALL, IID_ICursorMove, m_pResourceDLL);
hr = CURSOR_DB_E_BUFFERTOOSMALL;
}
else
{
switch (m_pCursorPosition->m_bmCurrent.GetStatus())
{
case VDBOOKMARKSTATUS_BEGINNING:
case VDBOOKMARKSTATUS_END:
case VDBOOKMARKSTATUS_CURRENT:
if (m_pCursorPosition->m_bmCurrent.GetBookmarkLen() > cbMaxSize)
{
VDSetErrorInfo(IDS_ERR_BUFFERTOOSMALL, IID_ICursorMove, m_pResourceDLL);
hr = CURSOR_DB_E_BUFFERTOOSMALL;
break;
}
*pcbBookmark = m_pCursorPosition->m_bmCurrent.GetBookmarkLen();
memcpy(pBookmark, m_pCursorPosition->m_bmCurrent.GetBookmark(), *pcbBookmark);
break;
case VDBOOKMARKSTATUS_INVALID:
*pcbBookmark = CURSOR_DB_BMK_SIZE;
*(BYTE*)pBookmark = CURSOR_DBBMK_INVALID;
break;
default:
ASSERT_(FALSE);
VDSetErrorInfo(IDS_ERR_INVALIDBMSTATUS, IID_ICursorMove, m_pResourceDLL);
hr = E_FAIL;
break;
}
}
return hr;
}
//=--------------------------------------------------------------------------=
// ICursorMove Clone
//=--------------------------------------------------------------------------=
// Returns a clone of the cursor
//
// Parameters:
// dwFlags - [in] a flag that specifies the clone options
// riid - [in] the interface desired for the returned clone
// ppvClonedCursor - [out] a pointer to memory in which to return newly
// created clone pointer
//
// Output:
// HRESULT - S_OK if successful
//
// Notes:
//
HRESULT CVDCursor::Clone(DWORD dwFlags, REFIID riid, IUnknown **ppvClonedCursor)
{
if (!IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorMove, m_pResourceDLL);
return E_FAIL;
}
CVDCursorPosition * pCursorPosition;
HRESULT hr;
if (CURSOR_DBCLONEOPTS_SAMEROW == dwFlags)
{
pCursorPosition = m_pCursorPosition;
}
else
{
// create new cursor position object
hr = CVDCursorPosition::Create(NULL,
m_pCursorPosition->GetCursorMain(),
&pCursorPosition,
m_pResourceDLL);
if (FAILED(hr))
return hr;
}
CVDCursor * pCursor = 0;
hr = CVDCursor::Create(pCursorPosition, &pCursor, m_pResourceDLL);
if (CURSOR_DBCLONEOPTS_SAMEROW != dwFlags)
{
// release our reference
pCursorPosition->Release();
}
*ppvClonedCursor = (ICursorScroll*)pCursor;
return hr;
}
//=--------------------------------------------------------------------------=
// ICursorScroll methods implemented
//=--------------------------------------------------------------------------=
//=--------------------------------------------------------------------------=
// ICursorScroll Scroll
//=--------------------------------------------------------------------------=
// Moves the current row to a new row within the cursor, specified as a
// fraction, and optionally fetches rows from that new position
//
// Parameters:
// ulNumerator - [in] the numerator of the fraction that states the
// position to scroll to in the cursor
// ulDenominator - [in] the denominator of that same fraction
// pFetchParams - [in, out] a pointer to fetch rows structure
//
// Output:
// HRESULT - S_OK if successful
// CURSOR_DB_E_BADFRACTION - bad fraction
//
// Notes:
//
HRESULT CVDCursor::Scroll(ULONG ulNumerator, ULONG ulDenominator, CURSOR_DBFETCHROWS *pFetchParams)
{
if (!IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorScroll, m_pResourceDLL);
return E_FAIL;
}
IRowsetScroll * pRowsetScroll = GetRowsetScroll();
if (!pRowsetScroll)
return E_NOTIMPL;
CURSOR_DBNOTIFYREASON rgReasons[1];
rgReasons[0].dwReason = CURSOR_DBREASON_MOVEPERCENT;
VariantInit((VARIANT*)&rgReasons[0].arg1);
rgReasons[0].arg1.vt = VT_UI4;
rgReasons[0].arg1.lVal = ulNumerator;
VariantInit((VARIANT*)&rgReasons[0].arg2);
rgReasons[0].arg2.vt = VT_UI4;
rgReasons[0].arg2.lVal = ulDenominator;
// notify other interested parties
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_CHANGED;
HRESULT hr = m_pCursorPosition->NotifyBefore(dwEventWhat, 1, rgReasons);
// make sure action was not cancelled
if (hr != S_OK)
{
VDSetErrorInfo(IDS_ERR_ACTIONCANCELLED, IID_ICursorScroll, m_pResourceDLL);
return E_FAIL;
}
if (0 == ulNumerator) // go to first row
{
LARGE_INTEGER dlOffset;
dlOffset.HighPart = 0;
dlOffset.LowPart = 1;
hr = FetchAtBookmark(CURSOR_DB_BMK_SIZE, (void*)&CURSOR_DBBMK_BEGINNING, dlOffset, pFetchParams);
}
else
if (ulDenominator == ulNumerator) // go to last row
{
LARGE_INTEGER dlOffset;
dlOffset.HighPart = -1;
dlOffset.LowPart = 0xFFFFFFFF;
hr = FetchAtBookmark(CURSOR_DB_BMK_SIZE, (void*)&CURSOR_DBBMK_END, dlOffset, pFetchParams);
}
else
{
HROW * pRow = NULL;
ULONG cRowsObtained = 0;
hr = pRowsetScroll->GetRowsAtRatio(0, 0,
ulNumerator,
ulDenominator,
1,
&cRowsObtained,
&pRow);
if FAILED(hr)
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_SCROLLFAILED, IID_ICursorScroll, pRowsetScroll, IID_IRowsetScroll, m_pResourceDLL);
if (SUCCEEDED(hr) && cRowsObtained)
{
// allocate buffer for bookmark plus length indicator
BYTE * pBuff = new BYTE[GetCursorMain()->GetMaxBookmarkLen() + sizeof(ULONG)];
if (!pBuff)
hr = E_OUTOFMEMORY;
else
{
// get the bookmark data
hr = GetRowset()->GetData(*pRow, GetCursorMain()->GetBookmarkAccessor(), pBuff);
if SUCCEEDED(hr)
{
ULONG * pulLen = (ULONG*)pBuff;
BYTE * pbmdata = pBuff + sizeof(ULONG);
LARGE_INTEGER dlOffset;
dlOffset.HighPart = 0;
dlOffset.LowPart = 0;
hr = FetchAtBookmark(*pulLen, pbmdata, dlOffset, pFetchParams);
}
else
{
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETDATAFAILED, IID_ICursorScroll, pRowsetScroll, IID_IRowset, m_pResourceDLL);
}
delete [] pBuff;
}
}
if (pRow)
{
if (cRowsObtained)
GetRowset()->ReleaseRows(1, pRow, NULL, NULL, NULL);
g_pMalloc->Free(pRow);
}
}
if SUCCEEDED(hr)
{
rgReasons[0].arg1 = m_pCursorPosition->m_bmCurrent.GetBookmarkVariant();
VariantClear((VARIANT*)&rgReasons[0].arg2);
m_pCursorPosition->NotifyAfter(dwEventWhat, 1, rgReasons);
}
else
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons);
return hr;
}
//=--------------------------------------------------------------------------=
// ICursorScroll GetApproximatePosition
//=--------------------------------------------------------------------------=
// Returns the approximate location of a bookmark within the cursor, specified
// as a fraction
//
// Parameters:
// cbBookmark - [in] length in bytes of the bookmark
// pBookmark - [in] a pointer to the bookmark
// pulNumerator - [out] a pointer to memory in which to return the
// numerator of the faction that defines the
// approximate position of the bookmark
// pulDenominator - [out] a pointer to memory in which to return the
// denominator of that same faction
//
// Output:
// HRESULT - S_OK if successful
// E_INVALIDARG bad parameter
//
// Notes:
//
HRESULT CVDCursor::GetApproximatePosition(ULONG cbBookmark, void *pBookmark, ULONG *pulNumerator, ULONG *pulDenominator)
{
if (!IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorScroll, m_pResourceDLL);
return E_FAIL;
}
ASSERT_(cbBookmark);
ASSERT_POINTER(pBookmark, BYTE);
ASSERT_POINTER(pulNumerator, ULONG);
ASSERT_POINTER(pulDenominator, ULONG);
if (!cbBookmark || !pBookmark || !pulNumerator || !pulDenominator)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorScroll, m_pResourceDLL);
return E_INVALIDARG;
}
if (CURSOR_DB_BMK_SIZE == cbBookmark)
{
if (memcmp(&CURSOR_DBBMK_BEGINNING, pBookmark, CURSOR_DB_BMK_SIZE) == 0)
{
*pulNumerator = 0;
*pulDenominator = 1;
return S_OK;
}
if (memcmp(&CURSOR_DBBMK_END, pBookmark, CURSOR_DB_BMK_SIZE) == 0)
{
*pulNumerator = 1;
*pulDenominator = 1;
return S_OK;
}
if (memcmp(&CURSOR_DBBMK_CURRENT, pBookmark, CURSOR_DB_BMK_SIZE) == 0)
{
cbBookmark = m_pCursorPosition->m_bmCurrent.GetBookmarkLen();
pBookmark = m_pCursorPosition->m_bmCurrent.GetBookmark();
}
}
IRowsetScroll * pRowsetScroll = GetRowsetScroll();
if (!pRowsetScroll)
return E_NOTIMPL;
HRESULT hr = pRowsetScroll->GetApproximatePosition(0,
cbBookmark,
(const BYTE *)pBookmark,
pulNumerator,
pulDenominator);
if SUCCEEDED(hr)
{
// since ICursor returns a zero based approximate position and IRowset is 1 based
// we need to adjust the return value
if (0 < *pulNumerator)
(*pulNumerator)--;
}
else
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETAPPROXPOSFAILED, IID_ICursorScroll, pRowsetScroll, IID_IRowsetScroll, m_pResourceDLL);
return hr;
}
//=--------------------------------------------------------------------------=
// ICursorScroll GetApproximateCount
//=--------------------------------------------------------------------------=
// Returns the approximate number of rows in the cursor
//
// Parameters:
// pudlApproxCount - [out] a pointer to a buffer containing the
// returned approximate count of the rows
// in the cursor
// pdwFullyPopuldated - [out] a pointer to a buffer containing returned
// flags indicating whether the cursor is fully
// populated
//
// Output:
// HRESULT - S_OK if successful
// E_INVALIDARG bad parameter
//
// Notes:
//
HRESULT CVDCursor::GetApproximateCount(LARGE_INTEGER *pudlApproxCount, DWORD *pdwFullyPopulated)
{
if (!IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorScroll, m_pResourceDLL);
return E_FAIL;
}
ASSERT_POINTER(pudlApproxCount, LARGE_INTEGER);
ASSERT_NULL_OR_POINTER(pdwFullyPopulated, DWORD);
if (!pudlApproxCount)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorScroll, m_pResourceDLL);
return E_INVALIDARG;
}
IRowsetScroll * pRowsetScroll = GetRowsetScroll();
if (!pRowsetScroll)
return E_NOTIMPL;
HRESULT hr;
if (pdwFullyPopulated)
{
*pdwFullyPopulated = CURSOR_DBCURSORPOPULATED_FULLY;
IDBAsynchStatus * pDBAsynchStatus = NULL;
hr = pRowsetScroll->QueryInterface(IID_IDBAsynchStatus, (void**)&pDBAsynchStatus);
if (SUCCEEDED(hr) && pDBAsynchStatus)
{
ULONG ulProgress;
ULONG ulProgressMax;
ULONG ulStatusCode;
hr = pDBAsynchStatus->GetStatus(DB_NULL_HCHAPTER, DBASYNCHOP_OPEN, &ulProgress, &ulProgressMax, &ulStatusCode, NULL);
if (SUCCEEDED(hr))
{
if (ulProgress < ulProgressMax)
*pdwFullyPopulated = CURSOR_DBCURSORPOPULATED_PARTIALLY;
}
pDBAsynchStatus->Release();
}
}
pudlApproxCount->HighPart = 0;
hr = pRowsetScroll->GetApproximatePosition(0, 0, NULL, NULL, &pudlApproxCount->LowPart);
if FAILED(hr)
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETAPPROXPOSFAILED, IID_ICursorScroll, pRowsetScroll, IID_IRowsetScroll, m_pResourceDLL);
else
pudlApproxCount->LowPart -= m_pCursorPosition->GetCursorMain()->AddedRows();
return hr;
}
//=--------------------------------------------------------------------------=
// ICursorUpdateARow methods
//=--------------------------------------------------------------------------=
// ICursorUpdateARow BeginUpdate
//=--------------------------------------------------------------------------=
// Begins an operation that updates the current or adds a new row
//
// Parameters:
// dwFlags - [in] specifies the operation to begin
//
//
// Output:
// HRESULT - S_OK if successful
// E_FAIL a provider-specific error occured
// E_INVALIDARG bad parameter
// E_OUTOFMEMORY not enough memory
// CURSOR_DB_E_UPDATEINPROGRESS an update is already in progress
//
// Notes:
//
HRESULT CVDCursor::BeginUpdate(DWORD dwFlags)
{
IRowset * pRowset = GetRowset();
IAccessor * pAccessor = GetAccessor();
IRowsetChange * pRowsetChange = GetRowsetChange();
// make sure we have valid rowset, accessor and change pointers
if (!pRowset || !pAccessor || !pRowsetChange || !IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorUpdateARow, m_pResourceDLL);
return E_FAIL;
}
// check dwFlags for acceptable values
if (dwFlags != CURSOR_DBROWACTION_UPDATE && dwFlags != CURSOR_DBROWACTION_ADD)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorUpdateARow, m_pResourceDLL);
return E_INVALIDARG;
}
// make sure that an update is not already in progress
if (m_pCursorPosition->GetEditMode() != CURSOR_DBEDITMODE_NONE)
{
VDSetErrorInfo(IDS_ERR_UPDATEINPROGRESS, IID_ICursorUpdateARow, m_pResourceDLL);
return CURSOR_DB_E_UPDATEINPROGRESS;
}
// setup notification structures
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_DATA_CHANGED |
CURSOR_DBEVENT_SET_OF_ROWS_CHANGED;
CURSOR_DBNOTIFYREASON rgReasons[1];
VariantInit((VARIANT*)&rgReasons[0].arg1);
VariantInit((VARIANT*)&rgReasons[0].arg2);
switch (dwFlags)
{
case CURSOR_DBROWACTION_UPDATE:
rgReasons[0].dwReason = CURSOR_DBREASON_EDIT;
break;
case CURSOR_DBROWACTION_ADD:
rgReasons[0].dwReason = CURSOR_DBREASON_ADDNEW;
break;
}
// notify other interested parties of action
HRESULT hr = m_pCursorPosition->NotifyBefore(dwEventWhat, 1, rgReasons);
// make sure action was not cancelled
if (hr != S_OK)
{
VDSetErrorInfo(IDS_ERR_ACTIONCANCELLED, IID_ICursorUpdateARow, m_pResourceDLL);
return E_FAIL;
}
// insert new hRow if we're going into add mode
if (dwFlags == CURSOR_DBROWACTION_ADD)
{
hr = InsertNewRow();
if (FAILED(hr))
{
// notify other interested parties of failure
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons);
return hr;
}
}
// reset column updates
hr = m_pCursorPosition->ResetColumnUpdates();
if (FAILED(hr))
{
// notify other interested parties of failure
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons);
return hr;
}
// place cursor in correct mode
switch (dwFlags)
{
case CURSOR_DBROWACTION_UPDATE:
m_pCursorPosition->SetEditMode(CURSOR_DBEDITMODE_UPDATE);
break;
case CURSOR_DBROWACTION_ADD:
m_pCursorPosition->SetEditMode(CURSOR_DBEDITMODE_ADD);
break;
}
// notify other interested parties of success
m_pCursorPosition->NotifyAfter(dwEventWhat, 1, rgReasons);
return S_OK;
}
//=--------------------------------------------------------------------------=
// ICursorUpdateARow SetColumn
//=--------------------------------------------------------------------------=
// Sets the current value of the specified column
//
// Parameters:
// pcid - [in] a pointer to the columnID for which data is
// to be set
// pBindParams - [in] a pointer to a column binding structure containing
// information about the data and a pointer to the data
//
// Output:
// HRESULT - S_OK if successful
// E_INVALIDARG bad parameter
// E_OUTOFMEMORY not enough memory
// E_FAIL a provider-specific error occured
// CURSOR_DB_E_STATEERROR not in update or add mode
// CURSOR_DB_E_BADCOLUMNID pcid was not a valid column identifier
// CURSOR_DB_E_BADBINDINFO bad binding information
//
// Notes:
//
HRESULT CVDCursor::SetColumn(CURSOR_DBCOLUMNID *pcid, CURSOR_DBBINDPARAMS *pBindParams)
{
ASSERT_POINTER(pcid, CURSOR_DBCOLUMNID)
ASSERT_POINTER(pBindParams, CURSOR_DBBINDPARAMS)
// make sure we have valid rowset
if (!IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorUpdateARow, m_pResourceDLL);
return E_FAIL;
}
// make sure we have all necessary pointers
if (!pcid || !pBindParams || !pBindParams->pData)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorUpdateARow, m_pResourceDLL);
return E_INVALIDARG;
}
// make sure we are in update or add mode
if (m_pCursorPosition->GetEditMode() == CURSOR_DBEDITMODE_NONE)
{
VDSetErrorInfo(IDS_ERR_STATEERROR, IID_ICursorUpdateARow, m_pResourceDLL);
return CURSOR_DB_E_STATEERROR;
}
CVDRowsetColumn * pColumn;
// validate cursor binding parameters and get rowset column
HRESULT hr = ValidateCursorBindParams(pcid, pBindParams, &pColumn);
if (FAILED(hr))
return hr;
CVDColumnUpdate * pColumnUpdate;
// create new column update object
hr = CVDColumnUpdate::Create(pColumn, pBindParams, &pColumnUpdate, m_pResourceDLL);
if (FAILED(hr))
return hr;
// setup notification structures
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_DATA_CHANGED;
CURSOR_DBNOTIFYREASON rgReasons[1];
VariantInit((VARIANT*)&rgReasons[0].arg1);
VariantInit((VARIANT*)&rgReasons[0].arg2);
rgReasons[0].dwReason = CURSOR_DBREASON_SETCOLUMN;
rgReasons[0].arg1.vt = VT_I4;
rgReasons[0].arg1.lVal = pColumn->GetNumber();
rgReasons[0].arg2 = pColumnUpdate->GetVariant();
// notify other interested parties of action
hr = m_pCursorPosition->NotifyBefore(dwEventWhat, 1, rgReasons);
// make sure action was not cancelled
if (hr != S_OK)
{
// release column update object
pColumnUpdate->Release();
VDSetErrorInfo(IDS_ERR_ACTIONCANCELLED, IID_ICursorUpdateARow, m_pResourceDLL);
return E_FAIL;
}
// update column in cursor position
m_pCursorPosition->SetColumnUpdate(pColumn->GetNumber(), pColumnUpdate);
// notify other interested parties of success
m_pCursorPosition->NotifyAfter(dwEventWhat, 1, rgReasons);
return S_OK;
}
//=--------------------------------------------------------------------------=
// ICursorUpdateARow GetColumn
//=--------------------------------------------------------------------------=
// Gets the current value of the specified column
//
// Parameters:
// pcid - [in] a pointer to the columnID for which data is
// to be returned
// pBindParams - [out] a pointer to a column binding structure in which
// to return data
// pdwFlags - [out] a pointer to memory in which to return the
// changed state of the returned data
//
// Output:
// HRESULT - S_OK if successful
// E_INVALIDARG bad parameter
// E_OUTOFMEMORY not enough memory
// E_FAIL a provider-specific error occured
// CURSOR_DB_E_STATEERROR not in update or add mode
// CURSOR_DB_E_BADCOLUMNID pcid was not a valid column identifier
// CURSOR_DB_E_BADBINDINFO bad binding information
//
// Notes:
//
HRESULT CVDCursor::GetColumn(CURSOR_DBCOLUMNID *pcid, CURSOR_DBBINDPARAMS *pBindParams, DWORD *pdwFlags)
{
ASSERT_POINTER(pcid, CURSOR_DBCOLUMNID)
ASSERT_POINTER(pBindParams, CURSOR_DBBINDPARAMS)
ASSERT_NULL_OR_POINTER(pdwFlags, DWORD)
// make sure rowset is valid
if (!IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorUpdateARow, m_pResourceDLL);
return E_FAIL;
}
// make sure we have all necessary pointers
if (!pcid || !pBindParams || !pBindParams->pData)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorUpdateARow, m_pResourceDLL);
return E_INVALIDARG;
}
// make sure we are in update or add mode
if (m_pCursorPosition->GetEditMode() == CURSOR_DBEDITMODE_NONE)
{
VDSetErrorInfo(IDS_ERR_STATEERROR, IID_ICursorUpdateARow, m_pResourceDLL);
return CURSOR_DB_E_STATEERROR;
}
CVDRowsetColumn * pColumn;
// validate cursor binding parameters and get rowset column
HRESULT hr = ValidateCursorBindParams(pcid, pBindParams, &pColumn);
if (FAILED(hr))
return hr;
// get column update pointer for this column
CVDColumnUpdate * pColumnUpdate = m_pCursorPosition->GetColumnUpdate(pColumn->GetNumber());
// if not changed, get original value
if (!pColumnUpdate)
{
hr = GetOriginalColumn(pColumn, pBindParams);
if (pdwFlags)
*pdwFlags = CURSOR_DBCOLUMNDATA_UNCHANGED;
}
else // otherwise, get modified value
{
hr = GetModifiedColumn(pColumnUpdate, pBindParams);
if (pdwFlags)
*pdwFlags = CURSOR_DBCOLUMNDATA_CHANGED;
}
return hr;
}
//=--------------------------------------------------------------------------=
// ICursorUpdateARow GetEditMode
//=--------------------------------------------------------------------------=
// Gets the current edit mode: add, update or none
//
// Parameters:
// pdwState - [out] a pointer to memory in which to return the
// current edit mode
//
// Output:
// HRESULT - S_OK if successful
// E_FAIL a provider-specific error occured
// E_INVALIDARG bad parameter
//
// Notes:
//
HRESULT CVDCursor::GetEditMode(DWORD *pdwState)
{
ASSERT_POINTER(pdwState, DWORD)
// make sure we have a valid rowset
if (!IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorUpdateARow, m_pResourceDLL);
return E_FAIL;
}
// make sure we have a pointer
if (!pdwState)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorUpdateARow, m_pResourceDLL);
return E_INVALIDARG;
}
// return edit mode
*pdwState = m_pCursorPosition->GetEditMode();
return S_OK;
}
//=--------------------------------------------------------------------------=
// ICursorUpdateARow Update
//=--------------------------------------------------------------------------=
// Sends the contents of the edit buffer to the database and optionally
// returns the bookmark for the updated or added row
//
// Parameters:
// pBookmarkType - [in] a pointer to a columnID that specifies the type
// of bookmark desired
// pcbBookmark - [out] a pointer to memory in which to return the actual
// length of the returned bookmark
// ppBookmark - [out] a pointer to memory in which to return a pointer
// to a bookmark
//
// Output:
// HRESULT - S_OK if successful
// E_FAIL a provider-specific error occured
// E_OUTOFMEMORY not enough memory
// CURSOR_DB_E_STATEERROR not in update or add mode
//
// Notes:
// Kagera does not allow variant bindings on dbtimestamp fields, so this code
// updates dbtimestamp fields using string pointers if possible.
//
HRESULT CVDCursor::Update(CURSOR_DBCOLUMNID *pBookmarkType, ULONG *pcbBookmark, void **ppBookmark)
{
ASSERT_NULL_OR_POINTER(pBookmarkType, CURSOR_DBCOLUMNID)
ASSERT_NULL_OR_POINTER(pcbBookmark, ULONG)
ASSERT_NULL_OR_POINTER(ppBookmark, void*)
IAccessor * pAccessor = GetAccessor();
IRowsetChange * pRowsetChange = GetRowsetChange();
IRowsetUpdate * pRowsetUpdate = GetRowsetUpdate();
BOOL fUndo = FALSE;
BOOL fInsert = FALSE;
// make sure we have valid accessor and change pointers
if (!pAccessor || !pRowsetChange || !IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorUpdateARow, m_pResourceDLL);
return E_FAIL;
}
// get current edit mode
const DWORD dwEditMode = m_pCursorPosition->GetEditMode();
// make sure we are in update or add mode
if (dwEditMode == CURSOR_DBEDITMODE_NONE)
{
VDSetErrorInfo(IDS_ERR_STATEERROR, IID_ICursorUpdateARow, m_pResourceDLL);
return CURSOR_DB_E_STATEERROR;
}
// get hRow of the row currently being edited
HROW hRow = m_pCursorPosition->GetEditRow();
// get column count
const ULONG ulColumns = GetCursorMain()->GetColumnsCount();
// create update buffer accessor bindings
DBBINDING * pBindings = new DBBINDING[ulColumns];
if (!pBindings)
{
VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursorUpdateARow, m_pResourceDLL);
return E_OUTOFMEMORY;
}
// clear out bindings
memset(pBindings, 0, ulColumns * sizeof(DBBINDING));
// setup notification structures
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_DATA_CHANGED |
CURSOR_DBEVENT_NONCURRENT_ROW_DATA_CHANGED |
CURSOR_DBEVENT_SET_OF_ROWS_CHANGED;
CURSOR_DBNOTIFYREASON rgReasons[1];
VariantInit((VARIANT*)&rgReasons[0].arg1);
VariantInit((VARIANT*)&rgReasons[0].arg2);
switch (dwEditMode)
{
case CURSOR_DBEDITMODE_UPDATE:
rgReasons[0].dwReason = CURSOR_DBREASON_MODIFIED;
rgReasons[0].arg1 = m_pCursorPosition->m_bmCurrent.GetBookmarkVariant();
break;
case CURSOR_DBEDITMODE_ADD:
rgReasons[0].dwReason = CURSOR_DBREASON_INSERTED;
rgReasons[0].arg1 = m_pCursorPosition->m_bmAddRow.GetBookmarkVariant();
break;
}
// notify other interested parties of action
HRESULT hr = m_pCursorPosition->NotifyBefore(dwEventWhat, 1, rgReasons);
// make sure action was not cancelled
if (hr != S_OK)
{
// destroy bindings
delete [] pBindings;
VDSetErrorInfo(IDS_ERR_ACTIONCANCELLED, IID_ICursorUpdateARow, m_pResourceDLL);
return E_FAIL;
}
// variables
ULONG cBindings = 0;
DBBINDING * pBinding = pBindings;
CVDColumnUpdate * pColumnUpdate;
ULONG obUpdate = 0;
// iterate through columns and setup binding structures
for (ULONG ulCol = 0; ulCol < ulColumns; ulCol++)
{
// get column update pointer
pColumnUpdate = m_pCursorPosition->GetColumnUpdate(ulCol);
if (pColumnUpdate)
{
// create column update buffer binding
pBinding->iOrdinal = pColumnUpdate->GetColumn()->GetOrdinal();
pBinding->obValue = obUpdate + sizeof(DBSTATUS) + sizeof(ULONG);
pBinding->obLength = obUpdate + sizeof(DBSTATUS);
pBinding->obStatus = obUpdate;
pBinding->dwPart = DBPART_VALUE;
pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED;
pBinding->wType = DBTYPE_VARIANT;
// determine if length part is included
if (pColumnUpdate->GetVarDataLen() != CURSOR_DB_NOVALUE)
pBinding->dwPart |= DBPART_LENGTH;
pBinding->dwPart |= DBPART_STATUS;
// check for variant binding on dbtimestamp field, and supplied variant is a bstr
if (pColumnUpdate->GetColumn()->GetType() == DBTYPE_DBTIMESTAMP && pColumnUpdate->GetVariantType() == VT_BSTR)
{
pBinding->dwPart &= ~DBPART_LENGTH;
pBinding->wType = DBTYPE_BYREF | DBTYPE_WSTR;
}
// increment update offset
obUpdate += sizeof(DBSTATUS) + sizeof(ULONG) + sizeof(VARIANT);
// increment binding
cBindings++;
pBinding++;
}
}
// if we have any bindings, then update
if (cBindings)
{
HACCESSOR hAccessor;
// create update accessor
hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, cBindings, pBindings, 0, &hAccessor, NULL);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_CREATEACCESSORFAILED, IID_ICursorUpdateARow, pAccessor, IID_IAccessor,
m_pResourceDLL);
if (FAILED(hr))
{
// destroy bindings
delete [] pBindings;
// notify other interested parties of failure
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons);
return hr;
}
// create update buffer
BYTE * pBuffer = new BYTE[cBindings * (sizeof(DBSTATUS) + sizeof(ULONG) + sizeof(VARIANT))];
BYTE * pBufferOld = new BYTE[cBindings * (sizeof(DBSTATUS) + sizeof(ULONG) + sizeof(VARIANT))];
if (!pBuffer || !pBufferOld)
{
// destroy bindings
delete [] pBindings;
// destroy buffers
delete [] pBuffer;
delete [] pBufferOld;
// release update accessor
pAccessor->ReleaseAccessor(hAccessor, NULL);
// notify other interested parties of failure
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons);
return E_OUTOFMEMORY;
}
// variables
obUpdate = 0;
pBinding = pBindings;
CURSOR_DBVARIANT variant;
// iterate through columns and setup buffer
for (ULONG ulCol = 0; ulCol < ulColumns; ulCol++)
{
// get column update pointer
pColumnUpdate = m_pCursorPosition->GetColumnUpdate(ulCol);
if (pColumnUpdate)
{
// obtain current status
DBSTATUS status = CursorInfoToStatus(pColumnUpdate->GetInfo());
// get value
variant = pColumnUpdate->GetVariant();
// check for empty value since some controls treat this as NULL and make sure that we
// treat the empty value as null
if (status == DBSTATUS_S_OK &&
variant.vt == VT_BSTR &&
wcslen(variant.bstrVal) == 0)
{
*(DBSTATUS*)(pBuffer + obUpdate) = DBSTATUS_S_ISNULL;
}
else
{
*(DBSTATUS*)(pBuffer + obUpdate) = status;
}
*(DBSTATUS*)(pBufferOld + obUpdate) = DBSTATUS_S_ISNULL;
// if necessary, set length part in buffer
if (pBinding->dwPart & DBPART_LENGTH)
{
*(ULONG*)(pBuffer + obUpdate + sizeof(DBSTATUS)) = pColumnUpdate->GetVarDataLen();
*(ULONG*)(pBufferOld + obUpdate + sizeof(DBSTATUS)) = 0;
}
// always set value part in buffer
if (pBinding->wType == (DBTYPE_BYREF | DBTYPE_WSTR))
{
*(BSTR*)(pBuffer + obUpdate + sizeof(DBSTATUS) + sizeof(ULONG)) = variant.bstrVal;
*(BSTR*)(pBufferOld + obUpdate + sizeof(DBSTATUS) + sizeof(ULONG)) = NULL;
}
else
{
memcpy(pBuffer + obUpdate + sizeof(DBSTATUS) + sizeof(ULONG), &variant, sizeof(VARIANT));
VariantInit((VARIANT *) (pBufferOld + obUpdate + sizeof(DBSTATUS) + sizeof(ULONG)));
}
// increment update offset
obUpdate += sizeof(DBSTATUS) + sizeof(ULONG) + sizeof(VARIANT);
// increment binding
pBinding++;
}
}
DBPENDINGSTATUS status;
if (dwEditMode == CURSOR_DBEDITMODE_ADD)
fInsert = TRUE;
else if (pRowsetUpdate)
{
pRowsetUpdate->GetRowStatus(NULL, 1, &hRow, &status);
if (status == DBPENDINGSTATUS_UNCHANGED)
fUndo = TRUE;
}
if (!fUndo && !fInsert)
{
hr = GetRowset()->GetData(hRow, hAccessor, pBufferOld);
if (status != DBPENDINGSTATUS_NEW)
fUndo = TRUE;
}
// modify columns (set/clear internal set data flag)
GetCursorMain()->SetInternalSetData(TRUE);
hr = pRowsetChange->SetData(hRow, hAccessor, pBuffer);
if (hr == DB_S_ERRORSOCCURRED)
{
// since partial changes occurred, restore data back
// to original values.
if (fUndo)
pRowsetUpdate->Undo(NULL, 1, &hRow, NULL, NULL, NULL);
else if (status != DBPENDINGSTATUS_NEW)
pRowsetChange->SetData(hRow, hAccessor, pBufferOld);
hr = DB_E_ERRORSOCCURRED;
}
GetCursorMain()->SetInternalSetData(FALSE);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_SETDATAFAILED, IID_ICursorUpdateARow, pRowsetChange, IID_IRowsetChange,
m_pResourceDLL);
// release update accessor
pAccessor->ReleaseAccessor(hAccessor, NULL);
obUpdate = 0;
pBinding = pBindings;
// iterate through columns and reset buffer
for (ulCol = 0; ulCol < ulColumns; ulCol++)
{
if (m_pCursorPosition->GetColumnUpdate(ulCol))
{
VARIANT var;
if (pBinding->wType == (DBTYPE_BYREF | DBTYPE_WSTR))
{
var.vt = VT_BSTR;
var.bstrVal = *(BSTR*)(pBufferOld + obUpdate + sizeof(DBSTATUS) + sizeof(ULONG));
}
else
{
memcpy(&var, pBufferOld + obUpdate + sizeof(DBSTATUS) + sizeof(ULONG), sizeof(VARIANT));
}
VariantClear(&var);
// increment update offset
obUpdate += sizeof(DBSTATUS) + sizeof(ULONG) + sizeof(VARIANT);
// increment binding
pBinding++;
}
}
// destroy update buffer
delete [] pBuffer;
delete [] pBufferOld;
}
// destroy bindings
delete [] pBindings;
if (FAILED(hr))
{
// notify other interested parties of failure
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons);
}
else
{
// return bookmark if requested to do so
if (pBookmarkType && pcbBookmark && ppBookmark)
{
switch (dwEditMode)
{
case CURSOR_DBEDITMODE_UPDATE:
*pcbBookmark = m_pCursorPosition->m_bmCurrent.GetBookmarkLen();
memcpy(*ppBookmark, m_pCursorPosition->m_bmCurrent.GetBookmark(), *pcbBookmark);
break;
case CURSOR_DBEDITMODE_ADD:
*pcbBookmark = m_pCursorPosition->m_bmAddRow.GetBookmarkLen();
memcpy(*ppBookmark, m_pCursorPosition->m_bmAddRow.GetBookmark(), *pcbBookmark);
break;
}
}
// if acquired, release same-row clone
if (m_pCursorPosition->GetSameRowClone())
m_pCursorPosition->ReleaseSameRowClone();
// also, release add row if we have one
if (m_pCursorPosition->m_bmAddRow.GetHRow())
m_pCursorPosition->ReleaseAddRow();
// reset edit mode
m_pCursorPosition->SetEditMode(CURSOR_DBEDITMODE_NONE);
// reset column updates
m_pCursorPosition->ResetColumnUpdates();
// notify other interested parties of success
m_pCursorPosition->NotifyAfter(dwEventWhat, 1, rgReasons);
}
return hr;
}
//=--------------------------------------------------------------------------=
// ICursorUpdateARow Cancel
//=--------------------------------------------------------------------------=
// Cancels the update or add operation
//
// Parameters:
// none
//
// Output:
// HRESULT - S_OK if successful
// E_FAIL a provider-specific error occured
// CURSOR_DB_E_STATEERROR not in update or add mode
//
// Notes:
//
HRESULT CVDCursor::Cancel(void)
{
// make sure we have a valid rowset
if (!IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorUpdateARow, m_pResourceDLL);
return E_FAIL;
}
// get current edit mode
const DWORD dwEditMode = m_pCursorPosition->GetEditMode();
// make sure we are in update or add mode
if (dwEditMode == CURSOR_DBEDITMODE_NONE)
{
VDSetErrorInfo(IDS_ERR_STATEERROR, IID_ICursorUpdateARow, m_pResourceDLL);
return CURSOR_DB_E_STATEERROR;
}
// try to get update pointer
IRowsetUpdate * pRowsetUpdate = GetRowsetUpdate();
// get hRow of the row currently being edited
HROW hRow = m_pCursorPosition->GetEditRow();
// setup notification structures
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_DATA_CHANGED;
CURSOR_DBNOTIFYREASON rgReasons[1];
VariantInit((VARIANT*)&rgReasons[0].arg1);
VariantInit((VARIANT*)&rgReasons[0].arg2);
rgReasons[0].dwReason = CURSOR_DBREASON_CANCELUPDATE;
// notify other interested parties of action
HRESULT hr = m_pCursorPosition->NotifyBefore(dwEventWhat, 1, rgReasons);
// make sure action was not cancelled
if (hr != S_OK)
{
VDSetErrorInfo(IDS_ERR_ACTIONCANCELLED, IID_ICursorUpdateARow, m_pResourceDLL);
return E_FAIL;
}
// if we are comming out of add mode, undo inserted row
if (pRowsetUpdate && dwEditMode == CURSOR_DBEDITMODE_ADD)
{
hr = pRowsetUpdate->Undo(0, 1, &hRow, NULL, NULL, NULL);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_UNDOFAILED, IID_ICursorUpdateARow, pRowsetUpdate, IID_IRowsetUpdate,
m_pResourceDLL);
if (FAILED(hr))
{
// notify other interested parties of failure
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons);
return hr;
}
}
// if acquired, release same-row clone
if (m_pCursorPosition->GetSameRowClone())
m_pCursorPosition->ReleaseSameRowClone();
// also, release add row if we have one
if (m_pCursorPosition->m_bmAddRow.GetHRow())
m_pCursorPosition->ReleaseAddRow();
// reset edit mode
m_pCursorPosition->SetEditMode(CURSOR_DBEDITMODE_NONE);
// reset column updates
m_pCursorPosition->ResetColumnUpdates();
// notify other interested parties of success
m_pCursorPosition->NotifyAfter(dwEventWhat, 1, rgReasons);
return S_OK;
}
//=--------------------------------------------------------------------------=
// ICursorUpdateARow Delete
//=--------------------------------------------------------------------------=
// Deletes the current row
//
// Parameters:
// none
//
// Output:
// HRESULT - S_OK if successful
// E_FAIL a provider-specific error occured
// CURSOR_DB_E_UPDATEINPROGRESS an update is already in progress
//
// Notes:
//
HRESULT CVDCursor::Delete(void)
{
IRowsetChange * pRowsetChange = GetRowsetChange();
// make sure we have a valid change pointer
if (!pRowsetChange || !IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorUpdateARow, m_pResourceDLL);
return E_FAIL;
}
// make sure that an update is not already in progress
if (m_pCursorPosition->GetEditMode() != CURSOR_DBEDITMODE_NONE)
{
VDSetErrorInfo(IDS_ERR_UPDATEINPROGRESS, IID_ICursorUpdateARow, m_pResourceDLL);
return CURSOR_DB_E_UPDATEINPROGRESS;
}
// get current hRow
HROW hRow = m_pCursorPosition->m_bmCurrent.GetHRow();
// setup notification structures
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_DATA_CHANGED |
CURSOR_DBEVENT_SET_OF_ROWS_CHANGED;
CURSOR_DBNOTIFYREASON rgReasons[1];
VariantInit((VARIANT*)&rgReasons[0].arg1);
VariantInit((VARIANT*)&rgReasons[0].arg2);
rgReasons[0].dwReason = CURSOR_DBREASON_DELETED;
rgReasons[0].arg1 = m_pCursorPosition->m_bmCurrent.GetBookmarkVariant();
// notify other interested parties of action
HRESULT hr = m_pCursorPosition->NotifyBefore(dwEventWhat, 1, rgReasons);
// make sure action was not cancelled
if (hr != S_OK)
{
VDSetErrorInfo(IDS_ERR_ACTIONCANCELLED, IID_ICursorUpdateARow, m_pResourceDLL);
return E_FAIL;
}
// try to delete current row (set/clear internal delete rows flag)
GetCursorMain()->SetInternalDeleteRows(TRUE);
hr = pRowsetChange->DeleteRows(0, 1, &hRow, NULL);
GetCursorMain()->SetInternalDeleteRows(FALSE);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_DELETEROWSFAILED, IID_ICursorUpdateARow, pRowsetChange, IID_IRowsetChange,
m_pResourceDLL);
if (FAILED(hr))
{
// notify other interested parties of failure
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons);
}
else
{
// notify other interested parties of success
m_pCursorPosition->NotifyAfter(dwEventWhat, 1, rgReasons);
}
return hr;
}
//=--------------------------------------------------------------------------=
// ICursorFind methods
//=--------------------------------------------------------------------------=
// ICursorFind FindByValues
//
HRESULT CVDCursor::FindByValues(ULONG cbBookmark,
LPVOID pBookmark,
DWORD dwFindFlags,
ULONG cValues,
CURSOR_DBCOLUMNID rgColumns[],
CURSOR_DBVARIANT rgValues[],
DWORD rgdwSeekFlags[],
CURSOR_DBFETCHROWS FAR *pFetchParams)
{
//////////////////////////////////////////////////////////////////////////
// this implementation limits the number of columns that can be searched
// to one, since current OLEDB spec only allows a single column accessor
// to be passed to IRowsetFind::FindNextRow (06/11/97)
//
if (cValues > 1)
return E_FAIL;
//
//////////////////////////////////////////////////////////////////////////
IAccessor * pAccessor = GetAccessor();
IRowsetFind * pRowsetFind = GetRowsetFind();
// make sure we have valid accessor and find pointers
if (!pAccessor || !pRowsetFind || !IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_IEntryID, m_pResourceDLL);
return E_FAIL;
}
// check for values
if (!cValues)
return S_OK;
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_CHANGED;
CURSOR_DBNOTIFYREASON rgReasons[1];
rgReasons[0].dwReason = CURSOR_DBREASON_FIND;
VariantInit((VARIANT*)&rgReasons[0].arg1);
rgReasons[0].arg1.vt = VT_UI4;
rgReasons[0].arg1.lVal = dwFindFlags;
VariantInit((VARIANT*)&rgReasons[0].arg2);
// notify other interested parties
HRESULT hr = m_pCursorPosition->NotifyBefore(dwEventWhat, 1, rgReasons);
// make sure action was not cancelled
if (hr != S_OK)
{
VDSetErrorInfo(IDS_ERR_ACTIONCANCELLED, IID_ICursorFind, m_pResourceDLL);
return E_FAIL;
}
ULONG ul;
HROW * pRow = NULL;
ULONG cRowsObtained = 0;
HACCESSOR hAccessor = NULL;
// allocate necessary memory
ULONG * pColumns = new ULONG[cValues];
DBTYPE * pDBTypes = new DBTYPE[cValues];
DBCOMPAREOP * pDBCompareOp = new DBCOMPAREOP[cValues];
BYTE ** ppValues = new BYTE*[cValues];
BOOL * fMemAllocated = new BOOL[cValues];
if (fMemAllocated)
{
// always init fMemAllocated flags to false
memset(fMemAllocated, 0, sizeof(BOOL) * cValues);
}
// make sure we received all requested memory
if (!pColumns || !pDBTypes || !ppValues || !pDBCompareOp || !fMemAllocated)
{
VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_IRowsetFind, m_pResourceDLL);
hr = E_OUTOFMEMORY;
goto cleanup;
}
// iterate through columns
for (ul = 0; ul < cValues; ul++)
{
// get column ordinal position
hr = GetOrdinal(rgColumns[ul], &pColumns[ul]);
if (FAILED(hr))
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_IRowsetFind, m_pResourceDLL);
hr = E_INVALIDARG;
goto cleanup;
}
// get find values from CURSOR_DBVARIANT
hr = GetDataFromDBVariant(&rgValues[ul],
&pDBTypes[ul],
&(ppValues[ul]),
&fMemAllocated[ul]);
if (FAILED(hr))
{
VDSetErrorInfo(IDS_ERR_CANTCOERCE, IID_IRowsetFind, m_pResourceDLL);
goto cleanup;
}
// setup seek flags
switch (rgdwSeekFlags[ul])
{
case CURSOR_DBSEEK_LT:
pDBCompareOp[ul] = DBCOMPAREOPS_LT;
break;
case CURSOR_DBSEEK_LE:
pDBCompareOp[ul] = DBCOMPAREOPS_LE;
break;
case CURSOR_DBSEEK_EQ:
pDBCompareOp[ul] = DBCOMPAREOPS_EQ;
break;
case CURSOR_DBSEEK_GE:
pDBCompareOp[ul] = DBCOMPAREOPS_GE;
break;
case CURSOR_DBSEEK_GT:
pDBCompareOp[ul] = DBCOMPAREOPS_GT;
break;
case CURSOR_DBSEEK_PARTIALEQ:
pDBCompareOp[ul] = DBCOMPAREOPS_BEGINSWITH;
break;
default:
VDSetErrorInfo(IDS_ERR_INVALIDSEEKFLAGS, IID_IRowsetFind, m_pResourceDLL);
hr = E_FAIL;
goto cleanup;
}
}
LONG cRows;
BOOL fSkipCurrent;
// determine direction of seek
if (CURSOR_DBFINDFLAGS_FINDPRIOR == dwFindFlags)
{
cRows = -1;
fSkipCurrent = TRUE;
}
else
{
cRows = 1;
fSkipCurrent = TRUE;
}
BYTE bSpecialBM;
// check for standard bookmarks
if (CURSOR_DB_BMK_SIZE == cbBookmark)
{
if (memcmp(&CURSOR_DBBMK_BEGINNING, pBookmark, CURSOR_DB_BMK_SIZE) == 0)
{
cbBookmark = sizeof(BYTE);
bSpecialBM = DBBMK_FIRST;
pBookmark = &bSpecialBM;
fSkipCurrent = FALSE;
}
else
if (memcmp(&CURSOR_DBBMK_END, pBookmark, CURSOR_DB_BMK_SIZE) == 0)
{
cbBookmark = sizeof(BYTE);
bSpecialBM = DBBMK_LAST;
pBookmark = &bSpecialBM;
fSkipCurrent = FALSE;
}
else
if (memcmp(&CURSOR_DBBMK_CURRENT, pBookmark, CURSOR_DB_BMK_SIZE) == 0)
{
cbBookmark = m_pCursorPosition->m_bmCurrent.GetBookmarkLen();
pBookmark = m_pCursorPosition->m_bmCurrent.GetBookmark();
}
}
DBBINDING binding;
// clear out binding
memset(&binding, 0, sizeof(DBBINDING));
// create value binding
binding.iOrdinal = pColumns[0];
binding.obValue = 0;
binding.dwPart = DBPART_VALUE;
binding.dwMemOwner = DBMEMOWNER_CLIENTOWNED;
binding.cbMaxLen = 0x7FFFFFFF;
binding.wType = pDBTypes[0];
// create accessor describing the value to be matched
hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &binding, 0, &hAccessor, NULL);
if (FAILED(hr))
goto cleanup;
// try to find hRow satisfying our condition
hr = pRowsetFind->FindNextRow(DB_NULL_HCHAPTER,
hAccessor,
ppValues[0],
pDBCompareOp[0],
cbBookmark,
(BYTE*)pBookmark,
fSkipCurrent,
cRows,
&cRowsObtained,
&pRow);
// check to see if we rached end of rowset
if (hr == DB_S_ENDOFROWSET && !cRowsObtained)
hr = E_FAIL;
if (FAILED(hr))
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_FINDFAILED, IID_ICursorFind, pRowsetFind, IID_IRowsetFind, m_pResourceDLL);
// check to see if we got the hRow
if (SUCCEEDED(hr) && cRowsObtained)
{
// allocate buffer for bookmark plus length indicator
BYTE * pBuff = new BYTE[GetCursorMain()->GetMaxBookmarkLen() + sizeof(ULONG)];
if (!pBuff)
hr = E_OUTOFMEMORY;
else
{
// get the bookmark data
hr = GetRowset()->GetData(*pRow, GetCursorMain()->GetBookmarkAccessor(), pBuff);
if (SUCCEEDED(hr))
{
ULONG * pulLen = (ULONG*)pBuff;
BYTE * pbmdata = pBuff + sizeof(ULONG);
LARGE_INTEGER dlOffset;
dlOffset.HighPart = 0;
dlOffset.LowPart = 0;
hr = FetchAtBookmark(*pulLen, pbmdata, dlOffset, pFetchParams);
}
else
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETDATAFAILED, IID_ICursorFind, pRowsetFind, IID_IRowset, m_pResourceDLL);
delete [] pBuff;
}
}
if (pRow)
{
// release hRow
if (cRowsObtained)
GetRowset()->ReleaseRows(1, pRow, NULL, NULL, NULL);
g_pMalloc->Free(pRow);
}
cleanup:
rgReasons[0].arg2.vt = VT_BOOL;
V_BOOL(&rgReasons[0].arg2) = SUCCEEDED(hr) ? TRUE : FALSE;
if (SUCCEEDED(hr))
{
// notify other interested parties of success
m_pCursorPosition->NotifyAfter(dwEventWhat, 1, rgReasons);
}
else
{
// notify other interested parties of failure
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons);
}
// free values
if (ppValues && fMemAllocated)
{
for (ul = 0; ul < cValues; ul++)
{
if (fMemAllocated[ul] && ppValues[ul])
g_pMalloc->Free(ppValues[ul]);
}
}
// free memory
delete [] pColumns;
delete [] pDBTypes;
delete [] ppValues;
delete [] pDBCompareOp;
delete [] fMemAllocated;
// release accessor
if (hAccessor)
pAccessor->ReleaseAccessor(hAccessor, NULL);
return hr;
}
//=--------------------------------------------------------------------------=
// IEnrtyID methods
//=--------------------------------------------------------------------------=
// IEntryID GetInterface
//=--------------------------------------------------------------------------=
// Gets the requested interface pointer to the given entryID
//
// Parameters:
// cbEntryID - [in] the size of the entryID
// pEntryID - [in] a pointer to the entryID
// dwFlags - [in] interface specific flags
// riid - [in] the interface id for the interface desired
// ppvObj - [out] a pointer to memory in which to return interface pointer
//
// Output:
// HRESULT - S_OK if successful
// E_INVALIDARG bad parameter
// E_OUTOFMEMORY not enough memory
// E_FAIL a provider-specific error occured
// E_NOINTERFACE no such interface supported
// CURSOR_DB_E_BADENTRYID bad entry identifier
//
// Notes:
//
HRESULT CVDCursor::GetInterface(ULONG cbEntryID, void *pEntryID, DWORD dwFlags, REFIID riid, IUnknown **ppvObj)
{
ASSERT_POINTER(pEntryID, BYTE)
ASSERT_POINTER(ppvObj, IUnknown*)
IRowset * pRowset = GetRowset();
// make sure we have a valid rowset pointer
if (!pRowset || !IsRowsetValid())
{
VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_IEntryID, m_pResourceDLL);
return E_FAIL;
}
// make sure we have all necessary pointers
if (!pEntryID || !ppvObj)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_IEntryID, m_pResourceDLL);
return E_INVALIDARG;
}
// init out parameter
*ppvObj = NULL;
HROW hRow;
CVDRowsetColumn * pColumn;
// validate supplied entryID, and get rowset column and hRow
HRESULT hr = ValidateEntryID(cbEntryID, (BYTE*)pEntryID, &pColumn, &hRow);
if (FAILED(hr))
return hr;
IUnknown * pUnknown = NULL;
// first, try to get requested interface from entry identifier
hr = QueryEntryIDInterface(pColumn, hRow, dwFlags, riid, &pUnknown);
// if we succeeded or caller is not asking for IStream then leave
if (SUCCEEDED(hr) || riid != IID_IStream)
{
// release reference on hRow
pRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL);
*ppvObj = pUnknown;
return hr;
}
#ifndef VD_DONT_IMPLEMENT_ISTREAM
IStream * pStream;
// create stream from entry identifier
hr = CreateEntryIDStream(pColumn, hRow, &pStream);
if (FAILED(hr))
{
// release reference on hRow
pRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL);
return hr;
}
CVDEntryIDData * pEntryIDData;
// create entryID data object
hr = CVDEntryIDData::Create(m_pCursorPosition, pColumn, hRow, pStream, &pEntryIDData, m_pResourceDLL);
// release reference on hRow
pRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL);
// release reference on stream
pStream->Release();
if (FAILED(hr))
return hr;
CVDStream * pVDStream;
// create viaduct stream object
hr = CVDStream::Create(pEntryIDData, pStream, &pVDStream, m_pResourceDLL);
// release reference on entryID data object
pEntryIDData->Release();
if (FAILED(hr))
return hr;
// return stream
*ppvObj = pVDStream;
return S_OK;
#else //VD_DONT_IMPLEMENT_ISTREAM
// release reference on hRow
pRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL);
VDSetErrorInfo(IDS_ERR_NOINTERFACE, IID_IEntryID, m_pResourceDLL);
return E_NOINTERFACE;
#endif //VD_DONT_IMPLEMENT_ISTREAM
}
/////////////////////////////////////////////////////////////////////////
// CVDNotifier functions
/////////////////////////////////////////////////////////////////////////
//+-------------------------------------------------------------------------
// Member: Notify Fail (public)
//
// Synopsis: send NotifyFail notification
//
// Arguments: dwEventWhat [in] what event is causing the notification
// cReasons [in] how many reasons
// rgReasons [in] list of reasons for the event
//
// Returns: S_OK it worked
HRESULT
CVDCursor::NotifyFail(DWORD dwEventWhat, ULONG cReasons,
CURSOR_DBNOTIFYREASON rgReasons[])
{
CVDNotifyDBEventsConnPt * pNotifyDBEventsConnPt = m_pConnPtContainer->GetNotifyDBEventsConnPt();
UINT uiConnectionsActive = pNotifyDBEventsConnPt->GetConnectionsActive();
INotifyDBEvents ** ppNotifyDBEvents = pNotifyDBEventsConnPt->GetNotifyDBEventsTable();
for (UINT uiConn = 0; uiConn < uiConnectionsActive; uiConn++)
ppNotifyDBEvents[uiConnectionsActive - uiConn - 1]->FailedToDo(dwEventWhat, cReasons, rgReasons);
return S_OK;
}
/////////////////////////////////////////////////////////////////////////
// CCursorNotifier helper functions
/////////////////////////////////////////////////////////////////////////
//+-------------------------------------------------------------------------
// Member: Notify OK To Do (public)
//
// Synopsis: Send OKToDo notification. If a client objects (by
// returning a non-zero HR, send FailedToDo to notified
// clients to cancel the event.
//
// Arguments: dwEventWhat [in] what event is causing the notification
// cReasons [in] how many reasons
// rgReasons [in] list of reasons for the event
//
// Returns: S_OK all clients agree it's OK to do the event
// other some client disagrees
HRESULT
CVDCursor::NotifyOKToDo(DWORD dwEventWhat, ULONG cReasons,
CURSOR_DBNOTIFYREASON rgReasons[])
{
HRESULT hr = S_OK;
CVDNotifyDBEventsConnPt * pNotifyDBEventsConnPt = m_pConnPtContainer->GetNotifyDBEventsConnPt();
UINT uiConnectionsActive = pNotifyDBEventsConnPt->GetConnectionsActive();
INotifyDBEvents ** ppNotifyDBEvents = pNotifyDBEventsConnPt->GetNotifyDBEventsTable();
for (UINT uiConn = 0; uiConn < uiConnectionsActive; uiConn++)
{
hr = ppNotifyDBEvents[uiConnectionsActive - uiConn - 1]->OKToDo(dwEventWhat, cReasons, rgReasons);
if (S_OK != hr)
{
for (UINT ui = 0; ui <= uiConn; ui++)
ppNotifyDBEvents[uiConnectionsActive - ui - 1]->Cancelled(dwEventWhat, cReasons, rgReasons);
break;
}
}
return hr;
}
//+-------------------------------------------------------------------------
// Member: Notify Sync Before (public)
//
// Synopsis: Send SyncBefore notification
//
// Arguments: dwEventWhat [in] what event is causing the notification
// cReasons [in] how many reasons
// rgReasons [in] list of reasons for the event
//
// Returns: S_OK all clients received notification
// other some client returned an error
HRESULT
CVDCursor::NotifySyncBefore(DWORD dwEventWhat, ULONG cReasons,
CURSOR_DBNOTIFYREASON rgReasons[])
{
HRESULT hr = S_OK;
CVDNotifyDBEventsConnPt * pNotifyDBEventsConnPt = m_pConnPtContainer->GetNotifyDBEventsConnPt();
UINT uiConnectionsActive = pNotifyDBEventsConnPt->GetConnectionsActive();
INotifyDBEvents ** ppNotifyDBEvents = pNotifyDBEventsConnPt->GetNotifyDBEventsTable();
for (UINT uiConn = 0; uiConn < uiConnectionsActive; uiConn++)
{
hr = ppNotifyDBEvents[uiConnectionsActive - uiConn - 1]->SyncBefore(dwEventWhat, cReasons, rgReasons);
if (S_OK != hr)
break;
}
return hr;
}
//+-------------------------------------------------------------------------
// Member: Notify About To Do (public)
//
// Synopsis: Send AboutToDo notification
//
// Arguments: dwEventWhat [in] what event is causing the notification
// cReasons [in] how many reasons
// rgReasons [in] list of reasons for the event
//
// Returns: S_OK all clients notified
// other some client returned an error
HRESULT
CVDCursor::NotifyAboutToDo(DWORD dwEventWhat, ULONG cReasons,
CURSOR_DBNOTIFYREASON rgReasons[])
{
HRESULT hr = S_OK;
CVDNotifyDBEventsConnPt * pNotifyDBEventsConnPt = m_pConnPtContainer->GetNotifyDBEventsConnPt();
UINT uiConnectionsActive = pNotifyDBEventsConnPt->GetConnectionsActive();
INotifyDBEvents ** ppNotifyDBEvents = pNotifyDBEventsConnPt->GetNotifyDBEventsTable();
for (UINT uiConn = 0; uiConn < uiConnectionsActive; uiConn++)
{
hr = ppNotifyDBEvents[uiConnectionsActive - uiConn - 1]->AboutToDo(dwEventWhat, cReasons, rgReasons);
if (S_OK != hr)
break;
}
return hr;
}
//+-------------------------------------------------------------------------
// Member: Notify Sync After (public)
//
// Synopsis: Send SyncAfter notification.
//
// Arguments: dwEventWhat [in] what event is causing the notification
// cReasons [in] how many reasons
// rgReasons [in] list of reasons for the event
//
// Returns: S_OK all clients notified
HRESULT
CVDCursor::NotifySyncAfter(DWORD dwEventWhat, ULONG cReasons,
CURSOR_DBNOTIFYREASON rgReasons[])
{
CVDNotifyDBEventsConnPt * pNotifyDBEventsConnPt = m_pConnPtContainer->GetNotifyDBEventsConnPt();
UINT uiConnectionsActive = pNotifyDBEventsConnPt->GetConnectionsActive();
INotifyDBEvents ** ppNotifyDBEvents = pNotifyDBEventsConnPt->GetNotifyDBEventsTable();
for (UINT uiConn = 0; uiConn < uiConnectionsActive; uiConn++)
ppNotifyDBEvents[uiConnectionsActive - uiConn - 1]->SyncAfter(dwEventWhat, cReasons, rgReasons);
return S_OK;
}
//+-------------------------------------------------------------------------
// Member: Notify Did Event (public)
//
// Synopsis: Send DidEvent notification
//
// Arguments: dwEventWhat [in] what event is causing the notification
// cReasons [in] how many reasons
// rgReasons [in] list of reasons for the event
//
// Returns: S_OK all clients notified
HRESULT
CVDCursor::NotifyDidEvent(DWORD dwEventWhat, ULONG cReasons,
CURSOR_DBNOTIFYREASON rgReasons[])
{
CVDNotifyDBEventsConnPt * pNotifyDBEventsConnPt = m_pConnPtContainer->GetNotifyDBEventsConnPt();
UINT uiConnectionsActive = pNotifyDBEventsConnPt->GetConnectionsActive();
INotifyDBEvents ** ppNotifyDBEvents = pNotifyDBEventsConnPt->GetNotifyDBEventsTable();
for (UINT uiConn = 0; uiConn < uiConnectionsActive; uiConn++)
ppNotifyDBEvents[uiConnectionsActive - uiConn - 1]->DidEvent(dwEventWhat, cReasons, rgReasons);
return S_OK;
}
//+-------------------------------------------------------------------------
// Member: Notify Cancel (public)
//
// Synopsis: Send Cancelled notification
//
// Arguments: dwEventWhat [in] what event is causing the notification
// cReasons [in] how many reasons
// rgReasons [in] list of reasons for the event
//
// Returns: S_OK all clients notified
HRESULT
CVDCursor::NotifyCancel(DWORD dwEventWhat, ULONG cReasons,
CURSOR_DBNOTIFYREASON rgReasons[])
{
CVDNotifyDBEventsConnPt * pNotifyDBEventsConnPt = m_pConnPtContainer->GetNotifyDBEventsConnPt();
UINT uiConnectionsActive = pNotifyDBEventsConnPt->GetConnectionsActive();
INotifyDBEvents ** ppNotifyDBEvents = pNotifyDBEventsConnPt->GetNotifyDBEventsTable();
for (UINT uiConn = 0; uiConn < uiConnectionsActive; uiConn++)
ppNotifyDBEvents[uiConnectionsActive - uiConn - 1]->Cancelled(dwEventWhat, cReasons, rgReasons);
return S_OK;
}