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