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

941 lines
26 KiB

  1. // ***************************************************************************
  2. // Copyright (C) 2000- Microsoft Corporation.
  3. // @File: sqlconnect.cpp
  4. //
  5. // PURPOSE:
  6. //
  7. // Handle the OLEDB connection and commands
  8. //
  9. // NOTES:
  10. //
  11. // Extern dependencies:
  12. // provision of "_Module" and the COM guids....
  13. //
  14. //
  15. // HISTORY:
  16. //
  17. // @Version: Whistler/Shiloh
  18. // 66601 srs 10/05/00 NTSNAP improvements
  19. //
  20. //
  21. // @EndHeader@
  22. // ***************************************************************************
  23. // the templates make awful, long names which result in excessive warnings
  24. //
  25. #ifdef HIDE_WARNINGS
  26. #pragma warning( disable : 4663)
  27. #pragma warning( disable : 4786)
  28. #pragma warning( disable : 4100)
  29. #pragma warning( disable : 4511)
  30. #endif
  31. #include <stdafx.h>
  32. #include <atlbase.h>
  33. #include <clogmsg.h>
  34. ////////////////////////////////////////////////////////////////////////
  35. // Standard foo for file name aliasing. This code block must be after
  36. // all includes of VSS header files.
  37. //
  38. #ifdef VSS_FILE_ALIAS
  39. #undef VSS_FILE_ALIAS
  40. #endif
  41. #define VSS_FILE_ALIAS "SQLCONNC"
  42. //
  43. ////////////////////////////////////////////////////////////////////////
  44. //---------------------------------------------------------------------------------------
  45. // routine to print out error information for a failed OLEDB request
  46. //
  47. // An optional parm is used to check for the 3014 code when a successful backup is
  48. // erroneously marked as failed due to other problems (such as msdb access)
  49. //
  50. void DumpErrorInfo (
  51. IUnknown* pObjectWithError,
  52. REFIID IID_InterfaceWithError,
  53. CLogMsg &msg,
  54. BOOL* pBackupSuccess = NULL
  55. )
  56. {
  57. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"DumpErrorInfo");
  58. CComPtr<IErrorInfo> apIErrorInfoAll;
  59. CComPtr<IErrorInfo> apIErrorInfoRecord;
  60. CComPtr<IErrorRecords> apIErrorRecords;
  61. CComPtr<ISupportErrorInfo> apISupportErrorInfo;
  62. CComPtr<ISQLErrorInfo> apISQLErrorInfo;
  63. CComPtr<ISQLServerErrorInfo> apISQLServerErrorInfo;
  64. // Number of error records.
  65. ULONG nRecs;
  66. ULONG nRec;
  67. // Basic error information from GetBasicErrorInfo.
  68. ERRORINFO errorinfo;
  69. // IErrorInfo values.
  70. CComBSTR bstrDescription;
  71. CComBSTR bstrSource;
  72. // ISQLErrorInfo parameters.
  73. CComBSTR bstrSQLSTATE;
  74. LONG lNativeError;
  75. // ISQLServerErrorInfo parameter pointers.
  76. SSERRORINFO* pSSErrorInfo = NULL;
  77. LPWSTR pSSErrorStrings = NULL;
  78. // Hard-code an American English locale for the example.
  79. //
  80. // **UNDONE** How should we internationalize properly?
  81. //
  82. DWORD MYLOCALEID = 0x0409;
  83. BOOL msg3014seen = FALSE;
  84. BOOL msg3013seen = FALSE;
  85. WCHAR buf[80];
  86. // Only ask for error information if the interface supports
  87. // it.
  88. if (FAILED(pObjectWithError->QueryInterface
  89. (
  90. IID_ISupportErrorInfo,
  91. (void**) &apISupportErrorInfo)
  92. ))
  93. {
  94. ft.Trace (VSSDBG_SQLLIB, L"SupportErrorErrorInfo interface not supported");
  95. return;
  96. }
  97. if (FAILED(apISupportErrorInfo->InterfaceSupportsErrorInfo(IID_InterfaceWithError)))
  98. {
  99. ft.Trace (VSSDBG_SQLLIB, L"InterfaceWithError interface not supported");
  100. return;
  101. }
  102. // Do not test the return of GetErrorInfo. It can succeed and return
  103. // a NULL pointer in pIErrorInfoAll. Simply test the pointer.
  104. GetErrorInfo(0, &apIErrorInfoAll);
  105. if (apIErrorInfoAll != NULL)
  106. {
  107. // Test to see if it's a valid OLE DB IErrorInfo interface
  108. // exposing a list of records.
  109. if (SUCCEEDED(apIErrorInfoAll->QueryInterface (
  110. IID_IErrorRecords,
  111. (void**) &apIErrorRecords)))
  112. {
  113. apIErrorRecords->GetRecordCount(&nRecs);
  114. // Within each record, retrieve information from each
  115. // of the defined interfaces.
  116. for (nRec = 0; nRec < nRecs; nRec++)
  117. {
  118. // From IErrorRecords, get the HRESULT and a reference
  119. // to the ISQLErrorInfo interface.
  120. apIErrorRecords->GetBasicErrorInfo(nRec, &errorinfo);
  121. apIErrorRecords->GetCustomErrorObject (
  122. nRec,
  123. IID_ISQLErrorInfo,
  124. (IUnknown**) &apISQLErrorInfo);
  125. // Display the HRESULT, then use the ISQLErrorInfo.
  126. ft.Trace(VSSDBG_SQLLIB, L"HRESULT:\t%#X\n", errorinfo.hrError);
  127. if (apISQLErrorInfo != NULL)
  128. {
  129. apISQLErrorInfo->GetSQLInfo(&bstrSQLSTATE, &lNativeError);
  130. // Display the SQLSTATE and native error values.
  131. ft.Trace(
  132. VSSDBG_SQLLIB,
  133. L"SQLSTATE:\t%s\nNative Error:\t%ld\n",
  134. bstrSQLSTATE,
  135. lNativeError);
  136. msg.Add(L"SQLSTATE: ");
  137. msg.Add(bstrSQLSTATE);
  138. swprintf(buf, L", Native Error: %d\n", lNativeError);
  139. msg.Add(buf);
  140. if (lNativeError == 3013)
  141. msg3013seen = TRUE;
  142. else if (lNativeError == 3014)
  143. msg3014seen = TRUE;
  144. // Get the ISQLServerErrorInfo interface from
  145. // ISQLErrorInfo before releasing the reference.
  146. apISQLErrorInfo->QueryInterface (
  147. IID_ISQLServerErrorInfo,
  148. (void**) &apISQLServerErrorInfo);
  149. // Test to ensure the reference is valid, then
  150. // get error information from ISQLServerErrorInfo.
  151. if (apISQLServerErrorInfo != NULL)
  152. {
  153. apISQLServerErrorInfo->GetErrorInfo (
  154. &pSSErrorInfo,
  155. &pSSErrorStrings);
  156. // ISQLServerErrorInfo::GetErrorInfo succeeds
  157. // even when it has nothing to return. Test the
  158. // pointers before using.
  159. if (pSSErrorInfo)
  160. {
  161. // Display the state and severity from the
  162. // returned information. The error message comes
  163. // from IErrorInfo::GetDescription.
  164. ft.Trace
  165. (
  166. VSSDBG_SQLLIB,
  167. L"Error state:\t%d\nSeverity:\t%d\n",
  168. pSSErrorInfo->bState,
  169. pSSErrorInfo->bClass
  170. );
  171. swprintf(buf, L"Error state: %d, Severity: %d\n",pSSErrorInfo->bState, pSSErrorInfo->bClass);
  172. msg.Add(buf);
  173. // IMalloc::Free needed to release references
  174. // on returned values. For the example, assume
  175. // the g_pIMalloc pointer is valid.
  176. g_pIMalloc->Free(pSSErrorStrings);
  177. g_pIMalloc->Free(pSSErrorInfo);
  178. }
  179. apISQLServerErrorInfo.Release ();
  180. }
  181. apISQLErrorInfo.Release ();
  182. } // got the custom error info
  183. if (SUCCEEDED(apIErrorRecords->GetErrorInfo (
  184. nRec,
  185. MYLOCALEID,
  186. &apIErrorInfoRecord)))
  187. {
  188. // Get the source and description (error message)
  189. // from the record's IErrorInfo.
  190. apIErrorInfoRecord->GetSource(&bstrSource);
  191. apIErrorInfoRecord->GetDescription(&bstrDescription);
  192. if (bstrSource != NULL)
  193. {
  194. ft.Trace(VSSDBG_SQLLIB, L"Source:\t\t%s\n", bstrSource);
  195. msg.Add(L"Source: ");
  196. msg.Add(bstrSource);
  197. msg.Add(L"\n");
  198. }
  199. if (bstrDescription != NULL)
  200. {
  201. ft.Trace(VSSDBG_SQLLIB, L"Error message:\t%s\n", bstrDescription);
  202. msg.Add(L"Error message: ");
  203. msg.Add(bstrDescription);
  204. msg.Add(L"\n");
  205. }
  206. apIErrorInfoRecord.Release ();
  207. }
  208. } // for each record
  209. }
  210. else
  211. {
  212. // IErrorInfo is valid; get the source and
  213. // description to see what it is.
  214. apIErrorInfoAll->GetSource(&bstrSource);
  215. apIErrorInfoAll->GetDescription(&bstrDescription);
  216. if (bstrSource != NULL)
  217. {
  218. ft.Trace(VSSDBG_SQLLIB, L"Source:\t\t%s\n", bstrSource);
  219. msg.Add(L"Source: ");
  220. msg.Add(bstrSource);
  221. msg.Add(L"\n");
  222. }
  223. if (bstrDescription != NULL)
  224. {
  225. ft.Trace(VSSDBG_SQLLIB, L"Error message:\t%s\n", bstrDescription);
  226. msg.Add(L"Error message: ");
  227. msg.Add(bstrDescription);
  228. msg.Add(L"\n");
  229. }
  230. }
  231. }
  232. else
  233. {
  234. ft.Trace(VSSDBG_SQLLIB, L"GetErrorInfo failed.");
  235. }
  236. if (pBackupSuccess)
  237. {
  238. *pBackupSuccess = (msg3014seen && !msg3013seen);
  239. }
  240. }
  241. //------------------------------------------------------------------
  242. //
  243. SqlConnection::~SqlConnection ()
  244. {
  245. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConnection::~SqlConnection");
  246. Disconnect ();
  247. }
  248. //------------------------------------------------------------------
  249. //
  250. void
  251. SqlConnection::ReleaseRowset ()
  252. {
  253. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConnection::ReleaseRowset");
  254. if (m_pBuffer)
  255. {
  256. delete[] m_pBuffer;
  257. m_pBuffer = NULL;
  258. }
  259. if (m_pBindings)
  260. {
  261. delete[] m_pBindings;
  262. m_pBindings = NULL;
  263. }
  264. m_cBindings = 0;
  265. if (m_pAccessor)
  266. {
  267. if (m_hAcc)
  268. {
  269. m_pAccessor->ReleaseAccessor (m_hAcc, NULL);
  270. m_hAcc = NULL;
  271. }
  272. m_pAccessor->Release ();
  273. m_pAccessor = NULL;
  274. }
  275. if (m_pRowset)
  276. {
  277. m_pRowset->Release ();
  278. m_pRowset = NULL;
  279. }
  280. }
  281. //------------------------------------------------------------------
  282. //
  283. void
  284. SqlConnection::Disconnect ()
  285. {
  286. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConenction::Disconnect");
  287. ReleaseRowset ();
  288. if (m_pCommand)
  289. {
  290. m_pCommand->Release ();
  291. m_pCommand = NULL;
  292. }
  293. if (m_pCommandFactory)
  294. {
  295. m_pCommandFactory->Release ();
  296. m_pCommandFactory = NULL;
  297. }
  298. }
  299. // log an error if not an out of resource error
  300. void SqlConnection::LogOledbError
  301. (
  302. CVssFunctionTracer &ft,
  303. CVssDebugInfo &dbgInfo,
  304. LPCWSTR wszRoutine,
  305. CLogMsg &msg
  306. )
  307. {
  308. if (ft.hr == E_OUTOFMEMORY ||
  309. ft.hr == HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY) ||
  310. ft.hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_SEARCH_HANDLES) ||
  311. ft.hr == HRESULT_FROM_WIN32(ERROR_NO_LOG_SPACE) ||
  312. ft.hr == HRESULT_FROM_WIN32(ERROR_DISK_FULL) ||
  313. ft.hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_USER_HANDLES))
  314. ft.Throw(dbgInfo, E_OUTOFMEMORY, L"Out of memory detected in function %s", wszRoutine);
  315. else
  316. {
  317. ft.LogError(VSS_ERROR_SQLLIB_OLEDB_ERROR, dbgInfo << wszRoutine << ft.hr << msg.GetMsg());
  318. ft.Throw
  319. (
  320. dbgInfo,
  321. E_UNEXPECTED,
  322. L"Error calling %s. hr = 0x%08lx.\n%s",
  323. wszRoutine,
  324. ft.hr,
  325. msg.GetMsg()
  326. );
  327. }
  328. }
  329. //------------------------------------------------------------------
  330. // Setup a session, ready to receive commands.
  331. //
  332. // This call may block for a long time while establishing the connection.
  333. //
  334. // See the "FrozenServer" object for a method to determine if the local
  335. // server is up or not prior to requesting a connection.
  336. //
  337. // The "trick" of prepending "tcp:" to the servername isn't fast or robust
  338. // enough to detect a shutdown server.
  339. //
  340. // Note for C programmers....BSTRs are used as part of the COM
  341. // environment to be interoperable with VisualBasic. The VARIANT
  342. // datatype doesn't work with standard C strings.
  343. //
  344. void
  345. SqlConnection::Connect (
  346. const WString& serverName)
  347. {
  348. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConnection::Connect");
  349. CLogMsg msg;
  350. CComPtr<IDBInitialize> apdbInitialize;
  351. // "Connect" always implies a "fresh" connection.
  352. //
  353. ReleaseRowset ();
  354. if (m_ServerName.compare (serverName) == 0 && m_pCommand)
  355. {
  356. // Requesting the same server and we are connected.
  357. //
  358. return;
  359. }
  360. Disconnect ();
  361. m_ServerName = serverName;
  362. ft.CoCreateInstanceWithLog(
  363. VSSDBG_SQLLIB,
  364. CLSID_SQLOLEDB,
  365. L"SQLOLEDB",
  366. CLSCTX_INPROC_SERVER,
  367. IID_IDBInitialize,
  368. (IUnknown**)&(apdbInitialize));
  369. if (ft.HrFailed())
  370. ft.CheckForError(VSSDBG_SQLLIB, L"CoCreateInstance");
  371. CComPtr<IDBProperties> apdbProperties;
  372. ft.hr = apdbInitialize->QueryInterface(IID_IDBProperties, (void **) &apdbProperties);
  373. if (ft.HrFailed())
  374. ft.CheckForError(VSSDBG_SQLLIB, L"IDBInitialize::QueryInterface");
  375. CComBSTR bstrComputerName = serverName.c_str ();
  376. // initial database context
  377. CComBSTR bstrDatabaseName = L"master";
  378. // use NT Authentication
  379. CComBSTR bstrSSPI = L"SSPI";
  380. const unsigned x_CPROP = 3;
  381. DBPROPSET propset;
  382. DBPROP rgprop[x_CPROP];
  383. propset.guidPropertySet = DBPROPSET_DBINIT;
  384. propset.cProperties = x_CPROP;
  385. propset.rgProperties = rgprop;
  386. for (unsigned i = 0; i < x_CPROP; i++)
  387. {
  388. VariantInit(&rgprop[i].vValue);
  389. rgprop[i].dwOptions = DBPROPOPTIONS_REQUIRED;
  390. rgprop[i].colid = DB_NULLID;
  391. rgprop[i].vValue.vt = VT_BSTR;
  392. }
  393. rgprop[0].dwPropertyID = DBPROP_INIT_DATASOURCE;
  394. rgprop[1].dwPropertyID = DBPROP_INIT_CATALOG;
  395. rgprop[2].dwPropertyID = DBPROP_AUTH_INTEGRATED;
  396. rgprop[0].vValue.bstrVal = bstrComputerName;
  397. rgprop[1].vValue.bstrVal = bstrDatabaseName;
  398. rgprop[2].vValue.bstrVal = bstrSSPI;
  399. ft.hr = apdbProperties->SetProperties(1, &propset);
  400. if (ft.HrFailed())
  401. {
  402. DumpErrorInfo(apdbProperties, IID_IDBProperties, msg);
  403. LogOledbError(ft, VSSDBG_SQLLIB, L"IDBProperties::SetProperties", msg);
  404. }
  405. ft.Trace(VSSDBG_SQLLIB, L"Connecting to server %s...", serverName.c_str ());
  406. ft.hr = apdbInitialize->Initialize();
  407. if (ft.HrFailed())
  408. {
  409. DumpErrorInfo(apdbInitialize, IID_IDBInitialize, msg);
  410. LogOledbError(ft, VSSDBG_SQLLIB, L"IDBInitialize::Initialize", msg);
  411. }
  412. CComPtr<IDBCreateSession> apCreateSession;
  413. ft.hr = apdbInitialize->QueryInterface(IID_IDBCreateSession, (void **) &apCreateSession);
  414. if (ft.HrFailed())
  415. {
  416. DumpErrorInfo(apdbInitialize, IID_IDBInitialize, msg);
  417. LogOledbError(ft, VSSDBG_SQLLIB, L"IDBInitialize::QueryInterface", msg);
  418. }
  419. // We keep the command factory around to generate commands.
  420. //
  421. ft.hr = apCreateSession->CreateSession (
  422. NULL,
  423. IID_IDBCreateCommand,
  424. (IUnknown **) &m_pCommandFactory);
  425. if (ft.HrFailed())
  426. {
  427. DumpErrorInfo(apCreateSession, IID_IDBCreateSession, msg);
  428. LogOledbError(ft, VSSDBG_SQLLIB, L"IDBCreateSession::CreateSession", msg);
  429. }
  430. ft.Trace(VSSDBG_SQLLIB, L"Connected\n");
  431. // Request the version of this server
  432. //
  433. DBPROPIDSET versionSet;
  434. DBPROPID versionID = DBPROP_DBMSVER;
  435. versionSet.guidPropertySet = DBPROPSET_DATASOURCEINFO;
  436. versionSet.cPropertyIDs = 1;
  437. versionSet.rgPropertyIDs = &versionID;
  438. ULONG propCount;
  439. DBPROPSET* pPropSet;
  440. ft.hr = apdbProperties->GetProperties (1, &versionSet, &propCount, &pPropSet);
  441. if (ft.HrFailed())
  442. {
  443. DumpErrorInfo(apdbProperties, IID_IDBProperties, msg);
  444. LogOledbError(ft, VSSDBG_SQLLIB, L"IDBProperties::GetProperties", msg);
  445. }
  446. ft.Trace(VSSDBG_SQLLIB, L"Version: %s\n", pPropSet->rgProperties->vValue.bstrVal);
  447. swscanf (pPropSet->rgProperties->vValue.bstrVal, L"%d", &m_ServerVersion);
  448. g_pIMalloc->Free(pPropSet->rgProperties);
  449. g_pIMalloc->Free(pPropSet);
  450. }
  451. //---------------------------------------------------------------------
  452. // Setup the command with some SQL text
  453. //
  454. void
  455. SqlConnection::SetCommand (const WString& command)
  456. {
  457. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConnection::SetCommand");
  458. CLogMsg msg;
  459. // Release the result of the previous command
  460. //
  461. ReleaseRowset ();
  462. // We create the command on the first request, then keep only one
  463. // around in the SqlConnection.
  464. //
  465. if (!m_pCommand)
  466. {
  467. ft.hr = m_pCommandFactory->CreateCommand (NULL, IID_ICommandText,
  468. (IUnknown **) &m_pCommand);
  469. if (ft.HrFailed())
  470. {
  471. DumpErrorInfo(m_pCommandFactory, IID_IDBCreateCommand, msg);
  472. LogOledbError(ft, VSSDBG_SQLLIB, L"IDBCreateCommand::CreateCommand", msg);
  473. }
  474. }
  475. ft.hr = m_pCommand->SetCommandText(DBGUID_DBSQL, command.c_str ());
  476. if (ft.HrFailed())
  477. {
  478. DumpErrorInfo (m_pCommand, IID_ICommandText, msg);
  479. LogOledbError(ft, VSSDBG_SQLLIB, L"ICommandText::SetCommandText", msg);
  480. }
  481. ft.Trace (VSSDBG_SQLLIB, L"SetCommand (%s)\n", command.c_str ());
  482. }
  483. //---------------------------------------------------------------------
  484. // Execute the command. "SetCommand" must have been called previously.
  485. //
  486. BOOL
  487. SqlConnection::ExecCommand ()
  488. {
  489. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConnection::ExecCommand");
  490. CLogMsg msg;
  491. CComPtr<IRowset> apRowset;
  492. DBROWCOUNT crows;
  493. HRESULT hr;
  494. // Release the result of the previous command
  495. //
  496. ReleaseRowset ();
  497. ft.hr = m_pCommand->Execute (
  498. NULL,
  499. IID_IRowset,
  500. NULL,
  501. &crows,
  502. (IUnknown **) &m_pRowset);
  503. if (ft.HrFailed())
  504. {
  505. BOOL backupSuccess = FALSE;
  506. DumpErrorInfo (m_pCommand, IID_ICommandText, msg, &backupSuccess);
  507. if (!backupSuccess)
  508. LogOledbError(ft, VSSDBG_SQLLIB, L"ICommandText::Execute", msg);
  509. }
  510. if (!m_pRowset)
  511. {
  512. ft.Trace(VSSDBG_SQLLIB, L"Command completed successfully with no rowset\n");
  513. return FALSE;
  514. }
  515. return TRUE;
  516. }
  517. //---------------------------------------------------------------------
  518. // return a vector of string, one for each row of the output.
  519. // The query should have returned a single column.
  520. //
  521. StringVector*
  522. SqlConnection::GetStringColumn ()
  523. {
  524. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConnection::GetStringColumn");
  525. CLogMsg msg;
  526. //ASSERT (m_pRowset)
  527. //
  528. CComPtr<IColumnsInfo> apColumnsInfo;
  529. ft.hr = m_pRowset->QueryInterface(IID_IColumnsInfo, (void **) &apColumnsInfo);
  530. if (ft.HrFailed())
  531. {
  532. DumpErrorInfo (m_pRowset, IID_IRowset, msg);
  533. LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::QueryInterface", msg);
  534. }
  535. // get columns info
  536. //
  537. DBCOLUMNINFO *rgColumnInfo;
  538. DBORDINAL cColumns;
  539. WCHAR *wszColumnInfo;
  540. ft.hr = apColumnsInfo->GetColumnInfo(&cColumns, &rgColumnInfo, &wszColumnInfo);
  541. if (ft.HrFailed())
  542. {
  543. DumpErrorInfo (apColumnsInfo, IID_IColumnsInfo, msg);
  544. LogOledbError(ft, VSSDBG_SQLLIB, L"IColumnsInfo::GetColumnInfo", msg);
  545. }
  546. // Auto objects ensure that memory is freed on exit
  547. //
  548. CAutoTask<DBCOLUMNINFO> argColumnInfo = rgColumnInfo;
  549. CAutoTask<WCHAR> awszColumnInfo = wszColumnInfo;
  550. // Setup a buffer to hold the string.
  551. // The output buffer holds a 4byte length, followed by the string column.
  552. //
  553. // "bufferSize" is in units of characters (not bytes)
  554. // Note that the "ulColumnSize" is in characters.
  555. // 1 char is used for the null term and we actually allocate one additional
  556. // char (hidden), just incase our provider gets the boundary condition wrong.
  557. //
  558. ULONG bufferSize = 1 + rgColumnInfo[0].ulColumnSize + (sizeof(ULONG)/sizeof(WCHAR));
  559. std::auto_ptr<WCHAR> rowBuffer(new WCHAR[bufferSize+1]);
  560. // Describe the binding for our single column of interest
  561. //
  562. DBBINDING rgbind[1];
  563. unsigned cBindings = 1;
  564. rgbind[0].dwPart = DBPART_VALUE|DBPART_LENGTH;
  565. rgbind[0].wType = DBTYPE_WSTR; // retrieve a
  566. rgbind[0].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
  567. rgbind[0].eParamIO = DBPARAMIO_NOTPARAM;
  568. rgbind[0].pObject = NULL;
  569. rgbind[0].pBindExt = NULL;
  570. rgbind[0].pTypeInfo = NULL;
  571. rgbind[0].dwFlags = 0;
  572. rgbind[0].obLength = 0; // offset to the length field
  573. rgbind[0].iOrdinal = 1; // column id's start at 1
  574. rgbind[0].obValue = sizeof(ULONG); // offset to the string field
  575. rgbind[0].cbMaxLen = (unsigned) (bufferSize*sizeof(WCHAR)-sizeof(ULONG));
  576. CComPtr<IAccessor> apAccessor;
  577. ft.hr = m_pRowset->QueryInterface(IID_IAccessor, (void **) &apAccessor);
  578. if (ft.HrFailed())
  579. {
  580. DumpErrorInfo (m_pRowset, IID_IRowset, msg);
  581. LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::QueryInterface", msg);
  582. }
  583. HACCESSOR hacc;
  584. ft.hr = apAccessor->CreateAccessor (
  585. DBACCESSOR_ROWDATA,
  586. cBindings,
  587. rgbind,
  588. 0,
  589. &hacc,
  590. NULL);
  591. if (ft.HrFailed())
  592. {
  593. DumpErrorInfo(apAccessor, IID_IAccessor, msg);
  594. LogOledbError(ft, VSSDBG_SQLLIB, L"IAccessor::CreateAccessor", msg);
  595. }
  596. // loop through rows, generating a vector of strings.
  597. //
  598. HROW hrow;
  599. HROW *rghrow = &hrow;
  600. DBCOUNTITEM crow;
  601. std::auto_ptr<StringVector> aVec (new StringVector);
  602. // pString points into the output buffer for the string column
  603. //
  604. WCHAR* pString = (WCHAR*)((BYTE*)rowBuffer.get () + sizeof (ULONG));
  605. while(TRUE)
  606. {
  607. ft.hr = m_pRowset->GetNextRows(NULL, 0, 1, &crow, &rghrow);
  608. if (ft.hr == DB_S_ENDOFROWSET)
  609. break;
  610. if (ft.HrFailed())
  611. {
  612. DumpErrorInfo (m_pRowset, IID_IRowset, msg);
  613. LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::GetNextRows", msg);
  614. }
  615. ft.hr = m_pRowset->GetData (hrow, hacc, rowBuffer.get());
  616. if (ft.HrFailed())
  617. {
  618. DumpErrorInfo(m_pRowset, IID_IRowset, msg);
  619. m_pRowset->ReleaseRows (1, rghrow, NULL, NULL, NULL);
  620. LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::GetData", msg);
  621. }
  622. unsigned tempChars = (*(ULONG*)rowBuffer.get ())/sizeof (WCHAR);
  623. WString tempStr (pString, tempChars);
  624. aVec->push_back (tempStr);
  625. ft.Trace(VSSDBG_SQLLIB, L"StringColumn: %s\n", tempStr.c_str ());
  626. m_pRowset->ReleaseRows(1, rghrow, NULL, NULL, NULL);
  627. }
  628. // UNDONE...make this an auto object to avoid leaks
  629. //
  630. apAccessor->ReleaseAccessor (hacc, NULL);
  631. return aVec.release ();
  632. }
  633. //---------------------------------------------------------------------
  634. // Fetch the first row of the result.
  635. //
  636. BOOL
  637. SqlConnection::FetchFirst ()
  638. {
  639. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConnection::FetchFirst");
  640. CLogMsg msg;
  641. // UNDONE...make this nicely restep back to the first row.
  642. //
  643. if (m_pBindings)
  644. {
  645. throw HRESULT(E_SQLLIB_PROTO);
  646. }
  647. CComPtr<IColumnsInfo> apColumnsInfo;
  648. ft.hr = m_pRowset->QueryInterface(IID_IColumnsInfo, (void **) &apColumnsInfo);
  649. if (ft.HrFailed())
  650. {
  651. DumpErrorInfo (m_pRowset, IID_IRowset, msg);
  652. LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::QueryInteface", msg);
  653. }
  654. // get columns info
  655. //
  656. DBCOLUMNINFO *rgColumnInfo;
  657. DBORDINAL cColumns;
  658. WCHAR *wszColumnInfo;
  659. ft.hr = apColumnsInfo->GetColumnInfo(&cColumns, &rgColumnInfo, &wszColumnInfo);
  660. if (ft.HrFailed())
  661. {
  662. DumpErrorInfo (apColumnsInfo, IID_IColumnsInfo, msg);
  663. LogOledbError(ft, VSSDBG_SQLLIB, L"IColumnsInfo::GetColumnInfo", msg);
  664. }
  665. // Auto objects ensure that memory is freed on exit
  666. //
  667. CAutoTask<DBCOLUMNINFO> argColumnInfo = rgColumnInfo;
  668. CAutoTask<WCHAR> awszColumnInfo = wszColumnInfo;
  669. // allocate bindings
  670. unsigned m_cBindings = (unsigned) cColumns;
  671. m_pBindings = new DBBINDING[m_cBindings];
  672. // Set up the bindings onto a buffer we'll allocate
  673. // UNDONE: do this properly for alignment & handling null indicators
  674. //
  675. unsigned cb = 0;
  676. for (unsigned icol = 0; icol < m_cBindings; icol++)
  677. {
  678. unsigned maxBytes;
  679. m_pBindings[icol].iOrdinal = icol + 1;
  680. m_pBindings[icol].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
  681. m_pBindings[icol].eParamIO = DBPARAMIO_NOTPARAM;
  682. m_pBindings[icol].pObject = NULL;
  683. m_pBindings[icol].pBindExt = NULL;
  684. m_pBindings[icol].pTypeInfo = NULL;
  685. m_pBindings[icol].dwFlags = 0;
  686. m_pBindings[icol].bPrecision = rgColumnInfo[icol].bPrecision;
  687. m_pBindings[icol].bScale = rgColumnInfo[icol].bScale;
  688. m_pBindings[icol].obStatus = 0; // no status info
  689. if (rgColumnInfo[icol].wType == DBTYPE_WSTR)
  690. { // do we need the length?
  691. m_pBindings[icol].dwPart = DBPART_VALUE; //|DBPART_LENGTH;
  692. m_pBindings[icol].wType = DBTYPE_WSTR;
  693. m_pBindings[icol].obLength = 0; //icol * sizeof(DBLENGTH);
  694. maxBytes = rgColumnInfo[icol].ulColumnSize * sizeof(WCHAR);
  695. }
  696. else
  697. {
  698. m_pBindings[icol].dwPart = DBPART_VALUE;
  699. m_pBindings[icol].wType = rgColumnInfo[icol].wType;
  700. m_pBindings[icol].obLength = 0; // no length
  701. maxBytes = rgColumnInfo[icol].ulColumnSize;
  702. }
  703. m_pBindings[icol].obValue = cb;
  704. m_pBindings[icol].cbMaxLen = maxBytes;
  705. cb += maxBytes;
  706. }
  707. // allocate data buffer
  708. //
  709. m_pBuffer = new BYTE[cb];
  710. m_BufferSize = cb;
  711. ft.hr = m_pRowset->QueryInterface(IID_IAccessor, (void **) &m_pAccessor);
  712. if (ft.HrFailed())
  713. {
  714. DumpErrorInfo (m_pRowset, IID_IRowset, msg);
  715. LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::QueryInterface", msg);
  716. }
  717. ft.hr = m_pAccessor->CreateAccessor (
  718. DBACCESSOR_ROWDATA,
  719. m_cBindings,
  720. m_pBindings,
  721. 0,
  722. &m_hAcc,
  723. NULL);
  724. if (ft.HrFailed())
  725. {
  726. DumpErrorInfo(m_pAccessor, IID_IAccessor, msg);
  727. LogOledbError(ft, VSSDBG_SQLLIB, L"IAccessor::CreateAccessor", msg);
  728. }
  729. // Fetch the first row
  730. //
  731. HROW hrow;
  732. HROW *rghrow = &hrow;
  733. DBCOUNTITEM crow;
  734. ft.hr = m_pRowset->GetNextRows(NULL, 0, 1, &crow, &rghrow);
  735. if (ft.hr == DB_S_ENDOFROWSET)
  736. {
  737. // No rows in this set
  738. //
  739. return FALSE;
  740. }
  741. if (ft.HrFailed())
  742. {
  743. DumpErrorInfo (m_pRowset, IID_IRowset, msg);
  744. LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::GetNextRows", msg);
  745. }
  746. ft.hr = m_pRowset->GetData (hrow, m_hAcc, m_pBuffer);
  747. if (ft.HrFailed())
  748. {
  749. DumpErrorInfo(m_pRowset, IID_IRowset, msg);
  750. m_pRowset->ReleaseRows (1, rghrow, NULL, NULL, NULL);
  751. LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::GetData", msg);
  752. }
  753. m_pRowset->ReleaseRows(1, rghrow, NULL, NULL, NULL);
  754. return TRUE;
  755. }
  756. //---------------------------------------------------------------------
  757. // Fetch the next row of the result.
  758. //
  759. BOOL
  760. SqlConnection::FetchNext ()
  761. {
  762. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConnection::FetchNext");
  763. HROW hrow;
  764. HROW *rghrow = &hrow;
  765. DBCOUNTITEM crow;
  766. CLogMsg msg;
  767. ft.hr = m_pRowset->GetNextRows(NULL, 0, 1, &crow, &rghrow);
  768. if (ft.hr == DB_S_ENDOFROWSET)
  769. {
  770. return FALSE;
  771. }
  772. if (ft.HrFailed())
  773. {
  774. DumpErrorInfo (m_pRowset, IID_IRowset, msg);
  775. LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::GetNextRows", msg);
  776. }
  777. ft.hr = m_pRowset->GetData (hrow, m_hAcc, m_pBuffer);
  778. if (ft.HrFailed())
  779. {
  780. DumpErrorInfo(m_pRowset, IID_IRowset, msg);
  781. m_pRowset->ReleaseRows (1, rghrow, NULL, NULL, NULL);
  782. LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::GetData", msg);
  783. }
  784. m_pRowset->ReleaseRows(1, rghrow, NULL, NULL, NULL);
  785. return TRUE;
  786. }
  787. //-----------------------------------------------------------
  788. // Provide a pointer to the n'th column.
  789. //
  790. void*
  791. SqlConnection::AccessColumn (int colid)
  792. {
  793. return m_pBuffer + m_pBindings[colid-1].obValue;
  794. }