//---------------------------------------------------------------------------
// CursorBase.cpp : CursorBase 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 "fastguid.h"
#include "resource.h"

SZTHISFILE


//=--------------------------------------------------------------------------=
// CVDCursorBase - Constructor
//
CVDCursorBase::CVDCursorBase()
{
    m_ulCursorBindings  = 0;
    m_pCursorBindings   = NULL;
    m_fNeedVarData      = FALSE;
    m_cbRowLength       = 0;
    m_cbVarRowLength    = 0;

#ifdef _DEBUG
    g_cVDCursorBaseCreated++;
#endif         
}

//=--------------------------------------------------------------------------=
// ~CVDCursorBase - Destructor
//
CVDCursorBase::~CVDCursorBase()
{
    DestroyCursorBindings(&m_pCursorBindings, &m_ulCursorBindings);

#ifdef _DEBUG
    g_cVDCursorBaseDestroyed++;
#endif         
}

//=--------------------------------------------------------------------------=
// DestroyCursorBindings - Destroy cursor bindings and column identifer names
//
void CVDCursorBase::DestroyCursorBindings(CURSOR_DBCOLUMNBINDING** ppCursorBindings,
											ULONG* pcBindings)
{
    for (ULONG ulBind = 0; ulBind < *pcBindings; ulBind++)
    {
        CURSOR_DBCOLUMNID * pCursorColumnID = &(*ppCursorBindings)[ulBind].columnID;

        if (pCursorColumnID->dwKind == CURSOR_DBCOLKIND_GUID_NAME || pCursorColumnID->dwKind == CURSOR_DBCOLKIND_NAME)
            delete [] pCursorColumnID->lpdbsz;
    }

    delete [] *ppCursorBindings;

    *ppCursorBindings = NULL;
    *pcBindings = 0;
}

//=--------------------------------------------------------------------------=
// IsValidCursorType - Return TRUE if specified cursor data type is valid
//
BOOL CVDCursorBase::IsValidCursorType(DWORD dwCursorType)
{
    BOOL fValid = FALSE;

    switch (dwCursorType)
    {
        case CURSOR_DBTYPE_I2:
        case CURSOR_DBTYPE_I4:
        case CURSOR_DBTYPE_I8:
        case CURSOR_DBTYPE_R4:
        case CURSOR_DBTYPE_R8:
        case CURSOR_DBTYPE_CY:
        case CURSOR_DBTYPE_DATE:
        case CURSOR_DBTYPE_FILETIME:
        case CURSOR_DBTYPE_BOOL:
        case CURSOR_DBTYPE_LPSTR:
        case CURSOR_DBTYPE_LPWSTR:
        case CURSOR_DBTYPE_BLOB:
        case CURSOR_DBTYPE_UI2:
        case CURSOR_DBTYPE_UI4:
        case CURSOR_DBTYPE_UI8:
        case CURSOR_DBTYPE_COLUMNID:
        case CURSOR_DBTYPE_BYTES:
        case CURSOR_DBTYPE_CHARS:
        case CURSOR_DBTYPE_WCHARS:
        case CURSOR_DBTYPE_ANYVARIANT:
        case VT_VARIANT:
        case VT_BSTR:
        case VT_UI1:
        case VT_I1:
            fValid = TRUE;
            break;
    }

    return fValid;
}

//=--------------------------------------------------------------------------=
// DoesCursorTypeNeedVarData - Return TRUE if specified cursor type needs 
//                             variable length buffer
//
BOOL CVDCursorBase::DoesCursorTypeNeedVarData(DWORD dwCursorType)
{
    BOOL fNeedsVarData = FALSE;

    switch (dwCursorType)
    {
        case CURSOR_DBTYPE_BLOB:
        case CURSOR_DBTYPE_LPSTR:
        case CURSOR_DBTYPE_LPWSTR:
            fNeedsVarData = TRUE;
            break;
    }

    return fNeedsVarData;
}

