// *************************************************************************** // Copyright (C) 2000- Microsoft Corporation. // @File: sqlconnect.cpp // // PURPOSE: // // Handle the OLEDB connection and commands // // NOTES: // // Extern dependencies: // provision of "_Module" and the COM guids.... // // // HISTORY: // // @Version: Whistler/Shiloh // 66601 srs 10/05/00 NTSNAP improvements // // // @EndHeader@ // *************************************************************************** // the templates make awful, long names which result in excessive warnings // #ifdef HIDE_WARNINGS #pragma warning( disable : 4663) #pragma warning( disable : 4786) #pragma warning( disable : 4100) #pragma warning( disable : 4511) #endif #include #include #include //////////////////////////////////////////////////////////////////////// // Standard foo for file name aliasing. This code block must be after // all includes of VSS header files. // #ifdef VSS_FILE_ALIAS #undef VSS_FILE_ALIAS #endif #define VSS_FILE_ALIAS "SQLCONNC" // //////////////////////////////////////////////////////////////////////// //--------------------------------------------------------------------------------------- // routine to print out error information for a failed OLEDB request // // An optional parm is used to check for the 3014 code when a successful backup is // erroneously marked as failed due to other problems (such as msdb access) // void DumpErrorInfo ( IUnknown* pObjectWithError, REFIID IID_InterfaceWithError, CLogMsg &msg, BOOL* pBackupSuccess = NULL ) { CVssFunctionTracer ft(VSSDBG_SQLLIB, L"DumpErrorInfo"); CComPtr apIErrorInfoAll; CComPtr apIErrorInfoRecord; CComPtr apIErrorRecords; CComPtr apISupportErrorInfo; CComPtr apISQLErrorInfo; CComPtr apISQLServerErrorInfo; // Number of error records. ULONG nRecs; ULONG nRec; // Basic error information from GetBasicErrorInfo. ERRORINFO errorinfo; // IErrorInfo values. CComBSTR bstrDescription; CComBSTR bstrSource; // ISQLErrorInfo parameters. CComBSTR bstrSQLSTATE; LONG lNativeError; // ISQLServerErrorInfo parameter pointers. SSERRORINFO* pSSErrorInfo = NULL; LPWSTR pSSErrorStrings = NULL; // Hard-code an American English locale for the example. // // **UNDONE** How should we internationalize properly? // DWORD MYLOCALEID = 0x0409; BOOL msg3014seen = FALSE; BOOL msg3013seen = FALSE; WCHAR buf[80]; // Only ask for error information if the interface supports // it. if (FAILED(pObjectWithError->QueryInterface ( IID_ISupportErrorInfo, (void**) &apISupportErrorInfo) )) { ft.Trace (VSSDBG_SQLLIB, L"SupportErrorErrorInfo interface not supported"); return; } if (FAILED(apISupportErrorInfo->InterfaceSupportsErrorInfo(IID_InterfaceWithError))) { ft.Trace (VSSDBG_SQLLIB, L"InterfaceWithError interface not supported"); return; } // Do not test the return of GetErrorInfo. It can succeed and return // a NULL pointer in pIErrorInfoAll. Simply test the pointer. GetErrorInfo(0, &apIErrorInfoAll); if (apIErrorInfoAll != NULL) { // Test to see if it's a valid OLE DB IErrorInfo interface // exposing a list of records. if (SUCCEEDED(apIErrorInfoAll->QueryInterface ( IID_IErrorRecords, (void**) &apIErrorRecords))) { apIErrorRecords->GetRecordCount(&nRecs); // Within each record, retrieve information from each // of the defined interfaces. for (nRec = 0; nRec < nRecs; nRec++) { // From IErrorRecords, get the HRESULT and a reference // to the ISQLErrorInfo interface. apIErrorRecords->GetBasicErrorInfo(nRec, &errorinfo); apIErrorRecords->GetCustomErrorObject ( nRec, IID_ISQLErrorInfo, (IUnknown**) &apISQLErrorInfo); // Display the HRESULT, then use the ISQLErrorInfo. ft.Trace(VSSDBG_SQLLIB, L"HRESULT:\t%#X\n", errorinfo.hrError); if (apISQLErrorInfo != NULL) { apISQLErrorInfo->GetSQLInfo(&bstrSQLSTATE, &lNativeError); // Display the SQLSTATE and native error values. ft.Trace( VSSDBG_SQLLIB, L"SQLSTATE:\t%s\nNative Error:\t%ld\n", bstrSQLSTATE, lNativeError); msg.Add(L"SQLSTATE: "); msg.Add(bstrSQLSTATE); swprintf(buf, L", Native Error: %d\n", lNativeError); msg.Add(buf); if (lNativeError == 3013) msg3013seen = TRUE; else if (lNativeError == 3014) msg3014seen = TRUE; // Get the ISQLServerErrorInfo interface from // ISQLErrorInfo before releasing the reference. apISQLErrorInfo->QueryInterface ( IID_ISQLServerErrorInfo, (void**) &apISQLServerErrorInfo); // Test to ensure the reference is valid, then // get error information from ISQLServerErrorInfo. if (apISQLServerErrorInfo != NULL) { apISQLServerErrorInfo->GetErrorInfo ( &pSSErrorInfo, &pSSErrorStrings); // ISQLServerErrorInfo::GetErrorInfo succeeds // even when it has nothing to return. Test the // pointers before using. if (pSSErrorInfo) { // Display the state and severity from the // returned information. The error message comes // from IErrorInfo::GetDescription. ft.Trace ( VSSDBG_SQLLIB, L"Error state:\t%d\nSeverity:\t%d\n", pSSErrorInfo->bState, pSSErrorInfo->bClass ); swprintf(buf, L"Error state: %d, Severity: %d\n",pSSErrorInfo->bState, pSSErrorInfo->bClass); msg.Add(buf); // IMalloc::Free needed to release references // on returned values. For the example, assume // the g_pIMalloc pointer is valid. g_pIMalloc->Free(pSSErrorStrings); g_pIMalloc->Free(pSSErrorInfo); } apISQLServerErrorInfo.Release (); } apISQLErrorInfo.Release (); } // got the custom error info if (SUCCEEDED(apIErrorRecords->GetErrorInfo ( nRec, MYLOCALEID, &apIErrorInfoRecord))) { // Get the source and description (error message) // from the record's IErrorInfo. apIErrorInfoRecord->GetSource(&bstrSource); apIErrorInfoRecord->GetDescription(&bstrDescription); if (bstrSource != NULL) { ft.Trace(VSSDBG_SQLLIB, L"Source:\t\t%s\n", bstrSource); msg.Add(L"Source: "); msg.Add(bstrSource); msg.Add(L"\n"); } if (bstrDescription != NULL) { ft.Trace(VSSDBG_SQLLIB, L"Error message:\t%s\n", bstrDescription); msg.Add(L"Error message: "); msg.Add(bstrDescription); msg.Add(L"\n"); } apIErrorInfoRecord.Release (); } } // for each record } else { // IErrorInfo is valid; get the source and // description to see what it is. apIErrorInfoAll->GetSource(&bstrSource); apIErrorInfoAll->GetDescription(&bstrDescription); if (bstrSource != NULL) { ft.Trace(VSSDBG_SQLLIB, L"Source:\t\t%s\n", bstrSource); msg.Add(L"Source: "); msg.Add(bstrSource); msg.Add(L"\n"); } if (bstrDescription != NULL) { ft.Trace(VSSDBG_SQLLIB, L"Error message:\t%s\n", bstrDescription); msg.Add(L"Error message: "); msg.Add(bstrDescription); msg.Add(L"\n"); } } } else { ft.Trace(VSSDBG_SQLLIB, L"GetErrorInfo failed."); } if (pBackupSuccess) { *pBackupSuccess = (msg3014seen && !msg3013seen); } } //------------------------------------------------------------------ // SqlConnection::~SqlConnection () { CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConnection::~SqlConnection"); Disconnect (); } //------------------------------------------------------------------ // void SqlConnection::ReleaseRowset () { CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConnection::ReleaseRowset"); if (m_pBuffer) { delete[] m_pBuffer; m_pBuffer = NULL; } if (m_pBindings) { delete[] m_pBindings; m_pBindings = NULL; } m_cBindings = 0; if (m_pAccessor) { if (m_hAcc) { m_pAccessor->ReleaseAccessor (m_hAcc, NULL); m_hAcc = NULL; } m_pAccessor->Release (); m_pAccessor = NULL; } if (m_pRowset) { m_pRowset->Release (); m_pRowset = NULL; } } //------------------------------------------------------------------ // void SqlConnection::Disconnect () { CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConenction::Disconnect"); ReleaseRowset (); if (m_pCommand) { m_pCommand->Release (); m_pCommand = NULL; } if (m_pCommandFactory) { m_pCommandFactory->Release (); m_pCommandFactory = NULL; } } // log an error if not an out of resource error void SqlConnection::LogOledbError ( CVssFunctionTracer &ft, CVssDebugInfo &dbgInfo, LPCWSTR wszRoutine, CLogMsg &msg ) { if (ft.hr == E_OUTOFMEMORY || ft.hr == HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY) || ft.hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_SEARCH_HANDLES) || ft.hr == HRESULT_FROM_WIN32(ERROR_NO_LOG_SPACE) || ft.hr == HRESULT_FROM_WIN32(ERROR_DISK_FULL) || ft.hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_USER_HANDLES)) ft.Throw(dbgInfo, E_OUTOFMEMORY, L"Out of memory detected in function %s", wszRoutine); else { ft.LogError(VSS_ERROR_SQLLIB_OLEDB_ERROR, dbgInfo << wszRoutine << ft.hr << msg.GetMsg()); ft.Throw ( dbgInfo, E_UNEXPECTED, L"Error calling %s. hr = 0x%08lx.\n%s", wszRoutine, ft.hr, msg.GetMsg() ); } } //------------------------------------------------------------------ // Setup a session, ready to receive commands. // // This call may block for a long time while establishing the connection. // // See the "FrozenServer" object for a method to determine if the local // server is up or not prior to requesting a connection. // // The "trick" of prepending "tcp:" to the servername isn't fast or robust // enough to detect a shutdown server. // // Note for C programmers....BSTRs are used as part of the COM // environment to be interoperable with VisualBasic. The VARIANT // datatype doesn't work with standard C strings. // void SqlConnection::Connect ( const WString& serverName) { CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConnection::Connect"); CLogMsg msg; CComPtr apdbInitialize; // "Connect" always implies a "fresh" connection. // ReleaseRowset (); if (m_ServerName.compare (serverName) == 0 && m_pCommand) { // Requesting the same server and we are connected. // return; } Disconnect (); m_ServerName = serverName; ft.CoCreateInstanceWithLog( VSSDBG_SQLLIB, CLSID_SQLOLEDB, L"SQLOLEDB", CLSCTX_INPROC_SERVER, IID_IDBInitialize, (IUnknown**)&(apdbInitialize)); if (ft.HrFailed()) ft.CheckForError(VSSDBG_SQLLIB, L"CoCreateInstance"); CComPtr apdbProperties; ft.hr = apdbInitialize->QueryInterface(IID_IDBProperties, (void **) &apdbProperties); if (ft.HrFailed()) ft.CheckForError(VSSDBG_SQLLIB, L"IDBInitialize::QueryInterface"); CComBSTR bstrComputerName = serverName.c_str (); // initial database context CComBSTR bstrDatabaseName = L"master"; // use NT Authentication CComBSTR bstrSSPI = L"SSPI"; const unsigned x_CPROP = 3; DBPROPSET propset; DBPROP rgprop[x_CPROP]; propset.guidPropertySet = DBPROPSET_DBINIT; propset.cProperties = x_CPROP; propset.rgProperties = rgprop; for (unsigned i = 0; i < x_CPROP; i++) { VariantInit(&rgprop[i].vValue); rgprop[i].dwOptions = DBPROPOPTIONS_REQUIRED; rgprop[i].colid = DB_NULLID; rgprop[i].vValue.vt = VT_BSTR; } rgprop[0].dwPropertyID = DBPROP_INIT_DATASOURCE; rgprop[1].dwPropertyID = DBPROP_INIT_CATALOG; rgprop[2].dwPropertyID = DBPROP_AUTH_INTEGRATED; rgprop[0].vValue.bstrVal = bstrComputerName; rgprop[1].vValue.bstrVal = bstrDatabaseName; rgprop[2].vValue.bstrVal = bstrSSPI; ft.hr = apdbProperties->SetProperties(1, &propset); if (ft.HrFailed()) { DumpErrorInfo(apdbProperties, IID_IDBProperties, msg); LogOledbError(ft, VSSDBG_SQLLIB, L"IDBProperties::SetProperties", msg); } ft.Trace(VSSDBG_SQLLIB, L"Connecting to server %s...", serverName.c_str ()); ft.hr = apdbInitialize->Initialize(); if (ft.HrFailed()) { DumpErrorInfo(apdbInitialize, IID_IDBInitialize, msg); LogOledbError(ft, VSSDBG_SQLLIB, L"IDBInitialize::Initialize", msg); } CComPtr apCreateSession; ft.hr = apdbInitialize->QueryInterface(IID_IDBCreateSession, (void **) &apCreateSession); if (ft.HrFailed()) { DumpErrorInfo(apdbInitialize, IID_IDBInitialize, msg); LogOledbError(ft, VSSDBG_SQLLIB, L"IDBInitialize::QueryInterface", msg); } // We keep the command factory around to generate commands. // ft.hr = apCreateSession->CreateSession ( NULL, IID_IDBCreateCommand, (IUnknown **) &m_pCommandFactory); if (ft.HrFailed()) { DumpErrorInfo(apCreateSession, IID_IDBCreateSession, msg); LogOledbError(ft, VSSDBG_SQLLIB, L"IDBCreateSession::CreateSession", msg); } ft.Trace(VSSDBG_SQLLIB, L"Connected\n"); // Request the version of this server // DBPROPIDSET versionSet; DBPROPID versionID = DBPROP_DBMSVER; versionSet.guidPropertySet = DBPROPSET_DATASOURCEINFO; versionSet.cPropertyIDs = 1; versionSet.rgPropertyIDs = &versionID; ULONG propCount; DBPROPSET* pPropSet; ft.hr = apdbProperties->GetProperties (1, &versionSet, &propCount, &pPropSet); if (ft.HrFailed()) { DumpErrorInfo(apdbProperties, IID_IDBProperties, msg); LogOledbError(ft, VSSDBG_SQLLIB, L"IDBProperties::GetProperties", msg); } ft.Trace(VSSDBG_SQLLIB, L"Version: %s\n", pPropSet->rgProperties->vValue.bstrVal); swscanf (pPropSet->rgProperties->vValue.bstrVal, L"%d", &m_ServerVersion); g_pIMalloc->Free(pPropSet->rgProperties); g_pIMalloc->Free(pPropSet); } //--------------------------------------------------------------------- // Setup the command with some SQL text // void SqlConnection::SetCommand (const WString& command) { CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConnection::SetCommand"); CLogMsg msg; // Release the result of the previous command // ReleaseRowset (); // We create the command on the first request, then keep only one // around in the SqlConnection. // if (!m_pCommand) { ft.hr = m_pCommandFactory->CreateCommand (NULL, IID_ICommandText, (IUnknown **) &m_pCommand); if (ft.HrFailed()) { DumpErrorInfo(m_pCommandFactory, IID_IDBCreateCommand, msg); LogOledbError(ft, VSSDBG_SQLLIB, L"IDBCreateCommand::CreateCommand", msg); } } ft.hr = m_pCommand->SetCommandText(DBGUID_DBSQL, command.c_str ()); if (ft.HrFailed()) { DumpErrorInfo (m_pCommand, IID_ICommandText, msg); LogOledbError(ft, VSSDBG_SQLLIB, L"ICommandText::SetCommandText", msg); } ft.Trace (VSSDBG_SQLLIB, L"SetCommand (%s)\n", command.c_str ()); } //--------------------------------------------------------------------- // Execute the command. "SetCommand" must have been called previously. // BOOL SqlConnection::ExecCommand () { CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConnection::ExecCommand"); CLogMsg msg; CComPtr apRowset; DBROWCOUNT crows; HRESULT hr; // Release the result of the previous command // ReleaseRowset (); ft.hr = m_pCommand->Execute ( NULL, IID_IRowset, NULL, &crows, (IUnknown **) &m_pRowset); if (ft.HrFailed()) { BOOL backupSuccess = FALSE; DumpErrorInfo (m_pCommand, IID_ICommandText, msg, &backupSuccess); if (!backupSuccess) LogOledbError(ft, VSSDBG_SQLLIB, L"ICommandText::Execute", msg); } if (!m_pRowset) { ft.Trace(VSSDBG_SQLLIB, L"Command completed successfully with no rowset\n"); return FALSE; } return TRUE; } //--------------------------------------------------------------------- // return a vector of string, one for each row of the output. // The query should have returned a single column. // StringVector* SqlConnection::GetStringColumn () { CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConnection::GetStringColumn"); CLogMsg msg; //ASSERT (m_pRowset) // CComPtr apColumnsInfo; ft.hr = m_pRowset->QueryInterface(IID_IColumnsInfo, (void **) &apColumnsInfo); if (ft.HrFailed()) { DumpErrorInfo (m_pRowset, IID_IRowset, msg); LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::QueryInterface", msg); } // get columns info // DBCOLUMNINFO *rgColumnInfo; DBORDINAL cColumns; WCHAR *wszColumnInfo; ft.hr = apColumnsInfo->GetColumnInfo(&cColumns, &rgColumnInfo, &wszColumnInfo); if (ft.HrFailed()) { DumpErrorInfo (apColumnsInfo, IID_IColumnsInfo, msg); LogOledbError(ft, VSSDBG_SQLLIB, L"IColumnsInfo::GetColumnInfo", msg); } // Auto objects ensure that memory is freed on exit // CAutoTask argColumnInfo = rgColumnInfo; CAutoTask awszColumnInfo = wszColumnInfo; // Setup a buffer to hold the string. // The output buffer holds a 4byte length, followed by the string column. // // "bufferSize" is in units of characters (not bytes) // Note that the "ulColumnSize" is in characters. // 1 char is used for the null term and we actually allocate one additional // char (hidden), just incase our provider gets the boundary condition wrong. // ULONG bufferSize = 1 + rgColumnInfo[0].ulColumnSize + (sizeof(ULONG)/sizeof(WCHAR)); std::auto_ptr rowBuffer(new WCHAR[bufferSize+1]); // Describe the binding for our single column of interest // DBBINDING rgbind[1]; unsigned cBindings = 1; rgbind[0].dwPart = DBPART_VALUE|DBPART_LENGTH; rgbind[0].wType = DBTYPE_WSTR; // retrieve a rgbind[0].dwMemOwner = DBMEMOWNER_CLIENTOWNED; rgbind[0].eParamIO = DBPARAMIO_NOTPARAM; rgbind[0].pObject = NULL; rgbind[0].pBindExt = NULL; rgbind[0].pTypeInfo = NULL; rgbind[0].dwFlags = 0; rgbind[0].obLength = 0; // offset to the length field rgbind[0].iOrdinal = 1; // column id's start at 1 rgbind[0].obValue = sizeof(ULONG); // offset to the string field rgbind[0].cbMaxLen = (unsigned) (bufferSize*sizeof(WCHAR)-sizeof(ULONG)); CComPtr apAccessor; ft.hr = m_pRowset->QueryInterface(IID_IAccessor, (void **) &apAccessor); if (ft.HrFailed()) { DumpErrorInfo (m_pRowset, IID_IRowset, msg); LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::QueryInterface", msg); } HACCESSOR hacc; ft.hr = apAccessor->CreateAccessor ( DBACCESSOR_ROWDATA, cBindings, rgbind, 0, &hacc, NULL); if (ft.HrFailed()) { DumpErrorInfo(apAccessor, IID_IAccessor, msg); LogOledbError(ft, VSSDBG_SQLLIB, L"IAccessor::CreateAccessor", msg); } // loop through rows, generating a vector of strings. // HROW hrow; HROW *rghrow = &hrow; DBCOUNTITEM crow; std::auto_ptr aVec (new StringVector); // pString points into the output buffer for the string column // WCHAR* pString = (WCHAR*)((BYTE*)rowBuffer.get () + sizeof (ULONG)); while(TRUE) { ft.hr = m_pRowset->GetNextRows(NULL, 0, 1, &crow, &rghrow); if (ft.hr == DB_S_ENDOFROWSET) break; if (ft.HrFailed()) { DumpErrorInfo (m_pRowset, IID_IRowset, msg); LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::GetNextRows", msg); } ft.hr = m_pRowset->GetData (hrow, hacc, rowBuffer.get()); if (ft.HrFailed()) { DumpErrorInfo(m_pRowset, IID_IRowset, msg); m_pRowset->ReleaseRows (1, rghrow, NULL, NULL, NULL); LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::GetData", msg); } unsigned tempChars = (*(ULONG*)rowBuffer.get ())/sizeof (WCHAR); WString tempStr (pString, tempChars); aVec->push_back (tempStr); ft.Trace(VSSDBG_SQLLIB, L"StringColumn: %s\n", tempStr.c_str ()); m_pRowset->ReleaseRows(1, rghrow, NULL, NULL, NULL); } // UNDONE...make this an auto object to avoid leaks // apAccessor->ReleaseAccessor (hacc, NULL); return aVec.release (); } //--------------------------------------------------------------------- // Fetch the first row of the result. // BOOL SqlConnection::FetchFirst () { CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConnection::FetchFirst"); CLogMsg msg; // UNDONE...make this nicely restep back to the first row. // if (m_pBindings) { throw HRESULT(E_SQLLIB_PROTO); } CComPtr apColumnsInfo; ft.hr = m_pRowset->QueryInterface(IID_IColumnsInfo, (void **) &apColumnsInfo); if (ft.HrFailed()) { DumpErrorInfo (m_pRowset, IID_IRowset, msg); LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::QueryInteface", msg); } // get columns info // DBCOLUMNINFO *rgColumnInfo; DBORDINAL cColumns; WCHAR *wszColumnInfo; ft.hr = apColumnsInfo->GetColumnInfo(&cColumns, &rgColumnInfo, &wszColumnInfo); if (ft.HrFailed()) { DumpErrorInfo (apColumnsInfo, IID_IColumnsInfo, msg); LogOledbError(ft, VSSDBG_SQLLIB, L"IColumnsInfo::GetColumnInfo", msg); } // Auto objects ensure that memory is freed on exit // CAutoTask argColumnInfo = rgColumnInfo; CAutoTask awszColumnInfo = wszColumnInfo; // allocate bindings unsigned m_cBindings = (unsigned) cColumns; m_pBindings = new DBBINDING[m_cBindings]; // Set up the bindings onto a buffer we'll allocate // UNDONE: do this properly for alignment & handling null indicators // unsigned cb = 0; for (unsigned icol = 0; icol < m_cBindings; icol++) { unsigned maxBytes; m_pBindings[icol].iOrdinal = icol + 1; m_pBindings[icol].dwMemOwner = DBMEMOWNER_CLIENTOWNED; m_pBindings[icol].eParamIO = DBPARAMIO_NOTPARAM; m_pBindings[icol].pObject = NULL; m_pBindings[icol].pBindExt = NULL; m_pBindings[icol].pTypeInfo = NULL; m_pBindings[icol].dwFlags = 0; m_pBindings[icol].bPrecision = rgColumnInfo[icol].bPrecision; m_pBindings[icol].bScale = rgColumnInfo[icol].bScale; m_pBindings[icol].obStatus = 0; // no status info if (rgColumnInfo[icol].wType == DBTYPE_WSTR) { // do we need the length? m_pBindings[icol].dwPart = DBPART_VALUE; //|DBPART_LENGTH; m_pBindings[icol].wType = DBTYPE_WSTR; m_pBindings[icol].obLength = 0; //icol * sizeof(DBLENGTH); maxBytes = rgColumnInfo[icol].ulColumnSize * sizeof(WCHAR); } else { m_pBindings[icol].dwPart = DBPART_VALUE; m_pBindings[icol].wType = rgColumnInfo[icol].wType; m_pBindings[icol].obLength = 0; // no length maxBytes = rgColumnInfo[icol].ulColumnSize; } m_pBindings[icol].obValue = cb; m_pBindings[icol].cbMaxLen = maxBytes; cb += maxBytes; } // allocate data buffer // m_pBuffer = new BYTE[cb]; m_BufferSize = cb; ft.hr = m_pRowset->QueryInterface(IID_IAccessor, (void **) &m_pAccessor); if (ft.HrFailed()) { DumpErrorInfo (m_pRowset, IID_IRowset, msg); LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::QueryInterface", msg); } ft.hr = m_pAccessor->CreateAccessor ( DBACCESSOR_ROWDATA, m_cBindings, m_pBindings, 0, &m_hAcc, NULL); if (ft.HrFailed()) { DumpErrorInfo(m_pAccessor, IID_IAccessor, msg); LogOledbError(ft, VSSDBG_SQLLIB, L"IAccessor::CreateAccessor", msg); } // Fetch the first row // HROW hrow; HROW *rghrow = &hrow; DBCOUNTITEM crow; ft.hr = m_pRowset->GetNextRows(NULL, 0, 1, &crow, &rghrow); if (ft.hr == DB_S_ENDOFROWSET) { // No rows in this set // return FALSE; } if (ft.HrFailed()) { DumpErrorInfo (m_pRowset, IID_IRowset, msg); LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::GetNextRows", msg); } ft.hr = m_pRowset->GetData (hrow, m_hAcc, m_pBuffer); if (ft.HrFailed()) { DumpErrorInfo(m_pRowset, IID_IRowset, msg); m_pRowset->ReleaseRows (1, rghrow, NULL, NULL, NULL); LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::GetData", msg); } m_pRowset->ReleaseRows(1, rghrow, NULL, NULL, NULL); return TRUE; } //--------------------------------------------------------------------- // Fetch the next row of the result. // BOOL SqlConnection::FetchNext () { CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConnection::FetchNext"); HROW hrow; HROW *rghrow = &hrow; DBCOUNTITEM crow; CLogMsg msg; ft.hr = m_pRowset->GetNextRows(NULL, 0, 1, &crow, &rghrow); if (ft.hr == DB_S_ENDOFROWSET) { return FALSE; } if (ft.HrFailed()) { DumpErrorInfo (m_pRowset, IID_IRowset, msg); LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::GetNextRows", msg); } ft.hr = m_pRowset->GetData (hrow, m_hAcc, m_pBuffer); if (ft.HrFailed()) { DumpErrorInfo(m_pRowset, IID_IRowset, msg); m_pRowset->ReleaseRows (1, rghrow, NULL, NULL, NULL); LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::GetData", msg); } m_pRowset->ReleaseRows(1, rghrow, NULL, NULL, NULL); return TRUE; } //----------------------------------------------------------- // Provide a pointer to the n'th column. // void* SqlConnection::AccessColumn (int colid) { return m_pBuffer + m_pBindings[colid-1].obValue; }