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.
941 lines
26 KiB
941 lines
26 KiB
// ***************************************************************************
|
|
// 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 <stdafx.h>
|
|
#include <atlbase.h>
|
|
#include <clogmsg.h>
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// 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<IErrorInfo> apIErrorInfoAll;
|
|
CComPtr<IErrorInfo> apIErrorInfoRecord;
|
|
CComPtr<IErrorRecords> apIErrorRecords;
|
|
CComPtr<ISupportErrorInfo> apISupportErrorInfo;
|
|
CComPtr<ISQLErrorInfo> apISQLErrorInfo;
|
|
CComPtr<ISQLServerErrorInfo> 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<IDBInitialize> 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<IDBProperties> 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<IDBCreateSession> 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<IRowset> 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<IColumnsInfo> 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<DBCOLUMNINFO> argColumnInfo = rgColumnInfo;
|
|
CAutoTask<WCHAR> 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<WCHAR> 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<IAccessor> 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<StringVector> 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<IColumnsInfo> 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<DBCOLUMNINFO> argColumnInfo = rgColumnInfo;
|
|
CAutoTask<WCHAR> 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;
|
|
}
|