//=--------------------------------------------------------------------------=
// GetCursorTypeLength - Get the size in bytes required by cursor data type
//
ULONG CVDCursorBase::GetCursorTypeLength(DWORD dwCursorType, ULONG cbMaxLen)
{
    ULONG cbRequired = 0;

    switch (dwCursorType)
    {
        case CURSOR_DBTYPE_I2:
        case CURSOR_DBTYPE_UI2:
            cbRequired = sizeof(short);
            break;

        case CURSOR_DBTYPE_I4:
        case CURSOR_DBTYPE_UI4:
            cbRequired = sizeof(long);
            break;

        case CURSOR_DBTYPE_I8:
        case CURSOR_DBTYPE_UI8:
            cbRequired = sizeof(LARGE_INTEGER);
            break;

        case CURSOR_DBTYPE_R4:
            cbRequired = sizeof(float);
            break;

        case CURSOR_DBTYPE_R8:
            cbRequired = sizeof(double);
            break;

        case CURSOR_DBTYPE_CY:
            cbRequired = sizeof(CY);
            break;

        case CURSOR_DBTYPE_DATE:
            cbRequired = sizeof(DATE);
            break;

        case CURSOR_DBTYPE_FILETIME:
            cbRequired = sizeof(FILETIME);
            break;

        case CURSOR_DBTYPE_BOOL:
            cbRequired = sizeof(VARIANT_BOOL);
            break;

        case CURSOR_DBTYPE_LPSTR:
            cbRequired = sizeof(LPSTR);
            break;

        case CURSOR_DBTYPE_LPWSTR:
            cbRequired = sizeof(LPWSTR);
            break;

        case CURSOR_DBTYPE_BLOB:
            cbRequired = sizeof(BLOB);
            break;

        case CURSOR_DBTYPE_COLUMNID:
            cbRequired = sizeof(CURSOR_DBCOLUMNID);
            break;

        case CURSOR_DBTYPE_BYTES:
            cbRequired = cbMaxLen;
            break;

        case CURSOR_DBTYPE_CHARS:
            cbRequired = cbMaxLen;
            break;

        case CURSOR_DBTYPE_WCHARS:
            cbRequired = cbMaxLen;
            break;

        case CURSOR_DBTYPE_ANYVARIANT:
            cbRequired = sizeof(CURSOR_DBVARIANT);
            break;

        case VT_VARIANT:
            cbRequired = sizeof(VARIANT);
            break;

        case VT_I1:
        case VT_UI1:
            cbRequired = sizeof(BYTE);
            break;
    }

    return cbRequired;
}

//=--------------------------------------------------------------------------=
// IsEqualCursorColumnID - Return TRUE if cursor column identifier are the same
//
BOOL CVDCursorBase::IsEqualCursorColumnID(const CURSOR_DBCOLUMNID& cursorColumnID1, const CURSOR_DBCOLUMNID& cursorColumnID2)
{
    // first check to see if column identifers are the same kind
    if (cursorColumnID1.dwKind != cursorColumnID1.dwKind)
        return FALSE;

    // then, check to see if they are equal
    BOOL bResult = TRUE;

    switch (cursorColumnID1.dwKind)
    {
	    case CURSOR_DBCOLKIND_GUID_NAME:
            if (!IsEqualGUID(cursorColumnID1.guid, cursorColumnID2.guid))
                bResult = FALSE;
            else if (lstrcmpW(cursorColumnID1.lpdbsz, cursorColumnID2.lpdbsz))
                bResult = FALSE;
            break;
            
	    case CURSOR_DBCOLKIND_GUID_NUMBER:
            if (!IsEqualGUID(cursorColumnID1.guid, cursorColumnID2.guid))
                bResult = FALSE;
            else if (cursorColumnID1.lNumber != cursorColumnID2.lNumber)
                bResult = FALSE;
            break;

        case CURSOR_DBCOLKIND_NAME:
            if (lstrcmpW(cursorColumnID1.lpdbsz, cursorColumnID2.lpdbsz))
                bResult = FALSE;
            break;
    }

    return bResult;
}

//=--------------------------------------------------------------------------=
// GetCursorColumnIDNameLength - Get the size in bytes of possible name attached
//                               to the specified cursor column identifier
//
ULONG CVDCursorBase::GetCursorColumnIDNameLength(const CURSOR_DBCOLUMNID& cursorColumnID)
{
    ULONG cbName = 0;

    if (cursorColumnID.dwKind == CURSOR_DBCOLKIND_GUID_NAME || cursorColumnID.dwKind == CURSOR_DBCOLKIND_NAME)
        cbName = (lstrlenW(cursorColumnID.lpdbsz) + 1) * sizeof(WCHAR);

    return cbName;
}

