/****************************************************************************** * $Id: IFRowsetImpl.h,v 1.1 2002/08/09 21:36:39 warmerda Exp $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: RowsetInterface implementation used for main features rowset. * Only used by CSFRowsetImpl in SFRS.h. * Author: Frank Warmerdam * * This code is closely derived from the code in ATLDB.H for IRowsetImpl. * It basically modifies the CRowsetImpl to check overall status from * the row fetching mechanism (which comes from CVirtualArray). This is * all based on the fact that we don't know the rowset size in advance, and * so must avoid calling CVirtualArray.GetSize(). * * Note: also contains some fixes mentioned in ICRRowsetImpl.h from which it * was directly derived. * ****************************************************************************** * Copyright (c) 2002, Frank Warmerdam * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************** * * $Log: IFRowsetImpl.h,v $ * Revision 1.1 2002/08/09 21:36:39 warmerda * New * * Revision 1.2 2002/02/05 20:42:46 warmerda * use CheckRows() in GetNextRows() to check availability * * Revision 1.1 2002/01/31 16:47:24 warmerda * New * */ #ifndef _IFRowsetImpl_INCLUDED #define _IFRowsetImpl_INCLUDED template HRESULT SFTransferData(T* pT, bool bReading, void* pData, RowClass* pRow, MapClass* /*pMap*/, HACCESSOR hAccessor) { ATLTRACE(atlTraceDBProvider, 2, _T("SFTransferData\n")); bool bFailed = false; bool bSucceeded = false; HRESULT hr = S_OK; __if_exists(T::Fire_OnFieldChange) { CAtlArray rgColumns; HROW hNotifyRow = NULL; //HROW hNotifyRow = pT->m_rgRowHandles.ReverseLookup(pRow); { POSITION pos = pT->m_rgRowHandles.GetStartPosition(); while( pos != NULL ) { MapClass::CPair* pPair = pT->m_rgRowHandles.GetNext( pos ); ATLASSERT( pPair != NULL ); if( pPair->m_value == pRow ) { hNotifyRow = pPair->m_key; break; } } } } __if_exists(T::Fire_OnRowChange) { // We need to send the DBREASON_ROW_FIRSTCHANGE notification's // SYNCHAFTER phase in this function. IFF. we're deferred and // we have a newly changed row. CComVariant varDeferred; bool bDeferred; hr = pT->GetPropValue(&DBPROPSET_ROWSET, DBPROP_IRowsetUpdate, &varDeferred); (FAILED(hr) || varDeferred.boolVal == ATL_VARIANT_FALSE) ? bDeferred = false : bDeferred = true; } int cAvailableRows; cAvailableRows = pT->m_rgRowData.CheckRows(pRow->m_iRowset, 1 ); // Check for a deleted row if( cAvailableRows < 1 ) { __if_exists(T::Fire_OnFieldChange) { if( !bReading ) { SendRowsFirstChangeFailureNotification( pT, pRow, &hNotifyRow, bDeferred ); } } return DB_E_DELETEDROW; } // NOTE: This was checking against DBPENDINGSTATUS_DELETED. Instead, it // should check for DBPENDINGSTATUS_INVALIDROW (means a forced deleted // row). if (pRow->m_status == DBPENDINGSTATUS_INVALIDROW) { __if_exists(T::Fire_OnFieldChange) { if( !bReading ) { SendRowsFirstChangeFailureNotification( pT, pRow, &hNotifyRow, bDeferred ); } } return DB_E_DELETEDROW; } T::_BindType* pBinding; bool bFound = pT->m_rgBindings.Lookup((INT_PTR)hAccessor, pBinding); if (!bFound || pBinding == NULL) { __if_exists(T::Fire_OnFieldChange) { if( !bReading ) { SendRowsFirstChangeFailureNotification( pT, pRow, &hNotifyRow, bDeferred ); } } return DB_E_BADACCESSORHANDLE; } if (pData == NULL && pBinding->cBindings != 0) { __if_exists(T::Fire_OnFieldChange) { if( !bReading ) { SendRowsFirstChangeFailureNotification( pT, pRow, &hNotifyRow, bDeferred ); } } return E_INVALIDARG; } void* pDstData; void* pSrcData; if (bReading) { pDstData = pData; pSrcData = (void*) pT->m_rgRowData.GetRow( (int)pRow->m_iRowset, hr ); if( pSrcData == NULL || hr != S_OK ) return hr; } else { pSrcData = pData; pDstData = (void*) pT->m_rgRowData.GetRow( (int)pRow->m_iRowset, hr ); if( pDstData == NULL || hr != S_OK ) return hr; } if (!bReading) { // Send the OKTODO notification __if_exists(T::Fire_OnFieldChange) { if (/* pRow->m_status != DBPENDINGSTATUS_NEW && */ pRow->m_status != (DBPENDINGSTATUS_NEW | DBPENDINGSTATUS_UNCHANGED)) { HRESULT hrNotify; for (DBORDINAL l=0; lcBindings; l++) { _ATLTRY { rgColumns.Add(pBinding->pBindings[l].iOrdinal); } _ATLCATCH( e ) { _ATLDELETEEXCEPTION( e ); return E_FAIL; } } hrNotify = pT->Fire_OnFieldChange(pT, hNotifyRow, pBinding->cBindings, rgColumns.GetData(), DBREASON_COLUMN_SET, DBEVENTPHASE_OKTODO, FALSE); if ((hrNotify != S_OK) && (hrNotify != E_FAIL)) { __if_exists(T::Fire_OnRowChange) { if (bDeferred) pT->Fire_OnRowChange(pT, 1, &hNotifyRow, DBREASON_ROW_FIRSTCHANGE, DBEVENTPHASE_FAILEDTODO, TRUE); return DB_E_CANCELED; } } hrNotify = pT->Fire_OnFieldChange(pT, hNotifyRow, pBinding->cBindings, rgColumns.GetData(), DBREASON_COLUMN_SET, DBEVENTPHASE_ABOUTTODO, FALSE); if ((hrNotify != S_OK) && (hrNotify != E_FAIL)) { __if_exists(T::Fire_OnRowChange) { if (bDeferred) pT->Fire_OnRowChange(pT, 1, &hNotifyRow, DBREASON_ROW_FIRSTCHANGE, DBEVENTPHASE_FAILEDTODO, TRUE); return DB_E_CANCELED; } } hrNotify = pT->Fire_OnFieldChange(pT, hNotifyRow, pBinding->cBindings, rgColumns.GetData(), DBREASON_COLUMN_SET, DBEVENTPHASE_SYNCHAFTER, FALSE); if ((hrNotify != S_OK) && (hrNotify != E_FAIL)) { __if_exists(T::Fire_OnRowChange) { if (bDeferred) pT->Fire_OnRowChange(pT, 1, &hNotifyRow, DBREASON_ROW_FIRSTCHANGE, DBEVENTPHASE_FAILEDTODO, TRUE); return DB_E_CANCELED; } } } } __if_exists(T::Fire_OnRowChange) { if(bDeferred && pRow->m_status != DBPENDINGSTATUS_CHANGED && pRow->m_status != (DBPENDINGSTATUS_NEW | DBPENDINGSTATUS_UNCHANGED)) { HRESULT hrNotify = pT->Fire_OnRowChange(pT, 1, &hNotifyRow, DBREASON_ROW_FIRSTCHANGE, DBEVENTPHASE_SYNCHAFTER, FALSE); if ((hrNotify != S_OK) && (hrNotify != E_FAIL)) { __if_exists(T::Fire_OnFieldChange) { pT->Fire_OnFieldChange(pT, hNotifyRow, pBinding->cBindings, rgColumns.GetData(), DBREASON_COLUMN_SET, DBEVENTPHASE_FAILEDTODO, TRUE); } return DB_E_CANCELED; } } } } DBORDINAL cCols; ATLCOLUMNINFO* pColInfo = T::GetColumnInfo(pT, &cCols); for (DBORDINAL iBind =0; iBind < pBinding->cBindings; iBind++) { DBBINDING* pBindCur = &(pBinding->pBindings[iBind]); DBORDINAL iColInfo; for (iColInfo = 0; iColInfo < cCols && pBindCur->iOrdinal != pColInfo[iColInfo].iOrdinal; iColInfo++); if (iColInfo == cCols) { __if_exists(T::Fire_OnFieldChange) { if( !bReading ) { SendColumnSetFailureNotification( pT, hNotifyRow, pBinding, rgColumns ); SendRowsFirstChangeFailureNotification( pT, pRow, &hNotifyRow, bDeferred ); } } return DB_E_BADORDINAL; } ATLCOLUMNINFO* pColCur = &(pColInfo[iColInfo]); // Ordinal found at iColInfo BYTE* pSrcTemp = (bReading) ? (BYTE*)pSrcData + pColCur->cbOffset : (BYTE*)pSrcData + pBindCur->obValue; BYTE* pDstTemp = NULL; if (pBindCur->dwPart & DBPART_VALUE) pDstTemp = (bReading) ? (BYTE*)pDstData + pBindCur->obValue : (BYTE*)pDstData + pColCur->cbOffset; if (!bReading) { // Check to see that the appropriate data parts are available if ((pBindCur->dwPart & DBPART_LENGTH) && !(pBindCur->dwPart & DBPART_VALUE) && !(pBindCur->dwPart & DBPART_STATUS)) { __if_exists(T::Fire_OnFieldChange) { if (/* pRow->m_status != DBPENDINGSTATUS_NEW && */ pRow->m_status != (DBPENDINGSTATUS_NEW | DBPENDINGSTATUS_UNCHANGED)) { pT->Fire_OnFieldChange(pT, hNotifyRow, pBinding->cBindings, rgColumns.GetData(), DBREASON_COLUMN_SET, DBEVENTPHASE_FAILEDTODO, TRUE); } } // Not sure why you would want to run SetData here! bFailed = true; continue; } } // Handle the the status for any consumer issues DBSTATUS dbStat = DBSTATUS_S_OK; if (bReading) { dbStat = pT->GetDBStatus(pRow, pColCur); if (dbStat == DBSTATUS_S_ISNULL) { if (pBindCur->dwPart & DBPART_STATUS) *((DBSTATUS*)((BYTE*)(pDstData) + pBindCur->obStatus)) = dbStat; // Set the length to 0 as reqiured by the spec. if (pBindCur->dwPart & DBPART_LENGTH) *((DBLENGTH*)((BYTE*)(pDstData) + pBindCur->obLength)) = 0; // Set the destination value to NULL if (pBindCur->dwPart & DBPART_VALUE) *pDstTemp = NULL; continue; } } else { // Allow the provider to do checking for DBSTATUS_S_ISNULL if (pBindCur->dwPart & DBPART_STATUS) { dbStat = *((DBSTATUS*)((BYTE*)(pSrcData) + pBindCur->obStatus)); // Return DBSTATUS_E_UNAVAILABLE if the status is DBSTATUS_S_OK // and either the value part is not bound or the length part is // bound and the type is DBTYPE_BYTES. // There was another entry of code here with LENGTH, NO VALUE, // and status was not DBSTATUS_S_ISNULL. May need to regenerate that if (dbStat == DBSTATUS_S_OK) { if (!(pBindCur->dwPart & DBPART_VALUE) || ((pBindCur->dwPart & DBPART_LENGTH) && (pBindCur->wType == DBTYPE_BYTES))) { // Can't set non-null columns w/o a value part __if_exists(T::Fire_OnFieldChange) { if (/* pRow->m_status != DBPENDINGSTATUS_NEW && */ pRow->m_status != (DBPENDINGSTATUS_NEW | DBPENDINGSTATUS_UNCHANGED)) { pT->Fire_OnFieldChange(pT, hNotifyRow, pBinding->cBindings, rgColumns.GetData(), DBREASON_COLUMN_SET, DBEVENTPHASE_FAILEDTODO, TRUE); } } bFailed = true; *((DBSTATUS*)((BYTE*)(pSrcData) + pBindCur->obStatus)) = DBSTATUS_E_UNAVAILABLE; continue; } } switch (dbStat) { case DBSTATUS_S_ISNULL: if (!(pColCur->dwFlags & DBCOLUMNFLAGS_ISNULLABLE) || FAILED(pT->SetDBStatus(&dbStat, pRow, pColCur))) { __if_exists(T::Fire_OnFieldChange) { if (/* pRow->m_status != DBPENDINGSTATUS_NEW && */ pRow->m_status != (DBPENDINGSTATUS_NEW | DBPENDINGSTATUS_UNCHANGED)) { pT->Fire_OnFieldChange(pT, hNotifyRow, pBinding->cBindings, rgColumns.GetData(), DBREASON_COLUMN_SET, DBEVENTPHASE_FAILEDTODO, TRUE); } } // Special processing for attempting to write, read-only columns if (!(pColCur->dwFlags & DBCOLUMNFLAGS_ISNULLABLE)) *((DBSTATUS*)((BYTE*)(pSrcData) + pBindCur->obStatus)) = DBSTATUS_E_INTEGRITYVIOLATION; bFailed = true; } else { __if_exists(T::Fire_OnFieldChange) { if (/* pRow->m_status != DBPENDINGSTATUS_NEW && */ pRow->m_status != (DBPENDINGSTATUS_NEW | DBPENDINGSTATUS_UNCHANGED)) { pT->Fire_OnFieldChange(pT, hNotifyRow, 1, &iBind, DBREASON_COLUMN_SET, DBEVENTPHASE_DIDEVENT, TRUE); } } bSucceeded = true; dbStat = DBSTATUS_S_OK; if (pBindCur->dwPart & DBPART_VALUE) *pDstTemp = NULL; } continue; break; case DBSTATUS_S_DEFAULT: case DBSTATUS_S_IGNORE: { HRESULT hrStatus = pT->SetDBStatus(&dbStat, pRow, pColCur); *((DBSTATUS*)((BYTE*)(pSrcData) + pBindCur->obStatus)) = dbStat; if (FAILED(hrStatus)) { __if_exists(T::Fire_OnFieldChange) { if (/* pRow->m_status != DBPENDINGSTATUS_NEW && */ pRow->m_status != (DBPENDINGSTATUS_NEW | DBPENDINGSTATUS_UNCHANGED)) { pT->Fire_OnFieldChange(pT, hNotifyRow, pBinding->cBindings, rgColumns.GetData(), DBREASON_COLUMN_SET, DBEVENTPHASE_FAILEDTODO, TRUE); } } // Note, status should be set by SetDBStatus bFailed = true; } else { __if_exists(T::Fire_OnFieldChange) { if (/* pRow->m_status != DBPENDINGSTATUS_NEW && */ pRow->m_status != (DBPENDINGSTATUS_NEW | DBPENDINGSTATUS_UNCHANGED)) { pT->Fire_OnFieldChange(pT, hNotifyRow, pBinding->cBindings, rgColumns.GetData(), DBREASON_COLUMN_SET, DBEVENTPHASE_DIDEVENT, TRUE); } } bSucceeded = true; } continue; break; } case DBSTATUS_S_OK: // Still call SetDBStatus here as they may have locks on // integrity contstraints to observe if (FAILED(pT->SetDBStatus(&dbStat, pRow, pColCur))) { __if_exists(T::Fire_OnFieldChange) { if (/* pRow->m_status != DBPENDINGSTATUS_NEW && */ pRow->m_status != (DBPENDINGSTATUS_NEW | DBPENDINGSTATUS_UNCHANGED)) { pT->Fire_OnFieldChange(pT, hNotifyRow, pBinding->cBindings, rgColumns.GetData(), DBREASON_COLUMN_SET, DBEVENTPHASE_FAILEDTODO, TRUE); } } bFailed = true; *((DBSTATUS*)((BYTE*)(pSrcData) + pBindCur->obStatus)) = dbStat; continue; } break; default: *((DBSTATUS*)((BYTE*)(pSrcData) + pBindCur->obStatus)) = DBSTATUS_E_BADSTATUS; __if_exists(T::Fire_OnFieldChange) { if (/* pRow->m_status != DBPENDINGSTATUS_NEW && */ pRow->m_status != (DBPENDINGSTATUS_NEW | DBPENDINGSTATUS_UNCHANGED)) { pT->Fire_OnFieldChange(pT, hNotifyRow, pBinding->cBindings, rgColumns.GetData(), DBREASON_COLUMN_SET, DBEVENTPHASE_FAILEDTODO, TRUE); } } bFailed = true; continue; break; } } } // Determine sizes of input and output columns DBLENGTH cbCol = 0; DBLENGTH cbDst; if (bReading) cbDst = pBindCur->cbMaxLen; else cbDst = pColCur->ulColumnSize; switch (pColCur->wType) { case DBTYPE_STR: if (bReading) cbCol = lstrlenA((LPSTR)(((BYTE*)pSrcData) + pColCur->cbOffset)); else { // Use the length field when setting data if (pBindCur->dwPart & DBPART_LENGTH) cbCol = *((DBLENGTH*)((BYTE*)(pSrcData) + pBindCur->obLength)); else cbCol = lstrlenA((LPSTR)(pSrcTemp)); // was cbDst if (cbCol >= cbDst) { if (cbCol > (cbDst + 1)) // over maximum case { __if_exists(T::Fire_OnFieldChange) { if (/* pRow->m_status != DBPENDINGSTATUS_NEW && */ pRow->m_status != (DBPENDINGSTATUS_NEW | DBPENDINGSTATUS_UNCHANGED)) { pT->Fire_OnFieldChange(pT, hNotifyRow, pBinding->cBindings, rgColumns.GetData(), DBREASON_COLUMN_SET, DBEVENTPHASE_FAILEDTODO, TRUE); } } bFailed = true; if (pBindCur->dwPart & DBPART_STATUS) *((DBSTATUS*)((BYTE*)(pSrcData) + pBindCur->obStatus)) = DBSTATUS_E_CANTCONVERTVALUE; continue; } } cbCol = cbDst; // Leave room for NULL term. need to copy for WSTR } break; case DBTYPE_WSTR: case DBTYPE_BSTR: if (bReading) cbCol = lstrlenW((LPWSTR)(((BYTE*)pSrcData) + pColCur->cbOffset)) * sizeof(WCHAR); else { if (pBindCur->dwPart & DBPART_LENGTH) cbCol = *((DBLENGTH*)((BYTE*)(pSrcData) + pBindCur->obLength)); else cbCol = lstrlenW((LPWSTR)(pSrcData)) * sizeof(WCHAR); if (cbCol >= cbDst) { if (cbCol > (cbDst + 1)) // over maximum case { __if_exists(T::Fire_OnFieldChange) { if (/* pRow->m_status != DBPENDINGSTATUS_NEW && */ pRow->m_status != (DBPENDINGSTATUS_NEW | DBPENDINGSTATUS_UNCHANGED)) { pT->Fire_OnFieldChange(pT, hNotifyRow, pBinding->cBindings, rgColumns.GetData(), DBREASON_COLUMN_SET, DBEVENTPHASE_FAILEDTODO, TRUE); } } bFailed = true; if (pBindCur->dwPart & DBPART_STATUS) *((DBSTATUS*)((BYTE*)(pSrcData) + pBindCur->obStatus)) = DBSTATUS_E_CANTCONVERTVALUE; continue; } } cbCol = cbDst; // Leave room for NULL term. need to copy for WSTR } break; case DBTYPE_BYTES: if (bReading) cbCol = pColCur->ulColumnSize; else { if (pBindCur->dwPart & DBPART_LENGTH) cbCol = *((DBLENGTH *)((BYTE*)(pSrcData) + pBindCur->obLength)); else { __if_exists(T::Fire_OnFieldChange) { if (/* pRow->m_status != DBPENDINGSTATUS_NEW && */ pRow->m_status != (DBPENDINGSTATUS_NEW | DBPENDINGSTATUS_UNCHANGED)) { pT->Fire_OnFieldChange(pT, hNotifyRow, pBinding->cBindings, rgColumns.GetData(), DBREASON_COLUMN_SET, DBEVENTPHASE_FAILEDTODO, TRUE); } } // If no length part is bound for DBTYPE_BYTES, it is an error bFailed = true; continue; } if (cbCol >= cbDst) cbCol = cbDst; // Leave room for NULL term. need to copy for WSTR } break; default: if (bReading) cbCol = pColCur->ulColumnSize; else cbDst = pColCur->ulColumnSize; break; } // Handle cases where we have provider owned memory. Note, these should be // with DBTYPE_BYREF (otherwise, it doesn't make sense). if (pBindCur->dwPart & DBPART_VALUE) { if (pBindCur->dwMemOwner == DBMEMOWNER_PROVIDEROWNED && pBindCur->wType & DBTYPE_BYREF) { *(BYTE**)pDstTemp = pSrcTemp; } else { ATLASSERT(pT->m_spConvert != NULL); hr = pT->m_spConvert->DataConvert(pColCur->wType, pBindCur->wType, cbCol, &cbDst, pSrcTemp, pDstTemp, pBindCur->cbMaxLen, dbStat, &dbStat, pBindCur->bPrecision, pBindCur->bScale,0); } } if (pBindCur->dwPart & DBPART_LENGTH) { if (bReading) { if (!(pBindCur->dwPart & DBPART_VALUE)) cbDst = cbCol; // We didn't have the data convert to correct this *((DBLENGTH*)((BYTE*)(pDstData) + pBindCur->obLength)) = (dbStat == DBSTATUS_S_ISNULL) ? 0 : cbDst; } else *((DBLENGTH*)((BYTE*)(pSrcData) + pBindCur->obLength)) = cbDst; } if (pBindCur->dwPart & DBPART_STATUS) { if (bReading) *((DBSTATUS*)((BYTE*)(pDstData) + pBindCur->obStatus)) = dbStat; else *((DBSTATUS*)((BYTE*)(pSrcData) + pBindCur->obStatus)) = dbStat; } if (FAILED(hr)) { if (!bReading) { __if_exists(T::Fire_OnFieldChange) { if (/* pRow->m_status != DBPENDINGSTATUS_NEW && */ pRow->m_status != (DBPENDINGSTATUS_NEW | DBPENDINGSTATUS_UNCHANGED)) { pT->Fire_OnFieldChange(pT, hNotifyRow, pBinding->cBindings, rgColumns.GetData(), DBREASON_COLUMN_SET, DBEVENTPHASE_FAILEDTODO, TRUE); } } } bFailed = true; } else { bSucceeded = true; } } // Return error codes to the consumer if (bFailed) { __if_exists(T::Fire_OnFieldChange) { if( !bReading ) { // SendColumnSetFailureNotification( pT, hNotifyRow, pBinding, rgColumns ); SendRowsFirstChangeFailureNotification( pT, pRow, &hNotifyRow, bDeferred ); } } return (bSucceeded != false) ? DB_S_ERRORSOCCURRED : DB_E_ERRORSOCCURRED; } else { if (!bReading) { __if_exists(T::Fire_OnFieldChange) { if (/* pRow->m_status != DBPENDINGSTATUS_NEW && */ pRow->m_status != (DBPENDINGSTATUS_NEW | DBPENDINGSTATUS_UNCHANGED)) { pT->Fire_OnFieldChange(pT, hNotifyRow, pBinding->cBindings, rgColumns.GetData(), DBREASON_COLUMN_SET, DBEVENTPHASE_DIDEVENT, TRUE); } } } return hr; } } // IFRowsetImpl template > class ATL_NO_VTABLE IFRowsetImpl : public RowsetInterface { public: typedef RowClass _HRowClass; IFRowsetImpl() { m_iRowset = 0; m_bCanScrollBack = false; m_bCanFetchBack = false; m_bRemoveDeleted = true; m_bIRowsetUpdate = false; m_bReset = true; m_bExternalFetch = false; } ~IFRowsetImpl() { //for (int i = 0; i < m_rgRowHandles.GetCount(); i++) // delete (m_rgRowHandles.GetValueAt(i)); POSITION pos = m_rgRowHandles.GetStartPosition(); while( pos != NULL ) { MapClass::CPair *pPair = m_rgRowHandles.GetNext(pos); ATLASSERT( pPair != NULL ); delete pPair->m_value; } } HRESULT RefRows(DBCOUNTITEM cRows, const HROW rghRows[], DBREFCOUNT rgRefCounts[], DBROWSTATUS rgRowStatus[], BOOL bAdd) { ATLTRACE(atlTraceDBProvider, 2, _T("IFRowsetImpl::AddRefRows\n")); if (cRows == 0) return S_OK; if (rghRows == NULL) return E_INVALIDARG; T::ObjectLock cab((T*)this); BOOL bSuccess1 = FALSE; BOOL bFailed1 = FALSE; DBROWSTATUS rs; DWORD dwRef; __if_exists(T::Fire_OnRowChange) { // Maintain an array of handles w/ zero ref counts for notification CAtlArray arrZeroHandles; } for (ULONG iRow = 0; iRow < cRows; iRow++) { HROW hRowCur = rghRows[iRow]; RowClass* pRow; bool bFound = m_rgRowHandles.Lookup((RowClass::KeyType)hRowCur, pRow); if (!bFound || pRow == NULL) { ATLTRACE(atlTraceDBProvider, 0, _T("Could not find HANDLE %x in list\n")); rs = DBROWSTATUS_E_INVALID; dwRef = 0; bFailed1 = TRUE; } else { if (pRow->m_status != DBPENDINGSTATUS_UNCHANGED && pRow->m_status != DBPENDINGSTATUS_INVALIDROW && pRow->m_dwRef == 0 && !bAdd) { if (rgRefCounts) rgRefCounts[iRow] = 0; if (rgRowStatus != NULL) rgRowStatus[iRow] = DBROWSTATUS_E_INVALID; bFailed1 = TRUE; continue; } // Check if we're in immediate or deferred mode CComVariant varDeferred; bool bDeferred; T* pT = (T*)this; HRESULT hr = pT->GetPropValue(&DBPROPSET_ROWSET, DBPROP_IRowsetUpdate, &varDeferred); (FAILED(hr) || varDeferred.boolVal == ATL_VARIANT_FALSE) ? bDeferred = false : bDeferred = true; if (!bDeferred && bAdd && pRow->m_status == DBPENDINGSTATUS_DELETED) { bFailed1 = TRUE; if (rgRowStatus != NULL) rgRowStatus[iRow] = DBROWSTATUS_E_DELETED; continue; } if (bAdd) dwRef = pRow->AddRefRow(); else { dwRef = pRow->ReleaseRow(); if ((pRow->m_status != DBPENDINGSTATUS_UNCHANGED && pRow->m_status != 0 && pRow->m_status != DBPENDINGSTATUS_INVALIDROW) && bDeferred) { if (rgRefCounts) rgRefCounts[iRow] = dwRef; if (rgRowStatus != NULL) rgRowStatus[iRow] = DBROWSTATUS_S_PENDINGCHANGES; bSuccess1 = TRUE; continue; } if (dwRef == 0) { __if_exists(T::Fire_OnRowsetChange) { _ATLTRY { arrZeroHandles.Add(hRowCur); } _ATLCATCH( e ) { _ATLDELETEEXCEPTION( e ); return E_FAIL; } } // Now determine if the DBPROP_REMOVEDELETED property // is ATL_VARIANT_FALSE. If so, then do NOT remove the // row. hr = pT->GetPropValue(&DBPROPSET_ROWSET, DBPROP_REMOVEDELETED, &varDeferred); if (FAILED(hr) || varDeferred.boolVal != ATL_VARIANT_FALSE) { delete pRow; m_rgRowHandles.RemoveKey((RowClass::KeyType)hRowCur); } } } bSuccess1 = TRUE; rs = DBROWSTATUS_S_OK; } if (rgRefCounts) rgRefCounts[iRow] = dwRef; if (rgRowStatus != NULL) rgRowStatus[iRow] = rs; } __if_exists(T::Fire_OnRowsetChange) { if (!bAdd && arrZeroHandles.GetCount() > 0) { T* pT = (T*)this; pT->Fire_OnRowChange(pT, (ULONG_PTR)arrZeroHandles.GetCount(), arrZeroHandles.GetData(), DBREASON_ROW_RELEASE, DBEVENTPHASE_DIDEVENT, FALSE); } } if (!bSuccess1 && !bFailed1) { ATLTRACE(atlTraceDBProvider, 0, _T("IFRowsetImpl::RefRows Unexpected state\n")); return E_FAIL; } HRESULT hr = S_OK; if (bSuccess1 && bFailed1) hr = DB_S_ERRORSOCCURRED; if (!bSuccess1 && bFailed1) hr = DB_E_ERRORSOCCURRED; return hr; } STDMETHOD(AddRefRows)(DBCOUNTITEM cRows, const HROW rghRows[], DBREFCOUNT rgRefCounts[], DBROWSTATUS rgRowStatus[]) { ATLTRACE(atlTraceDBProvider, 2, _T("IFRowsetImpl::AddRefRows\n")); if (cRows == 0) return S_OK; return RefRows(cRows, rghRows, rgRefCounts, rgRowStatus, TRUE); } virtual DBSTATUS GetDBStatus(RowClass* , ATLCOLUMNINFO*) { return DBSTATUS_S_OK; } virtual HRESULT SetDBStatus(DBSTATUS*, RowClass* , ATLCOLUMNINFO*) { // The provider overrides this function to handle special processing // for DBSTATUS_S_ISNULL and DBSTATUS_S_DEFAULT. return S_OK; } OUT_OF_LINE HRESULT GetDataHelper(HACCESSOR hAccessor, ATLCOLUMNINFO*& rpInfo, void** ppBinding, void*& rpSrcData, DBORDINAL& rcCols, CComPtr& rspConvert, RowClass* pRow) { HRESULT hr = S_OK; ATLASSERT(ppBinding != NULL); T* pT = (T*) this; // *ppBinding = (void*)pT->m_rgBindings.Lookup((INT_PTR)hAccessor); T::_BindingVector::CPair* pPair = pT->m_rgBindings.Lookup( hAccessor ); if (pPair == NULL || pPair->m_value == NULL) return DB_E_BADACCESSORHANDLE; *ppBinding = pPair->m_value; rpSrcData = (void*)pT->m_rgRowData.GetRow(pRow->m_iRowset, hr); if( rpSrcData == NULL ) return hr; rpInfo = T::GetColumnInfo((T*)this, &rcCols); rspConvert = pT->m_spConvert; return S_OK; } STDMETHOD(GetData)(HROW hRow, HACCESSOR hAccessor, void *pDstData) { T* pT = (T*)this; RowClass* pRow; if (hRow == NULL ) return DB_E_BADROWHANDLE; if( !pT->m_rgRowHandles.Lookup((INT_PTR)hRow, pRow)) return DB_E_BADROWHANDLE; if (pRow == NULL) return DB_E_BADROWHANDLE; return SFTransferData (pT, true, pDstData, pRow, &(pT->m_rgRowHandles), hAccessor); } HRESULT CreateRow(DBROWOFFSET lRowsOffset, DBCOUNTITEM& cRowsObtained, HROW* rgRows) { RowClass* pRow = NULL; ATLASSERT(lRowsOffset >= 0); RowClass::KeyType key = lRowsOffset+1; ATLASSERT(key > 0); bool bFound = m_rgRowHandles.Lookup(key,pRow); if (!bFound || pRow == NULL) { ATLTRY(pRow = new RowClass(lRowsOffset)) if (pRow == NULL) return E_OUTOFMEMORY; _ATLTRY { m_rgRowHandles.SetAt(key, pRow); } _ATLCATCH( e ) { _ATLDELETEEXCEPTION( e ); delete pRow; pRow = NULL; return E_OUTOFMEMORY; } } pRow->AddRefRow(); m_bReset = false; rgRows[cRowsObtained++] = (HROW)key; return S_OK; } STDMETHOD(GetNextRows)(HCHAPTER hReserved, DBROWOFFSET lRowsOffset, DBROWCOUNT cRows, DBCOUNTITEM *pcRowsObtained, HROW **prghRows) { DBROWOFFSET lTmpRows = lRowsOffset; ATLTRACE(atlTraceDBProvider, 2, _T("IFRowsetImpl::GetNextRows\n")); T* pT = (T*) this; __if_exists(T::Fire_OnRowChange) { // Check to see if someone is in an event handler. If we do, then // we should return DB_E_NOTREENTRANT. if (!pT->IncrementMutex()) { // Note, we can't set this above this block because we may // inadvertantly reset somebody else's pcRowsObtained if (pcRowsObtained != NULL) *pcRowsObtained = 0; return DB_E_NOTREENTRANT; } else pT->DecrementMutex(); } if (pcRowsObtained != NULL) *pcRowsObtained = 0; if (prghRows == NULL || pcRowsObtained == NULL) return E_INVALIDARG; if (cRows == 0) return S_OK; HRESULT hr = S_OK; T::ObjectLock cab(pT); if (lRowsOffset < 0 && !m_bCanScrollBack) return DB_E_CANTSCROLLBACKWARDS; if (cRows < 0 ) return DB_E_CANTFETCHBACKWARDS; // Calculate # of rows in set and the base fetch position. If the rowset // is at its head position, then lRowOffset < 0 means moving from the BACK // of the rowset and not the front. if( lRowsOffset == MINLONG_PTR ) return DB_S_ENDOFROWSET; // In the case where the user is moving backwards after moving forwards, // we do not wrap around to the end of the rowset. if ((m_iRowset == 0 && !m_bReset && cRows < 0) ) return DB_S_ENDOFROWSET; // Fire events for OKTODO and ABOUTTODO after all validation has taken // place but before any permanent changes to the rowset state take place __if_exists(T::Fire_OnRowsetChange) { // Only fire these events if we're not being called by a bookmark // operation (which is why m_bExternalFetch would be set to true) if(!m_bExternalFetch) { HRESULT hrNotify = pT->Fire_OnRowsetChange(pT, DBREASON_ROWSET_FETCHPOSITIONCHANGE, DBEVENTPHASE_OKTODO, FALSE); if (hrNotify == S_FALSE) return DB_E_CANCELED; else { hrNotify = pT->Fire_OnRowsetChange(pT, DBREASON_ROWSET_FETCHPOSITIONCHANGE, DBEVENTPHASE_ABOUTTODO, FALSE); if (hrNotify == S_FALSE) return DB_E_CANCELED; else { hrNotify = pT->Fire_OnRowsetChange(pT, DBREASON_ROWSET_FETCHPOSITIONCHANGE, DBEVENTPHASE_SYNCHAFTER, FALSE); if (hrNotify == S_FALSE) return DB_E_CANCELED; } } } } // Note, if m_bReset, m_iRowset must be 0 if (lRowsOffset < 0 && m_bReset) { CPLDebug( "OGR_ATL", "Backup not supported."); return DB_E_CANTFETCHBACKWARDS; } int iStepSize = cRows >= 0 ? 1 : -1; cRows = AbsVal(cRows); lRowsOffset += m_iRowset; *pcRowsObtained = 0; CComHeapPtr rghRowsAllocated; if (*prghRows == NULL) { DBROWOFFSET cHandlesToAlloc = cRows; if (iStepSize == -1 && lRowsOffset < cHandlesToAlloc) cHandlesToAlloc = lRowsOffset; rghRowsAllocated.Allocate(cHandlesToAlloc); if(rghRowsAllocated == NULL) return E_OUTOFMEMORY; *prghRows = rghRowsAllocated; } // Check to see if we have the desired number of rows available // from the data source. int cAvailableRows; cAvailableRows = pT->m_rgRowData.CheckRows(lRowsOffset, cRows); if( cAvailableRows < cRows ) { cRows = cAvailableRows; hr = DB_S_ENDOFROWSET; } while (lRowsOffset >= 0 && cRows != 0) { // cRows > cRowsInSet && iStepSize < 0 if (lRowsOffset == 0 && cRows > 0 && iStepSize < 0) break; // in the case where we have iStepSize < 0, move the row back // further because we want the previous row DBROWOFFSET lRow = lRowsOffset; if (iStepSize < 0) lRow += iStepSize; hr = pT->CreateRow(lRow, *pcRowsObtained, *prghRows); if (FAILED(hr)) { RefRows(*pcRowsObtained, *prghRows, NULL, NULL, FALSE); for (ULONG iRowDel = 0; iRowDel < *pcRowsObtained; iRowDel++) *prghRows[iRowDel] = NULL; *pcRowsObtained = 0; return hr; } __if_exists(T::Fire_OnRowsetChange) { if (!m_bExternalFetch) pT->Fire_OnRowsetChange(pT, DBREASON_ROWSET_FETCHPOSITIONCHANGE, DBEVENTPHASE_DIDEVENT, TRUE); } cRows--; lRowsOffset += iStepSize; } // If we have multiple rows fetched, return one event, per the specification // containing all rows activated. if (*pcRowsObtained >= 1) { __if_exists(T::Fire_OnRowsetChange) { CAtlArray rgActivated; for (size_t ulActivated = 0; ulActivated < *pcRowsObtained; ulActivated++) { // This is a bit of an assumption that all newly activated // rows would have the ref count as 1. Another way to solve this // problem would be to modify the signature of CreateRow to take // a CAtlArray as a parameter and store the activated rows. RowClass* pActiveRow; if( m_rgRowHandles.Lookup((*prghRows)[ulActivated], pActiveRow ) && (pActiveRow != NULL && pActiveRow->m_dwRef == 1) ) { _ATLTRY { rgActivated.Add((*prghRows)[ulActivated]); } _ATLCATCH( e ) { _ATLDELETEEXCEPTION( e ); return E_OUTOFMEMORY; } } } if (rgActivated.GetCount() > 0) { pT->Fire_OnRowChange(pT, (DBCOUNTITEM)rgActivated.GetCount(), rgActivated.GetData(), DBREASON_ROW_ACTIVATE, DBEVENTPHASE_DIDEVENT, FALSE); } } } if ((lRowsOffset < 0 && cRows) || (lRowsOffset == 0 && cRows > 0 && iStepSize < 0)) hr = DB_S_ENDOFROWSET; m_iRowset = lRowsOffset; if (SUCCEEDED(hr)) rghRowsAllocated.Detach(); return hr; } STDMETHOD(ReleaseRows)(DBCOUNTITEM cRows, const HROW rghRows[], DBROWOPTIONS rgRowOptions[], DBREFCOUNT rgRefCounts[], DBROWSTATUS rgRowStatus[]) { ATLTRACE(atlTraceDBProvider, 2, _T("IFRowsetImpl::ReleaseRows\n")); __if_exists(T::Fire_OnRowChange) { T* pT = (T*) this; // Check to see if someone is in an event handler. If we do, then // we should return DB_E_NOTREENTRANT. if (!pT->IncrementMutex()) return DB_E_NOTREENTRANT; else pT->DecrementMutex(); } if (cRows == 0) return S_OK; rgRowOptions; return RefRows(cRows, rghRows, rgRefCounts, rgRowStatus, FALSE); } STDMETHOD(RestartPosition)(HCHAPTER /*hReserved*/) { ATLTRACE(atlTraceDBProvider, 2, _T("IFRowsetImpl::RestartPosition\n")); T* pT = (T*) this; __if_exists(T::Fire_OnRowsetChange) { // Check to see if someone is in an event handler. If we do, then // we should return DB_E_NOTREENTRANT. if (!pT->IncrementMutex()) return DB_E_NOTREENTRANT; else pT->DecrementMutex(); bool bNeedEvents = ((m_iRowset != 0 || !m_bReset)) ? true : false; // Only fire the events iff. we are actually causing a reset if (bNeedEvents) { HRESULT hrNotify = pT->Fire_OnRowsetChange(pT, DBREASON_ROWSET_FETCHPOSITIONCHANGE, DBEVENTPHASE_OKTODO, FALSE); if (hrNotify == S_FALSE) return DB_E_CANCELED; else { hrNotify = pT->Fire_OnRowsetChange(pT, DBREASON_ROWSET_FETCHPOSITIONCHANGE, DBEVENTPHASE_ABOUTTODO, FALSE); if (hrNotify == S_FALSE) return DB_E_CANCELED; else { hrNotify = pT->Fire_OnRowsetChange(pT, DBREASON_ROWSET_FETCHPOSITIONCHANGE, DBEVENTPHASE_SYNCHAFTER, FALSE); if (hrNotify == S_FALSE) return DB_E_CANCELED; } } } } // Check to see if DBPROP_CANHOLDROWS is set to false. In this case, // return a DB_E_ROWSNOTRELEASED. CComVariant varHoldRows; HRESULT hr = pT->GetPropValue(&DBPROPSET_ROWSET, DBPROP_CANHOLDROWS, &varHoldRows); if (FAILED(hr) || varHoldRows.boolVal == ATL_VARIANT_FALSE) { if (m_rgRowHandles.GetCount() > 0) { RowClass* pRow = NULL; POSITION pos = pT->m_rgRowHandles.GetStartPosition(); while (pos != NULL) { MapClass::CPair* pPair = pT->m_rgRowHandles.GetNext(pos); ATLASSERT( pPair != NULL ); HROW hRow = pPair->m_key; bool bFound = pT->m_rgRowHandles.Lookup(hRow, pRow); if (bFound && pRow != NULL && pRow->m_status != DBPENDINGSTATUS_UNCHANGED) { __if_exists(T::Fire_OnRowsetChange) { if (bNeedEvents) { pT->Fire_OnRowsetChange(pT, DBREASON_ROWSET_FETCHPOSITIONCHANGE, DBEVENTPHASE_FAILEDTODO, TRUE); } } return DB_E_ROWSNOTRELEASED; } } } } m_iRowset = 0; m_bReset = true; __if_exists(T::Fire_OnRowsetChange) { // listener must comply so blow off ret val. if (bNeedEvents) pT->Fire_OnRowsetChange(pT, DBREASON_ROWSET_FETCHPOSITIONCHANGE, DBEVENTPHASE_DIDEVENT, TRUE); } return S_OK; } MapClass m_rgRowHandles; DBROWOFFSET m_iRowset; // cursor unsigned m_bCanScrollBack:1; unsigned m_bCanFetchBack:1; unsigned m_bRemoveDeleted:1; // DBPROP_REMOVEDELETED unsigned m_bIRowsetUpdate:1; // DBPROP_IRowsetUpdate unsigned m_bReset:1; unsigned m_bExternalFetch:1; }; #endif // ifndef _IFRowsetImpl_INCLUDED