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

1419 lines
49 KiB

//---------------------------------------------------------------------------
// MetadataCursor.cpp : MetadataCursor 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 "CursBase.h"
#include "CursMeta.h"
#include "fastguid.h"
#include "resource.h"
SZTHISFILE
//=--------------------------------------------------------------------------=
// CVDMetadataCursor - Constructor
//
CVDMetadataCursor::CVDMetadataCursor()
{
m_dwRefCount = 1;
m_lCurrentRow = -1; // before first
m_ulColumns = 0;
m_pColumns = NULL;
m_ulMetaColumns = 0;
m_pMetaColumns = NULL;
#ifdef _DEBUG
g_cVDMetadataCursorCreated++;
#endif
}
//=--------------------------------------------------------------------------=
// ~CVDMetadataCursor - Destructor
//
CVDMetadataCursor::~CVDMetadataCursor()
{
#ifdef _DEBUG
g_cVDMetadataCursorDestroyed++;
#endif
}
//=--------------------------------------------------------------------------=
// RowToBookmark - Convert row to bookmark
//=--------------------------------------------------------------------------=
//
// Parameters:
// lRow - [in] a row number
// pcbBookmark - [out] a pointer to memory in which to return the length
// in bytes of the corresponding bookmark
// pBookmark - [out] a pointer to memory in which to return the bookmark
//
// Notes:
//
void CVDMetadataCursor::RowToBookmark(LONG lRow, ULONG * pcbBookmark, void * pBookmark) const
{
if (lRow < 0)
{
*pcbBookmark = CURSOR_DB_BMK_SIZE;
memcpy(pBookmark, &CURSOR_DBBMK_BEGINNING, CURSOR_DB_BMK_SIZE);
}
else if (lRow >= (LONG)m_ulColumns)
{
*pcbBookmark = CURSOR_DB_BMK_SIZE;
memcpy(pBookmark, &CURSOR_DBBMK_END, CURSOR_DB_BMK_SIZE);
}
else
{
*pcbBookmark = sizeof(LONG);
memcpy(pBookmark, &lRow, sizeof(LONG));
}
}
//=--------------------------------------------------------------------------=
// BookmarkToRow - Convert bookmark to row
//=--------------------------------------------------------------------------=
//
// Parameters:
// cbBookmark - [in] the length in bytes of the bookmark
// pBookmark - [in] a pointer to the bookmark
// pRow - [out] a pointer to memory in which to return the
// corresponding row
//
// Output:
// BOOL - TRUE if successful
//
// Notes:
//
BOOL CVDMetadataCursor::BookmarkToRow(ULONG cbBookmark, void * pBookmark, LONG * plRow) const
{
BOOL fResult = FALSE;
if (cbBookmark == CURSOR_DB_BMK_SIZE)
{
if (memcmp(pBookmark, &CURSOR_DBBMK_BEGINNING, CURSOR_DB_BMK_SIZE) == 0)
{
*plRow = -1;
fResult = TRUE;
}
else if (memcmp(pBookmark, &CURSOR_DBBMK_END, CURSOR_DB_BMK_SIZE) == 0)
{
*plRow = (LONG)m_ulColumns;
fResult = TRUE;
}
else if (memcmp(pBookmark, &CURSOR_DBBMK_CURRENT, CURSOR_DB_BMK_SIZE) == 0)
{
*plRow = m_lCurrentRow;
fResult = TRUE;
}
}
else
if (cbBookmark == sizeof(LONG))
{
memcpy(plRow, pBookmark, sizeof(LONG));
if (*plRow >= 0 && *plRow < (LONG)m_ulColumns)
fResult = TRUE;
}
return fResult;
}
//=--------------------------------------------------------------------------=
// ReturnData_I4 - Coerce I4 data into buffers
//=--------------------------------------------------------------------------=
// This function coerces the specified data into supplied buffers
//
// Parameters:
// dwData - [in] the 4-byte data
// pCursorBinding - [in] the cursor binding describing the format of the
// returned information
// pData - [in] a pointer to the fixed area buffer
// pVarData - [in] a pointer to the variable length buffer
//
// Output:
// ULONG - the number of bytes used in variable length buffer
//
// Notes:
//
ULONG CVDMetadataCursor::ReturnData_I4(DWORD dwData, CURSOR_DBCOLUMNBINDING * pCursorBinding,
BYTE * pData, BYTE * pVarData)
{
ULONG cbVarData = 0;
if (pCursorBinding->dwBinding == CURSOR_DBBINDING_DEFAULT)
{
if (pCursorBinding->obData != CURSOR_DB_NOVALUE)
*(DWORD*)(pData + pCursorBinding->obData) = dwData;
}
else if (pCursorBinding->dwBinding == CURSOR_DBBINDING_VARIANT)
{
if (pCursorBinding->obData != CURSOR_DB_NOVALUE)
{
CURSOR_DBVARIANT * pVariant = (CURSOR_DBVARIANT*)(pData + pCursorBinding->obData);
VariantInit((VARIANT*)pVariant);
pVariant->vt = CURSOR_DBTYPE_I4;
pVariant->lVal = dwData;
}
}
if (pCursorBinding->obVarDataLen != CURSOR_DB_NOVALUE)
*(ULONG*)(pData + pCursorBinding->obVarDataLen) = 0;
if (pCursorBinding->obInfo != CURSOR_DB_NOVALUE)
*(DWORD*)(pData + pCursorBinding->obInfo) = CURSOR_DB_NOINFO;
return cbVarData;
}
//=--------------------------------------------------------------------------=
// ReturnData_BOOL - Coerce BOOL data into buffers
//=--------------------------------------------------------------------------=
// This function coerces the specified data into supplied buffers
//
// Parameters:
// fData - [in] the boolean data
// pCursorBinding - [in] the cursor binding describing the format of the
// returned information
// pData - [in] a pointer to the fixed area buffer
// pVarData - [in] a pointer to the variable length buffer
//
// Output:
// ULONG - the number of bytes used in variable length buffer
//
// Notes:
//
ULONG CVDMetadataCursor::ReturnData_BOOL(VARIANT_BOOL fData, CURSOR_DBCOLUMNBINDING * pCursorBinding,
BYTE * pData, BYTE * pVarData)
{
ULONG cbVarData = 0;
if (pCursorBinding->dwBinding == CURSOR_DBBINDING_DEFAULT)
{
if (pCursorBinding->obData != CURSOR_DB_NOVALUE)
*(VARIANT_BOOL*)(pData + pCursorBinding->obData) = fData;
}
else if (pCursorBinding->dwBinding == CURSOR_DBBINDING_VARIANT)
{
if (pCursorBinding->obData != CURSOR_DB_NOVALUE)
{
CURSOR_DBVARIANT * pVariant = (CURSOR_DBVARIANT*)(pData + pCursorBinding->obData);
VariantInit((VARIANT*)pVariant);
pVariant->vt = CURSOR_DBTYPE_BOOL;
pVariant->boolVal = fData;
}
}
if (pCursorBinding->obVarDataLen != CURSOR_DB_NOVALUE)
*(ULONG*)(pData + pCursorBinding->obVarDataLen) = 0;
if (pCursorBinding->obInfo != CURSOR_DB_NOVALUE)
*(DWORD*)(pData + pCursorBinding->obInfo) = CURSOR_DB_NOINFO;
return cbVarData;
}
//=--------------------------------------------------------------------------=
// ReturnData_LPWSTR - Coerce LPWSTR data into buffers
//=--------------------------------------------------------------------------=
// This function coerces the specified data into supplied buffers
//
// Parameters:
// pwszData - [in] the string data
// pCursorBinding - [in] the cursor binding describing the format of the
// returned information
// pData - [in] a pointer to the fixed area buffer
// pVarData - [in] a pointer to the variable length buffer
//
// Output:
// ULONG - the number of bytes used in variable length buffer
//
// Notes:
//
ULONG CVDMetadataCursor::ReturnData_LPWSTR(WCHAR * pwszData, CURSOR_DBCOLUMNBINDING * pCursorBinding,
BYTE * pData, BYTE * pVarData)
{
ULONG cbVarData = 0;
ULONG cbLength = 0;
DWORD dwInfo = CURSOR_DB_NOINFO;
if (pCursorBinding->dwBinding == CURSOR_DBBINDING_DEFAULT)
{
if (pCursorBinding->dwDataType == CURSOR_DBTYPE_CHARS)
{
if (pwszData)
cbLength = GET_MBCSLEN_FROMWIDE(pwszData);
if (pCursorBinding->obData != CURSOR_DB_NOVALUE)
{
if (pwszData)
{
MAKE_MBCSPTR_FROMWIDE(pszData, pwszData);
memcpy(pData + pCursorBinding->obData, pszData, min(pCursorBinding->cbMaxLen, cbLength));
if (pCursorBinding->cbMaxLen < cbLength)
dwInfo = CURSOR_DB_TRUNCATED;
}
else
{
*(CHAR*)(pData + pCursorBinding->obData) = 0;
dwInfo = CURSOR_DB_NULL;
}
}
}
else if (pCursorBinding->dwDataType == CURSOR_DBTYPE_WCHARS)
{
if (pwszData)
cbLength = (lstrlenW(pwszData) + 1) * sizeof(WCHAR);
if (pCursorBinding->obData != CURSOR_DB_NOVALUE)
{
if (pwszData)
{
memcpy(pData + pCursorBinding->obData, pwszData, min(pCursorBinding->cbMaxLen, cbLength));
if (pCursorBinding->cbMaxLen < cbLength)
dwInfo = CURSOR_DB_TRUNCATED;
}
else
{
*(WCHAR*)(pData + pCursorBinding->obData) = 0;
dwInfo = CURSOR_DB_NULL;
}
}
}
else if (pCursorBinding->dwDataType == CURSOR_DBTYPE_LPSTR)
{
if (pwszData)
cbLength = GET_MBCSLEN_FROMWIDE(pwszData);
if (pCursorBinding->obData != CURSOR_DB_NOVALUE)
{
if (pwszData)
{
MAKE_MBCSPTR_FROMWIDE(pszData, pwszData);
*(LPSTR*)(pData + pCursorBinding->obData) = (LPSTR)pVarData;
if (pCursorBinding->cbMaxLen == CURSOR_DB_NOMAXLENGTH)
{
memcpy(pVarData, pszData, cbLength);
cbVarData = cbLength;
}
else
{
memcpy(pVarData, pszData, min(pCursorBinding->cbMaxLen, cbLength));
cbVarData = min(pCursorBinding->cbMaxLen, cbLength);
if (pCursorBinding->cbMaxLen < cbLength)
dwInfo = CURSOR_DB_TRUNCATED;
}
}
else
{
*(LPSTR*)(pData + pCursorBinding->obData) = NULL;
dwInfo = CURSOR_DB_NULL;
}
}
}
else if (pCursorBinding->dwDataType == CURSOR_DBTYPE_LPWSTR)
{
if (pwszData)
cbLength = (lstrlenW(pwszData) + 1) * sizeof(WCHAR);
if (pCursorBinding->obData != CURSOR_DB_NOVALUE)
{
if (pwszData)
{
*(LPWSTR*)(pData + pCursorBinding->obData) = (LPWSTR)pVarData;
if (pCursorBinding->cbMaxLen == CURSOR_DB_NOMAXLENGTH)
{
memcpy(pVarData, pwszData, cbLength);
cbVarData = cbLength;
}
else
{
memcpy(pVarData, pwszData, min(pCursorBinding->cbMaxLen, cbLength));
cbVarData = min(pCursorBinding->cbMaxLen, cbLength);
if (pCursorBinding->cbMaxLen < cbLength)
dwInfo = CURSOR_DB_TRUNCATED;
}
}
else
{
*(LPWSTR*)(pData + pCursorBinding->obData) = NULL;
dwInfo = CURSOR_DB_NULL;
}
}
}
}
else if (pCursorBinding->dwBinding == CURSOR_DBBINDING_VARIANT)
{
if (pCursorBinding->dwDataType == CURSOR_DBTYPE_LPSTR)
{
if (pwszData)
cbLength = GET_MBCSLEN_FROMWIDE(pwszData);
if (pCursorBinding->obData != CURSOR_DB_NOVALUE)
{
CURSOR_DBVARIANT * pVariant = (CURSOR_DBVARIANT*)(pData + pCursorBinding->obData);
VariantInit((VARIANT*)pVariant);
if (pwszData)
{
MAKE_MBCSPTR_FROMWIDE(pszData, pwszData);
pVariant->vt = CURSOR_DBTYPE_LPSTR;
pVariant->pszVal = (LPSTR)pVarData;
if (pCursorBinding->cbMaxLen == CURSOR_DB_NOMAXLENGTH)
{
memcpy(pVarData, pszData, cbLength);
cbVarData = cbLength;
}
else
{
memcpy(pVarData, pszData, min(pCursorBinding->cbMaxLen, cbLength));
cbVarData = min(pCursorBinding->cbMaxLen, cbLength);
if (pCursorBinding->cbMaxLen < cbLength)
dwInfo = CURSOR_DB_TRUNCATED;
}
}
else
{
pVariant->vt = VT_NULL;
dwInfo = CURSOR_DB_NULL;
}
}
}
else if (pCursorBinding->dwDataType == CURSOR_DBTYPE_LPWSTR)
{
if (pwszData)
cbLength = (lstrlenW(pwszData) + 1) * sizeof(WCHAR);
if (pCursorBinding->obData != CURSOR_DB_NOVALUE)
{
CURSOR_DBVARIANT * pVariant = (CURSOR_DBVARIANT*)(pData + pCursorBinding->obData);
VariantInit((VARIANT*)pVariant);
if (pwszData)
{
pVariant->vt = CURSOR_DBTYPE_LPWSTR;
pVariant->pwszVal = (LPWSTR)pVarData;
if (pCursorBinding->cbMaxLen == CURSOR_DB_NOMAXLENGTH)
{
memcpy(pVarData, pwszData, cbLength);
cbVarData = cbLength;
}
else
{
memcpy(pVarData, pwszData, min(pCursorBinding->cbMaxLen, cbLength));
cbVarData = min(pCursorBinding->cbMaxLen, cbLength);
if (pCursorBinding->cbMaxLen < cbLength)
dwInfo = CURSOR_DB_TRUNCATED;
}
}
else
{
pVariant->vt = VT_NULL;
dwInfo = CURSOR_DB_NULL;
}
}
}
else if (pCursorBinding->dwDataType == VT_BSTR)
{
if (pwszData)
cbLength = (lstrlenW(pwszData) + 1) * sizeof(WCHAR);
if (pCursorBinding->obData != CURSOR_DB_NOVALUE)
{
CURSOR_DBVARIANT * pVariant = (CURSOR_DBVARIANT*)(pData + pCursorBinding->obData);
VariantInit((VARIANT*)pVariant);
if (pwszData)
{
pVariant->vt = VT_BSTR;
pVariant->pwszVal = SysAllocString(pwszData);
}
else
{
pVariant->vt = VT_NULL;
dwInfo = CURSOR_DB_NULL;
}
}
}
}
if (pCursorBinding->obVarDataLen != CURSOR_DB_NOVALUE)
*(ULONG*)(pData + pCursorBinding->obVarDataLen) = cbLength;
if (pCursorBinding->obInfo != CURSOR_DB_NOVALUE)
*(DWORD*)(pData + pCursorBinding->obInfo) = dwInfo;
return cbVarData;
}
//=--------------------------------------------------------------------------=
// ReturnData_DBCOLUMNID - Coerce DBCOLUMNID data into buffers
//=--------------------------------------------------------------------------=
// This function coerces the specified data into supplied buffers
//
// Parameters:
// cursorColumnID - [in] the cursor column identifier
// pCursorBinding - [in] the cursor binding describing the format of the
// returned information
// pData - [in] a pointer to the fixed area buffer
// pVarData - [in] a pointer to the variable length buffer
//
// Output:
// ULONG - the number of bytes used in variable length buffer
//
// Notes:
//
ULONG CVDMetadataCursor::ReturnData_DBCOLUMNID(CURSOR_DBCOLUMNID cursorColumnID, CURSOR_DBCOLUMNBINDING * pCursorBinding,
BYTE * pData, BYTE * pVarData)
{
ULONG cbVarData = 0;
if (pCursorBinding->dwBinding == CURSOR_DBBINDING_DEFAULT)
{
if (pCursorBinding->obData != CURSOR_DB_NOVALUE)
*(CURSOR_DBCOLUMNID*)(pData + pCursorBinding->obData) = cursorColumnID;
}
else if (pCursorBinding->dwBinding == CURSOR_DBBINDING_VARIANT)
{
if (pCursorBinding->obData != CURSOR_DB_NOVALUE)
{
CURSOR_DBVARIANT * pVariant = (CURSOR_DBVARIANT*)(pData + pCursorBinding->obData);
CURSOR_DBCOLUMNID * pCursorColumnID = (CURSOR_DBCOLUMNID*)pVarData;
VariantInit((VARIANT*)pVariant);
pVariant->vt = CURSOR_DBTYPE_COLUMNID;
pVariant->pColumnid = pCursorColumnID;
*pCursorColumnID = cursorColumnID;
cbVarData = sizeof(CURSOR_DBCOLUMNID);
}
}
if (pCursorBinding->obVarDataLen != CURSOR_DB_NOVALUE)
*(DWORD*)(pData + pCursorBinding->obVarDataLen) = 0;
if (pCursorBinding->obInfo != CURSOR_DB_NOVALUE)
*(DWORD*)(pData + pCursorBinding->obInfo) = CURSOR_DB_NOINFO;
return cbVarData;
}
//=--------------------------------------------------------------------------=
// ReturnData_Bookmark - Coerce bookmark data into buffers
//=--------------------------------------------------------------------------=
// This function coerces the specified data into supplied buffers
//
// Parameters:
// lRow - [in] the current row
// pCursorBinding - [in] the cursor binding describing the format of the
// returned information
// pData - [in] a pointer to the fixed area buffer
// pVarData - [in] a pointer to the variable length buffer
//
// Output:
// ULONG - the number of bytes used in variable length buffer
//
// Notes:
//
ULONG CVDMetadataCursor::ReturnData_Bookmark(LONG lRow, CURSOR_DBCOLUMNBINDING * pCursorBinding,
BYTE * pData, BYTE * pVarData)
{
ULONG cbVarData = 0;
ULONG cbLength = sizeof(LONG);
DWORD dwInfo = CURSOR_DB_NOINFO;
if (pCursorBinding->dwBinding == CURSOR_DBBINDING_DEFAULT)
{
if (pCursorBinding->dwDataType == CURSOR_DBTYPE_BYTES)
{
if (pCursorBinding->obData != CURSOR_DB_NOVALUE)
{
memcpy(pData + pCursorBinding->obData, &lRow, min(pCursorBinding->cbMaxLen, cbLength));
if (pCursorBinding->cbMaxLen < cbLength)
dwInfo = CURSOR_DB_TRUNCATED;
}
}
else if (pCursorBinding->dwDataType == CURSOR_DBTYPE_BLOB)
{
if (pCursorBinding->obData != CURSOR_DB_NOVALUE)
{
*(ULONG*)(pData + pCursorBinding->obData) = cbLength;
*(LPBYTE*)(pData + pCursorBinding->obData + sizeof(ULONG)) = (LPBYTE)pVarData;
if (pCursorBinding->cbMaxLen == CURSOR_DB_NOMAXLENGTH)
{
memcpy((LPBYTE)pVarData, &lRow, cbLength);
cbVarData = cbLength;
}
else
{
memcpy(pVarData, &lRow, min(pCursorBinding->cbMaxLen, cbLength));
cbVarData = min(pCursorBinding->cbMaxLen, cbLength);
if (pCursorBinding->cbMaxLen < cbLength)
dwInfo = CURSOR_DB_TRUNCATED;
}
}
}
}
else if (pCursorBinding->dwBinding == CURSOR_DBBINDING_VARIANT)
{
if (pCursorBinding->dwDataType == CURSOR_DBTYPE_BLOB)
{
if (pCursorBinding->obData != CURSOR_DB_NOVALUE)
{
CURSOR_DBVARIANT * pVariant = (CURSOR_DBVARIANT*)(pData + pCursorBinding->obData);
VariantInit((VARIANT*)pVariant);
pVariant->vt = CURSOR_DBTYPE_BLOB;
pVariant->blob.cbSize = cbLength;
pVariant->blob.pBlobData = (LPBYTE)pVarData;
if (pCursorBinding->cbMaxLen == CURSOR_DB_NOMAXLENGTH)
{
memcpy((LPBYTE)pVarData, &lRow, cbLength);
cbVarData = cbLength;
}
else
{
memcpy(pVarData, &lRow, min(pCursorBinding->cbMaxLen, cbLength));
cbVarData = min(pCursorBinding->cbMaxLen, cbLength);
if (pCursorBinding->cbMaxLen < cbLength)
dwInfo = CURSOR_DB_TRUNCATED;
}
}
}
}
if (pCursorBinding->obVarDataLen != CURSOR_DB_NOVALUE)
*(ULONG*)(pData + pCursorBinding->obVarDataLen) = cbLength;
if (pCursorBinding->obInfo != CURSOR_DB_NOVALUE)
*(DWORD*)(pData + pCursorBinding->obInfo) = dwInfo;
return cbVarData;
}
//=--------------------------------------------------------------------------=
// Create - Create metadata cursor object
//=--------------------------------------------------------------------------=
// This function creates and initializes a new metadata cursor object
//
// Parameters:
// ulColumns - [in] the number of rowset columns
// pColumns - [in] a pointer to rowset columns where to
// retrieve metadata
// ulMetaColumns - [in] the number of rowset meta-columns (can be 0)
// pMetaColumns - [in] a pointer to rowset meta-columns where to
// retrieve metadata (can be NULL)
// ppMetaDataCursor - [out] a pointer in which to return pointer to
// metadata cursor object
// pResourceDLL - [in] a pointer which keeps track of resource DLL
//
// Output:
// HRESULT - S_OK if successful
// E_INVALIDARG bad parameter
// E_OUTOFMEMORY not enough memory to create object
//
// Notes:
//
HRESULT CVDMetadataCursor::Create(ULONG ulColumns, CVDRowsetColumn * pColumns, ULONG ulMetaColumns,
CVDRowsetColumn * pMetaColumns, CVDMetadataCursor ** ppMetadataCursor, CVDResourceDLL * pResourceDLL)
{
ASSERT_POINTER(pColumns, CVDRowsetColumn)
ASSERT_NULL_OR_POINTER(pMetaColumns, CVDRowsetColumn)
ASSERT_POINTER(ppMetadataCursor, CVDMetadataCursor*)
ASSERT_POINTER(pResourceDLL, CVDResourceDLL)
if (!ppMetadataCursor || !pColumns)
return E_INVALIDARG;
*ppMetadataCursor = NULL;
CVDMetadataCursor * pMetadataCursor = new CVDMetadataCursor();
if (!pMetadataCursor)
return E_OUTOFMEMORY;
pMetadataCursor->m_ulColumns = ulColumns;
pMetadataCursor->m_pColumns = pColumns;
pMetadataCursor->m_ulMetaColumns = ulMetaColumns;
pMetadataCursor->m_pMetaColumns = pMetaColumns;
pMetadataCursor->m_pResourceDLL = pResourceDLL;
*ppMetadataCursor = pMetadataCursor;
return S_OK;
}
//=--------------------------------------------------------------------------=
// IUnknown methods implemented
//=--------------------------------------------------------------------------=
//=--------------------------------------------------------------------------=
// IUnknown QueryInterface
//
HRESULT CVDMetadataCursor::QueryInterface(REFIID riid, void **ppvObjOut)
{
ASSERT_POINTER(ppvObjOut, IUnknown*)
if (!ppvObjOut)
return E_INVALIDARG;
*ppvObjOut = NULL;
switch (riid.Data1)
{
QI_INTERFACE_SUPPORTED((ICursor*)this, IUnknown);
QI_INTERFACE_SUPPORTED(this, ICursor);
QI_INTERFACE_SUPPORTED(this, ICursorMove);
QI_INTERFACE_SUPPORTED(this, ICursorScroll);
QI_INTERFACE_SUPPORTED(this, ISupportErrorInfo);
}
if (NULL == *ppvObjOut)
return E_NOINTERFACE;
AddRef();
return S_OK;
}
//=--------------------------------------------------------------------------=
// IUnknown AddRef
//
ULONG CVDMetadataCursor::AddRef(void)
{
return ++m_dwRefCount;
}
//=--------------------------------------------------------------------------=
// IUnknown Release
//
ULONG CVDMetadataCursor::Release(void)
{
if (1 > --m_dwRefCount)
{
delete this;
return 0;
}
return m_dwRefCount;
}
//=--------------------------------------------------------------------------=
// 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:
// This function only succeeds when creating a meta-metadata cursor.
//
HRESULT CVDMetadataCursor::GetColumnsCursor(REFIID riid, IUnknown **ppvColumnsCursor, ULONG *pcRows)
{
ASSERT_POINTER(ppvColumnsCursor, IUnknown*)
ASSERT_NULL_OR_POINTER(pcRows, ULONG)
if (!ppvColumnsCursor)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursor, m_pResourceDLL);
return E_INVALIDARG;
}
// init out parameters
*ppvColumnsCursor = NULL;
if (pcRows)
*pcRows = 0;
if (!m_ulMetaColumns) // can't create meta-meta-metadata cursor
{
VDSetErrorInfo(IDS_ERR_CANTCREATEMETACURSOR, IID_ICursor, m_pResourceDLL);
return E_FAIL;
}
// 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 meta-metadata cursor
CVDMetadataCursor * pMetadataCursor;
HRESULT hr = CVDMetadataCursor::Create(m_ulMetaColumns, m_pMetaColumns, 0, 0, &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 = m_ulMetaColumns;
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 CVDMetadataCursor::SetBindings(ULONG cCol, CURSOR_DBCOLUMNBINDING rgBoundColumns[], ULONG cbRowLength, DWORD dwFlags)
{
ASSERT_NULL_OR_POINTER(rgBoundColumns, CURSOR_DBCOLUMNBINDING)
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 = m_ulMetaColumns;
CVDRowsetColumn * pColumns = m_pMetaColumns;
if (!pColumns)
{
ulColumns = m_ulColumns;
pColumns = m_pColumns;
}
ULONG cbNewRowLength;
ULONG cbNewVarRowLength;
HRESULT hr = ValidateCursorBindings(ulColumns, pColumns, cCol, rgBoundColumns, cbRowLength, dwFlags,
&cbNewRowLength, &cbNewVarRowLength);
if (SUCCEEDED(hr)) // if so, then set them 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;
}
}
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
// CURSOR_DB_S_ENDOFCURSOR reached end of the cursor
//
// Notes:
//
HRESULT CVDMetadataCursor::GetNextRows(LARGE_INTEGER udlRowsToSkip, CURSOR_DBFETCHROWS *pFetchParams)
{
ASSERT_NULL_OR_POINTER(pFetchParams, CURSOR_DBFETCHROWS)
// return if caller doesn't supply fetch rows structure
if (!pFetchParams)
return S_OK;
// init out parameter
pFetchParams->cRowsReturned = 0;
// return if caller didn't ask for any rows
if (!pFetchParams->cRowsRequested)
return S_OK;
// make sure fetch flags has only valid values
if (pFetchParams->dwFlags != CURSOR_DBROWFETCH_DEFAULT &&
pFetchParams->dwFlags != CURSOR_DBROWFETCH_CALLEEALLOCATES &&
pFetchParams->dwFlags != CURSOR_DBROWFETCH_FORCEREFRESH &&
pFetchParams->dwFlags != (CURSOR_DBROWFETCH_CALLEEALLOCATES | CURSOR_DBROWFETCH_FORCEREFRESH))
return CURSOR_DB_E_BADFETCHINFO;
// if memory was caller allocated, make sure caller supplied data pointer
if (!(pFetchParams->dwFlags & CURSOR_DBROWFETCH_CALLEEALLOCATES) && !pFetchParams->pData)
return CURSOR_DB_E_BADFETCHINFO;
// if memory was caller allocated, make sure caller supplied var-data pointer and size if needed
if (!(pFetchParams->dwFlags & CURSOR_DBROWFETCH_CALLEEALLOCATES) && m_fNeedVarData &&
(!pFetchParams->pVarData || !pFetchParams->cbVarData))
return CURSOR_DB_E_BADFETCHINFO;
// allocate necessary memory
if (pFetchParams->dwFlags & CURSOR_DBROWFETCH_CALLEEALLOCATES)
{
// inline memory
pFetchParams->pData = g_pMalloc->Alloc(pFetchParams->cRowsRequested * m_cbRowLength);
if (!pFetchParams->pData)
{
VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL);
return E_OUTOFMEMORY;
}
if (m_fNeedVarData)
{
// out-of-line memory
pFetchParams->pVarData = g_pMalloc->Alloc(pFetchParams->cRowsRequested * m_cbVarRowLength);
if (!pFetchParams->pData)
{
g_pMalloc->Free(pFetchParams->pData);
pFetchParams->pData = NULL;
VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL);
return E_OUTOFMEMORY;
}
}
else
pFetchParams->pVarData = NULL;
}
// fetch data
HRESULT hrFetch = S_OK;
CVDRowsetColumn * pColumn;
CURSOR_DBCOLUMNID cursorColumnID;
CURSOR_DBCOLUMNBINDING * pCursorBinding;
BYTE * pData = (BYTE*)pFetchParams->pData;
BYTE * pVarData = (BYTE*)pFetchParams->pVarData;
// iterate through rows
for (ULONG ulRow = 0; ulRow < pFetchParams->cRowsRequested; ulRow++)
{
// increment row
m_lCurrentRow++;
// make sure we didn't hit end of table
if (m_lCurrentRow >= (LONG)m_ulColumns)
{
m_lCurrentRow = (LONG)m_ulColumns;
hrFetch = CURSOR_DB_S_ENDOFCURSOR;
goto DoneFetchingMetaData;
}
pCursorBinding = m_pCursorBindings;
pColumn = &m_pColumns[m_lCurrentRow];
// iterate through bindings
for (ULONG ulBind = 0; ulBind < m_ulCursorBindings; ulBind++)
{
cursorColumnID = pCursorBinding->columnID;
// return requested data
if (IsEqualCursorColumnID(cursorColumnID, CURSOR_COLUMN_BINDTYPE))
{
pVarData += ReturnData_I4(pColumn->GetBindType(), pCursorBinding, pData, pVarData);
}
else if (IsEqualCursorColumnID(cursorColumnID, CURSOR_COLUMN_COLUMNID))
{
pVarData += ReturnData_DBCOLUMNID(pColumn->GetCursorColumnID(), pCursorBinding, pData, pVarData);
}
else if (IsEqualCursorColumnID(cursorColumnID, CURSOR_COLUMN_DATACOLUMN))
{
pVarData += ReturnData_BOOL(pColumn->GetDataColumn(), pCursorBinding, pData, pVarData);
}
else if (IsEqualCursorColumnID(cursorColumnID, CURSOR_COLUMN_ENTRYIDMAXLENGTH))
{
pVarData += ReturnData_I4(pColumn->GetEntryIDMaxLength(), pCursorBinding, pData, pVarData);
}
else if (IsEqualCursorColumnID(cursorColumnID, CURSOR_COLUMN_FIXED))
{
pVarData += ReturnData_BOOL(pColumn->GetFixed(), pCursorBinding, pData, pVarData);
}
else if (IsEqualCursorColumnID(cursorColumnID, CURSOR_COLUMN_MAXLENGTH))
{
pVarData += ReturnData_I4(pColumn->GetMaxLength(), pCursorBinding, pData, pVarData);
}
else if (IsEqualCursorColumnID(cursorColumnID, CURSOR_COLUMN_NAME))
{
pVarData += ReturnData_LPWSTR(pColumn->GetName(), pCursorBinding, pData, pVarData);
}
else if (IsEqualCursorColumnID(cursorColumnID, CURSOR_COLUMN_NUMBER))
{
pVarData += ReturnData_I4(pColumn->GetNumber(), pCursorBinding, pData, pVarData);
}
else if (IsEqualCursorColumnID(cursorColumnID, CURSOR_COLUMN_NULLABLE))
{
pVarData += ReturnData_BOOL(pColumn->GetNullable(), pCursorBinding, pData, pVarData);
}
else if (IsEqualCursorColumnID(cursorColumnID, CURSOR_COLUMN_SCALE))
{
pVarData += ReturnData_I4(pColumn->GetScale(), pCursorBinding, pData, pVarData);
}
else if (IsEqualCursorColumnID(cursorColumnID, CURSOR_COLUMN_TYPE))
{
pVarData += ReturnData_I4(pColumn->GetCursorType(), pCursorBinding, pData, pVarData);
}
else if (IsEqualCursorColumnID(cursorColumnID, CURSOR_COLUMN_UPDATABLE))
{
pVarData += ReturnData_I4(pColumn->GetUpdatable(), pCursorBinding, pData, pVarData);
}
else if (IsEqualCursorColumnID(cursorColumnID, CURSOR_COLUMN_UNIQUE))
{
pVarData += ReturnData_BOOL(pColumn->GetUnique(), pCursorBinding, pData, pVarData);
}
else if (IsEqualCursorColumnID(cursorColumnID, CURSOR_COLUMN_CASESENSITIVE))
{
pVarData += ReturnData_BOOL(pColumn->GetCaseSensitive(), pCursorBinding, pData, pVarData);
}
else if (IsEqualCursorColumnID(cursorColumnID, CURSOR_COLUMN_AUTOINCREMENT))
{
pVarData += ReturnData_BOOL(pColumn->GetAutoIncrement(), pCursorBinding, pData, pVarData);
}
else if (IsEqualCursorColumnID(cursorColumnID, CURSOR_COLUMN_HASDEFAULT))
{
pVarData += ReturnData_BOOL(pColumn->GetHasDefault(), pCursorBinding, pData, pVarData);
}
else if (IsEqualCursorColumnID(cursorColumnID, CURSOR_COLUMN_COLLATINGORDER))
{
pVarData += ReturnData_I4(pColumn->GetCollatingOrder(), pCursorBinding, pData, pVarData);
}
else if (IsEqualCursorColumnID(cursorColumnID, CURSOR_COLUMN_BASENAME))
{
pVarData += ReturnData_LPWSTR(pColumn->GetBaseName(), pCursorBinding, pData, pVarData);
}
else if (IsEqualCursorColumnID(cursorColumnID, CURSOR_COLUMN_BASECOLUMNNAME))
{
pVarData += ReturnData_LPWSTR(pColumn->GetBaseColumnName(), pCursorBinding, pData, pVarData);
}
else if (IsEqualCursorColumnID(cursorColumnID, CURSOR_COLUMN_DEFAULTVALUE))
{
pVarData += ReturnData_LPWSTR(pColumn->GetDefaultValue(), pCursorBinding, pData, pVarData);
}
else if (IsEqualCursorColumnID(cursorColumnID, CURSOR_COLUMN_BMKTEMPORARY))
{
pVarData += ReturnData_Bookmark(m_lCurrentRow, pCursorBinding, pData, pVarData);
}
pCursorBinding++;
}
// increment returned row count
pFetchParams->cRowsReturned++;
pData += m_cbRowLength;
}
DoneFetchingMetaData:
// 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 CVDMetadataCursor::Requery(void)
{
m_lCurrentRow = -1;
return S_OK;
}
//=--------------------------------------------------------------------------=
// 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 CVDMetadataCursor::Move(ULONG cbBookmark, void *pBookmark, LARGE_INTEGER dlOffset, CURSOR_DBFETCHROWS *pFetchParams)
{
ASSERT_POINTER(pBookmark, BYTE)
ASSERT_NULL_OR_POINTER(pFetchParams, CURSOR_DBFETCHROWS)
if (!cbBookmark || !pBookmark)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursor, m_pResourceDLL);
return E_INVALIDARG;
}
if (!BookmarkToRow(cbBookmark, pBookmark, &m_lCurrentRow))
{
VDSetErrorInfo(IDS_ERR_BADBOOKMARK, IID_ICursor, m_pResourceDLL);
return CURSOR_DB_E_BADBOOKMARK;
}
m_lCurrentRow += (LONG)dlOffset.LowPart;
if (m_lCurrentRow < -1)
{
m_lCurrentRow = -1;
return CURSOR_DB_S_ENDOFCURSOR;
}
else
if (m_lCurrentRow >= (LONG)m_ulColumns)
{
m_lCurrentRow = (LONG)m_ulColumns;
return CURSOR_DB_S_ENDOFCURSOR;
}
if (!pFetchParams)
return S_OK;
// since get next rows starts from the row after the current row we must
// back up one row
m_lCurrentRow--;
if (m_lCurrentRow < -1)
m_lCurrentRow = -1;
return CVDMetadataCursor::GetNextRows(g_liZero, pFetchParams);
}
//=--------------------------------------------------------------------------=
// 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 CVDMetadataCursor::GetBookmark(CURSOR_DBCOLUMNID *pBookmarkType,
ULONG cbMaxSize,
ULONG *pcbBookmark,
void *pBookmark)
{
ASSERT_POINTER(pBookmarkType, CURSOR_DBCOLUMNID)
ASSERT_POINTER(pcbBookmark, ULONG)
ASSERT_POINTER(pBookmark, BYTE)
if (!pBookmarkType || !pcbBookmark || !pBookmark)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursor, m_pResourceDLL);
return E_INVALIDARG;
}
if (cbMaxSize < sizeof(LONG))
{
VDSetErrorInfo(IDS_ERR_BUFFERTOOSMALL, IID_ICursor, m_pResourceDLL);
return CURSOR_DB_E_BUFFERTOOSMALL;
}
RowToBookmark(m_lCurrentRow, pcbBookmark, pBookmark);
return S_OK;
}
//=--------------------------------------------------------------------------=
// 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 CVDMetadataCursor::Clone(DWORD dwFlags, REFIID riid, IUnknown **ppvClonedCursor)
{
CVDMetadataCursor * pMetaCursor = 0;
HRESULT hr = CVDMetadataCursor::Create(m_ulColumns,
m_pColumns,
m_ulMetaColumns,
m_pMetaColumns,
&pMetaCursor,
m_pResourceDLL);
*ppvClonedCursor = (ICursor*)pMetaCursor;
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 CVDMetadataCursor::Scroll(ULONG ulNumerator, ULONG ulDenominator, CURSOR_DBFETCHROWS *pFetchParams)
{
ASSERT_NULL_OR_POINTER(pFetchParams, CURSOR_DBFETCHROWS)
if (!ulDenominator) // division by zero is a bad thing!
{
// this is a Viaduct1 error message, which doesn't really apply
VDSetErrorInfo(IDS_ERR_BADFRACTION, IID_ICursor, m_pResourceDLL);
return CURSOR_DB_E_BADFRACTION;
}
m_lCurrentRow = (LONG)((ulNumerator * m_ulColumns) / ulDenominator);
if (m_lCurrentRow >= (LONG)m_ulColumns)
m_lCurrentRow = (LONG)m_ulColumns - 1;
if (!pFetchParams)
return S_OK;
// since get next rows starts from the row after the current row we must
// back up one row
m_lCurrentRow--;
if (m_lCurrentRow < -1)
m_lCurrentRow = -1;
return CVDMetadataCursor::GetNextRows(g_liZero, pFetchParams);
}
//=--------------------------------------------------------------------------=
// 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 CVDMetadataCursor::GetApproximatePosition(ULONG cbBookmark, void *pBookmark, ULONG *pulNumerator, ULONG *pulDenominator)
{
ASSERT_POINTER(pBookmark, BYTE)
ASSERT_POINTER(pulNumerator, ULONG)
ASSERT_POINTER(pulDenominator, ULONG)
if (!pBookmark || !pulNumerator || !pulDenominator)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursor, m_pResourceDLL);
return E_INVALIDARG;
}
LONG lRow;
if (!BookmarkToRow(cbBookmark, pBookmark, &lRow))
{
VDSetErrorInfo(IDS_ERR_BADBOOKMARK, IID_ICursor, m_pResourceDLL);
return CURSOR_DB_E_BADBOOKMARK;
}
*pulNumerator = lRow + 1;
*pulDenominator = m_ulColumns ? m_ulColumns : 1;
return S_OK;
}
//=--------------------------------------------------------------------------=
// 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 CVDMetadataCursor::GetApproximateCount(LARGE_INTEGER *pudlApproxCount, DWORD *pdwFullyPopulated)
{
ASSERT_POINTER(pudlApproxCount, LARGE_INTEGER)
ASSERT_NULL_OR_POINTER(pdwFullyPopulated, DWORD)
if (!pudlApproxCount)
{
VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursor, m_pResourceDLL);
return E_INVALIDARG;
}
pudlApproxCount->HighPart = 0;
pudlApproxCount->LowPart = m_ulColumns;
if (pdwFullyPopulated)
*pdwFullyPopulated = CURSOR_DBCURSORPOPULATED_FULLY;
return S_OK;
}