//=--------------------------------------------------------------------------=
// ValidateCursorBindings - Validate cursor column bindings
//=--------------------------------------------------------------------------=
// This function makes sure the specified column bindings are acceptable
//
// Parameters:
//    ulColumns             - [in]  the number available columns
//    pColumns              - [in]  an array of available columns
//    ulBindings            - [in]  the number of cursor column bindings
//    pCursorBindings       - [in]  an array of cursor column bindings
//    cbRequestedRowLength  - [in]  the requested 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
//    pcbNewRowLength       - [out] a pointer to memory in which to return
//                                  the new number of bytes of inline memory
//                                  in a single row of data for all bindings
//    pcbNewRowLength       - [out] a pointer to memory in which to return
//                                  the new number of bytes of out-of-line memory
//                                  in a single row of data for all bindings
//
// Output:
//    HRESULT - S_OK if successful
//              CURSOR_DB_E_BADBINDINFO bad binding information
//              CURSOR_DB_E_COLUMNUNAVAILABLE columnID is not available
//              CURSOR_DB_E_ROWTOOSHORT cbRequestedRowLength was less than the minumum (and not zero)
//
// Notes:
//    This function also computes and returns the new fixed and variable buffer row length required
//    by all the cursor bindings.
//
HRESULT CVDCursorBase::ValidateCursorBindings(ULONG ulColumns, 
											  CVDRowsetColumn * pColumns, 
											  ULONG ulBindings, 
											  CURSOR_DBCOLUMNBINDING * pCursorBindings, 
											  ULONG cbRequestedRowLength, 
											  DWORD dwFlags,
                                              ULONG * pcbNewRowLength,
                                              ULONG * pcbNewVarRowLength)
{
    DWORD cbMaxLength;
    DWORD dwCursorType;
    BOOL fColumnIDAvailable;

    CVDRowsetColumn * pColumn;

    ULONG cbRequiredRowLength = 0;
    ULONG cbRequiredVarRowLength = 0;

    CURSOR_DBCOLUMNBINDING * pBinding = pCursorBindings;

    // iterate through bindings
    for (ULONG ulBind = 0; ulBind < ulBindings; ulBind++)
    {
        // make sure column identifier is available
        fColumnIDAvailable = FALSE;

        pColumn = pColumns;

        for (ULONG ulCol = 0; ulCol < ulColumns && !fColumnIDAvailable; ulCol++)
        {
            if (IsEqualCursorColumnID(pBinding->columnID, pColumn->GetCursorColumnID()))
            {
                cbMaxLength = pColumn->GetMaxLength();
                dwCursorType = pColumn->GetCursorType();
                fColumnIDAvailable = TRUE;
            }

            pColumn++;
        }

        if (!fColumnIDAvailable)
        {
            VDSetErrorInfo(IDS_ERR_COLUMNUNAVAILABLE, IID_ICursor, m_pResourceDLL);
            return CURSOR_DB_E_COLUMNUNAVAILABLE;
        }

        // 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 (pBinding->cbMaxLen == CURSOR_DB_NOMAXLENGTH && 
            pBinding->dwBinding == CURSOR_DBBINDING_DEFAULT)
        {
            if (pBinding->dwDataType == CURSOR_DBTYPE_CHARS || 
                pBinding->dwDataType == CURSOR_DBTYPE_WCHARS || 
                pBinding->dwDataType == CURSOR_DBTYPE_BYTES)
            {
                VDSetErrorInfo(IDS_ERR_BADCURSORBINDINFO, IID_ICursor, m_pResourceDLL);
                return CURSOR_DB_E_BADBINDINFO;
            }
        }

        // check binding bit mask for possible values
        if (pBinding->dwBinding != CURSOR_DBBINDING_DEFAULT && 
            pBinding->dwBinding != CURSOR_DBBINDING_VARIANT &&
            pBinding->dwBinding != CURSOR_DBBINDING_ENTRYID && 
            pBinding->dwBinding != (CURSOR_DBBINDING_VARIANT | CURSOR_DBBINDING_ENTRYID))
        {
            VDSetErrorInfo(IDS_ERR_BADCURSORBINDINFO, IID_ICursor, m_pResourceDLL);
            return CURSOR_DB_E_BADBINDINFO;
        }

        // check for valid cursor type
        if (!IsValidCursorType(pBinding->dwDataType))
        {
            VDSetErrorInfo(IDS_ERR_BADCURSORBINDINFO, IID_ICursor, 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 (pBinding->dwBinding & CURSOR_DBBINDING_VARIANT)
        {
            if (pBinding->dwDataType == CURSOR_DBTYPE_CHARS || 
                pBinding->dwDataType == CURSOR_DBTYPE_WCHARS || 
                pBinding->dwDataType == CURSOR_DBTYPE_BYTES)
            {
                VDSetErrorInfo(IDS_ERR_BADCURSORBINDINFO, IID_ICursor, m_pResourceDLL);
                return CURSOR_DB_E_BADBINDINFO;
            }
        }

        // if its not a variant binding make sure the cursor type is not CURSOR_DBTYPE_ANYVARIANT
        if (!(pBinding->dwBinding & CURSOR_DBBINDING_VARIANT) && pBinding->dwDataType == CURSOR_DBTYPE_ANYVARIANT)
        {
            VDSetErrorInfo(IDS_ERR_BADCURSORBINDINFO, IID_ICursor, m_pResourceDLL);
            return CURSOR_DB_E_BADBINDINFO;
        }

        // calulate row length required by data field
        if (!(pBinding->dwBinding & CURSOR_DBBINDING_VARIANT))
            cbRequiredRowLength += GetCursorTypeLength(pBinding->dwDataType, pBinding->cbMaxLen);
        else
            cbRequiredRowLength += sizeof(CURSOR_DBVARIANT);

        // calulate row length required by variable data length field
        if (pBinding->obVarDataLen != CURSOR_DB_NOVALUE)
            cbRequiredRowLength += sizeof(ULONG);

        // calulate row length required by information field
        if (pBinding->obInfo != CURSOR_DB_NOVALUE)
            cbRequiredRowLength += sizeof(DWORD);

        // calulate variable row length required by data field
        if (!(pBinding->dwBinding & CURSOR_DBBINDING_VARIANT))
        {
            if (DoesCursorTypeNeedVarData(pBinding->dwDataType))
            {
                if (pBinding->cbMaxLen != CURSOR_DB_NOMAXLENGTH)
                    cbRequiredVarRowLength += pBinding->cbMaxLen;
                else
                    cbRequiredVarRowLength += cbMaxLength;
            }
        }
        else    // variant binding
        {
            if (DoesCursorTypeNeedVarData(pBinding->dwDataType))
            {
                if (pBinding->cbMaxLen != CURSOR_DB_NOMAXLENGTH)
                    cbRequiredVarRowLength += pBinding->cbMaxLen;
                else
                    cbRequiredVarRowLength += cbMaxLength;
            }

            if (pBinding->dwDataType == CURSOR_DBTYPE_COLUMNID)
                cbRequiredVarRowLength += sizeof(CURSOR_DBCOLUMNID);
        }

        pBinding++;
    }

    // if we're replacing bindings reset row lengths
    if (dwFlags == CURSOR_DBCOLUMNBINDOPTS_REPLACE)
    {
        *pcbNewRowLength    = 0;
        *pcbNewVarRowLength = 0;
    }
    else // if we're adding bindings set to current row lengths
    {
        *pcbNewRowLength    = m_cbRowLength;
        *pcbNewVarRowLength = m_cbVarRowLength;
    }

    // if no row length was requested, use required row length
    if (!cbRequestedRowLength)
    {
        *pcbNewRowLength += cbRequiredRowLength;
    }
    else    // make sure row length is large enough
    {
        if (cbRequestedRowLength < *pcbNewRowLength + cbRequiredRowLength)
        {
            VDSetErrorInfo(IDS_ERR_ROWTOOSHORT, IID_ICursor, m_pResourceDLL);
            return CURSOR_DB_E_ROWTOOSHORT;
        }

        // use requested row length
        *pcbNewRowLength += cbRequestedRowLength;
    }

    // calculate required variable row length
    *pcbNewVarRowLength += cbRequiredVarRowLength;

    return S_OK;
}


//=--------------------------------------------------------------------------=
// DoCursorBindingsNeedVarData - Return TRUE if current cursor column bindings
//                               need variable length buffer
//
BOOL CVDCursorBase::DoCursorBindingsNeedVarData()
{
    BOOL fNeedVarData = FALSE;

    CURSOR_DBCOLUMNBINDING * pCursorBinding = m_pCursorBindings;

    for (ULONG ulBind = 0; ulBind < m_ulCursorBindings && !fNeedVarData; ulBind++)
    {
        if (DoesCursorTypeNeedVarData(pCursorBinding->dwDataType))
            fNeedVarData = TRUE;

        pCursorBinding++;
    }

    return fNeedVarData;
}

//=--------------------------------------------------------------------------=
// Validate fetch params
//=--------------------------------------------------------------------------=
//
// Parameters:
//    pFetchParams   - [in] ptr to the CURSOR_DBFETCHROWS structure
//    riid		     - [in] guid of calling interface (used for error generation)
//
// Output:
//    HRESULT - S_OK if pFetchParams valid
//              CURSOR_DB_E_BADFETCHINFO if pFetchParams invalid
//
//
HRESULT CVDCursorBase::ValidateFetchParams(CURSOR_DBFETCHROWS *pFetchParams, REFIID riid)
{

    if (!pFetchParams)
	{
        VDSetErrorInfo(IDS_ERR_INVALIDARG, riid, m_pResourceDLL);
        return E_INVALIDARG;
	}

    // init out parameter
    pFetchParams->cRowsReturned = 0;

    // return if caller didn't ask for any rows
    if (!pFetchParams->cRowsRequested)
        return S_OK;

	HRESULT hr = 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))
        hr =  CURSOR_DB_E_BADFETCHINFO;

    // if memory was caller allocated, make sure caller supplied data pointer 
    if (!(pFetchParams->dwFlags & CURSOR_DBROWFETCH_CALLEEALLOCATES) && !pFetchParams->pData)
        hr =  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))
        hr =  CURSOR_DB_E_BADFETCHINFO;

	if (FAILED(hr))
        VDSetErrorInfo(IDS_ERR_BADFETCHINFO, riid, m_pResourceDLL);

	return hr;

}

//=--------------------------------------------------------------------------=
// IUnknown methods implemented
//=--------------------------------------------------------------------------=
//=--------------------------------------------------------------------------=
// IUnknown QueryInterface
//
HRESULT CVDCursorBase::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_IF(this, ICursorScroll, SupportsScroll());
		QI_INTERFACE_SUPPORTED(this, ISupportErrorInfo);
    }                   

    if (NULL == *ppvObjOut)
        return E_NOINTERFACE;

    AddRef();

    return S_OK;
}

//=--------------------------------------------------------------------------=
// IUnknown AddRef (Notifier and MetadataCursor maintain reference count)
//
ULONG CVDCursorBase::AddRef(void)
{
   return (ULONG)E_NOTIMPL;
}

//=--------------------------------------------------------------------------=
// IUnknown Release (Notifier and MetadataCursor maintain reference count)
//
ULONG CVDCursorBase::Release(void)
{
   return (ULONG)E_NOTIMPL;
}

//=--------------------------------------------------------------------------=
// ICursor methods implemented
//=--------------------------------------------------------------------------=
// 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_OUTOFMEMORY not enough memory
//
// Notes:
//    Parameter validation is performed by derived classes
//
HRESULT CVDCursorBase::SetBindings(ULONG cCol, CURSOR_DBCOLUMNBINDING rgBoundColumns[], ULONG cbRowLength, DWORD dwFlags)
{
    // reset flag
    m_fNeedVarData = FALSE;

    // if we should replace, then first destroy existing bindings
    if (dwFlags == CURSOR_DBCOLUMNBINDOPTS_REPLACE)
	    DestroyCursorBindings(&m_pCursorBindings, &m_ulCursorBindings);
    
    // if no new bindings are supplied, we're done
    if (!cCol)
        return S_OK;

    // create new storage
    CURSOR_DBCOLUMNBINDING * pCursorBindings = new CURSOR_DBCOLUMNBINDING[m_ulCursorBindings + cCol];

    if (!pCursorBindings)
    {
        VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL);
        return E_OUTOFMEMORY;
    }

    // if we have exsiting bindings, then copy them over
    if (m_pCursorBindings)
        memcpy(pCursorBindings, m_pCursorBindings, m_ulCursorBindings * sizeof(CURSOR_DBCOLUMNBINDING));

    // then append new bindings directly,
    memcpy(pCursorBindings + m_ulCursorBindings, rgBoundColumns, cCol * sizeof(CURSOR_DBCOLUMNBINDING));

    // and adjust possible cursor column identifier names in new bindings
    for (ULONG ulBind = m_ulCursorBindings; ulBind < m_ulCursorBindings + cCol; ulBind++)
    {
        CURSOR_DBCOLUMNID * pCursorColumnID = &pCursorBindings[ulBind].columnID; 

        if (pCursorColumnID->dwKind == CURSOR_DBCOLKIND_GUID_NAME || pCursorColumnID->dwKind == CURSOR_DBCOLKIND_NAME)
        {
            const int nLength = lstrlenW(pCursorColumnID->lpdbsz);

            WCHAR * pwszName = new WCHAR[nLength + 1];
			if (!pwszName)
			{
				DestroyCursorBindings(&pCursorBindings, &ulBind);
				delete [] m_pCursorBindings;
				m_ulCursorBindings	= 0;
				VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL);
				return E_OUTOFMEMORY;
			}
			memcpy(pwszName, pCursorColumnID->lpdbsz, (nLength + 1) * sizeof(WCHAR));
			pCursorColumnID->lpdbsz = pwszName;
        }
    }

    m_ulCursorBindings += cCol;

    // delete previous storage
	// any existing bindings will have been copied over into 
	delete [] m_pCursorBindings;

    m_pCursorBindings = pCursorBindings;

    // determine if new bindings need variable length buffer
    m_fNeedVarData = DoCursorBindingsNeedVarData();

    return S_OK;
}

//=--------------------------------------------------------------------------=
// ICursor GetBindings
//=--------------------------------------------------------------------------=
// Returns the current column bindings
//
// Parameters:
//    pcCol             - [out] a pointer to memory in which to return the 
//                              number of bound columns
//    prgBoundColumns   - [out] a pointer to memory in which to return a 
//                              pointer to an array containing the current
//                              column bindings (callee allocated)
//    pcbRowLength      - [out] a pointer to memory in which to return the
//                              number of bytes of inline memory in a single 
//                              row
//
// Output:
//    HRESULT - S_OK if successful
//              E_OUTOFMEMORY not enough memory
//
// Notes: 
//
HRESULT CVDCursorBase::GetBindings(ULONG *pcCol, 
								   CURSOR_DBCOLUMNBINDING *prgBoundColumns[], 
								   ULONG *pcbRowLength)
{
    ASSERT_NULL_OR_POINTER(pcCol, ULONG)
    ASSERT_NULL_OR_POINTER(prgBoundColumns, CURSOR_DBCOLUMNBINDING)
    ASSERT_NULL_OR_POINTER(pcbRowLength, ULONG)

    // init out parameters
    if (pcCol)
        *pcCol = 0;

    if (prgBoundColumns)
        *prgBoundColumns = NULL;

    if (pcbRowLength)
        *pcbRowLength = 0;

    // return column bindings
    if (prgBoundColumns && m_ulCursorBindings)    
    {
        // calculate size of bindings
        ULONG cbBindings = m_ulCursorBindings * sizeof(CURSOR_DBCOLUMNBINDING);

        // calculate extra space needed for names in column identifers
        ULONG cbNames = 0;

        for (ULONG ulBind = 0; ulBind < m_ulCursorBindings; ulBind++)
            cbNames += GetCursorColumnIDNameLength(m_pCursorBindings[ulBind].columnID);

        // allocate memory for bindings and names
        CURSOR_DBCOLUMNBINDING * pCursorBindings = (CURSOR_DBCOLUMNBINDING*)g_pMalloc->Alloc(cbBindings + cbNames);

        if (!pCursorBindings)
        {
            VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL);
            return E_OUTOFMEMORY;
        }

        // copy bindings directly
        memcpy(pCursorBindings, m_pCursorBindings, cbBindings);

        // adjust column identifier names
        WCHAR * pwszName = (WCHAR*)(pCursorBindings + m_ulCursorBindings);

        for (ulBind = 0; ulBind < m_ulCursorBindings; ulBind++)
        {
            CURSOR_DBCOLUMNID * pCursorColumnID = &pCursorBindings[ulBind].columnID;

            if (pCursorColumnID->dwKind == CURSOR_DBCOLKIND_GUID_NAME || pCursorColumnID->dwKind == CURSOR_DBCOLKIND_NAME)
            {
                const int nLength = lstrlenW(pCursorColumnID->lpdbsz);

                memcpy(pwszName, pCursorColumnID->lpdbsz, (nLength + 1) * sizeof(WCHAR)); 
                pCursorColumnID->lpdbsz = pwszName;
                pwszName += nLength + 1;
            }
        }

        *prgBoundColumns = pCursorBindings;

		// sanity check
		ASSERT_((BYTE*)pwszName == ((BYTE*)pCursorBindings) + cbBindings + cbNames);
    
	}
    
    // return bound column count
    if (pcCol)  
        *pcCol = m_ulCursorBindings;

    // return row length
    if (pcbRowLength)
        *pcbRowLength = m_cbRowLength;

    return S_OK;
}