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.

943 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.hr = CoCreateInstance
  363. (
  364. CLSID_SQLOLEDB,
  365. NULL,
  366. CLSCTX_INPROC_SERVER,
  367. IID_IDBInitialize,
  368. (void **) &apdbInitialize
  369. );
  370. if (ft.HrFailed())
  371. ft.CheckForError(VSSDBG_SQLLIB, L"CoCreateInstance");
  372. CComPtr<IDBProperties> apdbProperties;
  373. ft.hr = apdbInitialize->QueryInterface(IID_IDBProperties, (void **) &apdbProperties);
  374. if (ft.HrFailed())
  375. ft.CheckForError(VSSDBG_SQLLIB, L"IDBInitialize::QueryInterface");
  376. CComBSTR bstrComputerName = serverName.c_str ();
  377. // initial database context
  378. CComBSTR bstrDatabaseName = L"master";
  379. // use NT Authentication
  380. CComBSTR bstrSSPI = L"SSPI";
  381. const unsigned x_CPROP = 3;
  382. DBPROPSET propset;
  383. DBPROP rgprop[x_CPROP];
  384. propset.guidPropertySet = DBPROPSET_DBINIT;
  385. propset.cProperties = x_CPROP;
  386. propset.rgProperties = rgprop;
  387. for (unsigned i = 0; i < x_CPROP; i++)
  388. {
  389. VariantInit(&rgprop[i].vValue);
  390. rgprop[i].dwOptions = DBPROPOPTIONS_REQUIRED;
  391. rgprop[i].colid = DB_NULLID;
  392. rgprop[i].vValue.vt = VT_BSTR;
  393. }
  394. rgprop[0].dwPropertyID = DBPROP_INIT_DATASOURCE;
  395. rgprop[1].dwPropertyID = DBPROP_INIT_CATALOG;
  396. rgprop[2].dwPropertyID = DBPROP_AUTH_INTEGRATED;
  397. rgprop[0].vValue.bstrVal = bstrComputerName;
  398. rgprop[1].vValue.bstrVal = bstrDatabaseName;
  399. rgprop[2].vValue.bstrVal = bstrSSPI;
  400. ft.hr = apdbProperties->SetProperties(1, &propset);
  401. if (ft.HrFailed())
  402. {
  403. DumpErrorInfo(apdbProperties, IID_IDBProperties, msg);
  404. LogOledbError(ft, VSSDBG_SQLLIB, L"IDBProperties::SetProperties", msg);
  405. }
  406. ft.Trace(VSSDBG_SQLLIB, L"Connecting to server %s...", serverName.c_str ());
  407. ft.hr = apdbInitialize->Initialize();
  408. if (ft.HrFailed())
  409. {
  410. DumpErrorInfo(apdbInitialize, IID_IDBInitialize, msg);
  411. LogOledbError(ft, VSSDBG_SQLLIB, L"IDBInitialize::Initialize", msg);
  412. }
  413. CComPtr<IDBCreateSession> apCreateSession;
  414. ft.hr = apdbInitialize->QueryInterface(IID_IDBCreateSession, (void **) &apCreateSession);
  415. if (ft.HrFailed())
  416. {
  417. DumpErrorInfo(apdbInitialize, IID_IDBInitialize, msg);
  418. LogOledbError(ft, VSSDBG_SQLLIB, L"IDBInitialize::QueryInterface", msg);
  419. }
  420. // We keep the command factory around to generate commands.
  421. //
  422. ft.hr = apCreateSession->CreateSession (
  423. NULL,
  424. IID_IDBCreateCommand,
  425. (IUnknown **) &m_pCommandFactory);
  426. if (ft.HrFailed())
  427. {
  428. DumpErrorInfo(apCreateSession, IID_IDBCreateSession, msg);
  429. LogOledbError(ft, VSSDBG_SQLLIB, L"IDBCreateSession::CreateSession", msg);
  430. }
  431. ft.Trace(VSSDBG_SQLLIB, L"Connected\n");
  432. // Request the version of this server
  433. //
  434. DBPROPIDSET versionSet;
  435. DBPROPID versionID = DBPROP_DBMSVER;
  436. versionSet.guidPropertySet = DBPROPSET_DATASOURCEINFO;
  437. versionSet.cPropertyIDs = 1;
  438. versionSet.rgPropertyIDs = &versionID;
  439. ULONG propCount;
  440. DBPROPSET* pPropSet;
  441. ft.hr = apdbProperties->GetProperties (1, &versionSet, &propCount, &pPropSet);
  442. if (ft.HrFailed())
  443. {
  444. DumpErrorInfo(apdbProperties, IID_IDBProperties, msg);
  445. LogOledbError(ft, VSSDBG_SQLLIB, L"IDBProperties::GetProperties", msg);
  446. }
  447. ft.Trace(VSSDBG_SQLLIB, L"Version: %s\n", pPropSet->rgProperties->vValue.bstrVal);
  448. swscanf (pPropSet->rgProperties->vValue.bstrVal, L"%d", &m_ServerVersion);
  449. g_pIMalloc->Free(pPropSet->rgProperties);
  450. g_pIMalloc->Free(pPropSet);
  451. }
  452. //---------------------------------------------------------------------
  453. // Setup the command with some SQL text
  454. //
  455. void
  456. SqlConnection::SetCommand (const WString& command)
  457. {
  458. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConnection::SetCommand");
  459. CLogMsg msg;
  460. // Release the result of the previous command
  461. //
  462. ReleaseRowset ();
  463. // We create the command on the first request, then keep only one
  464. // around in the SqlConnection.
  465. //
  466. if (!m_pCommand)
  467. {
  468. ft.hr = m_pCommandFactory->CreateCommand (NULL, IID_ICommandText,
  469. (IUnknown **) &m_pCommand);
  470. if (ft.HrFailed())
  471. {
  472. DumpErrorInfo(m_pCommandFactory, IID_IDBCreateCommand, msg);
  473. LogOledbError(ft, VSSDBG_SQLLIB, L"IDBCreateCommand::CreateCommand", msg);
  474. }
  475. }
  476. ft.hr = m_pCommand->SetCommandText(DBGUID_DBSQL, command.c_str ());
  477. if (ft.HrFailed())
  478. {
  479. DumpErrorInfo (m_pCommand, IID_ICommandText, msg);
  480. LogOledbError(ft, VSSDBG_SQLLIB, L"ICommandText::SetCommandText", msg);
  481. }
  482. ft.Trace (VSSDBG_SQLLIB, L"SetCommand (%s)\n", command.c_str ());
  483. }
  484. //---------------------------------------------------------------------
  485. // Execute the command. "SetCommand" must have been called previously.
  486. //
  487. BOOL
  488. SqlConnection::ExecCommand ()
  489. {
  490. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConnection::ExecCommand");
  491. CLogMsg msg;
  492. CComPtr<IRowset> apRowset;
  493. DBROWCOUNT crows;
  494. HRESULT hr;
  495. // Release the result of the previous command
  496. //
  497. ReleaseRowset ();
  498. ft.hr = m_pCommand->Execute (
  499. NULL,
  500. IID_IRowset,
  501. NULL,
  502. &crows,
  503. (IUnknown **) &m_pRowset);
  504. if (ft.HrFailed())
  505. {
  506. BOOL backupSuccess = FALSE;
  507. DumpErrorInfo (m_pCommand, IID_ICommandText, msg, &backupSuccess);
  508. if (!backupSuccess)
  509. LogOledbError(ft, VSSDBG_SQLLIB, L"ICommandText::Execute", msg);
  510. }
  511. if (!m_pRowset)
  512. {
  513. ft.Trace(VSSDBG_SQLLIB, L"Command completed successfully with no rowset\n");
  514. return FALSE;
  515. }
  516. return TRUE;
  517. }
  518. //---------------------------------------------------------------------
  519. // return a vector of string, one for each row of the output.
  520. // The query should have returned a single column.
  521. //
  522. StringVector*
  523. SqlConnection::GetStringColumn ()
  524. {
  525. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConnection::GetStringColumn");
  526. CLogMsg msg;
  527. //ASSERT (m_pRowset)
  528. //
  529. CComPtr<IColumnsInfo> apColumnsInfo;
  530. ft.hr = m_pRowset->QueryInterface(IID_IColumnsInfo, (void **) &apColumnsInfo);
  531. if (ft.HrFailed())
  532. {
  533. DumpErrorInfo (m_pRowset, IID_IRowset, msg);
  534. LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::QueryInterface", msg);
  535. }
  536. // get columns info
  537. //
  538. DBCOLUMNINFO *rgColumnInfo;
  539. DBORDINAL cColumns;
  540. WCHAR *wszColumnInfo;
  541. ft.hr = apColumnsInfo->GetColumnInfo(&cColumns, &rgColumnInfo, &wszColumnInfo);
  542. if (ft.HrFailed())
  543. {
  544. DumpErrorInfo (apColumnsInfo, IID_IColumnsInfo, msg);
  545. LogOledbError(ft, VSSDBG_SQLLIB, L"IColumnsInfo::GetColumnInfo", msg);
  546. }
  547. // Auto objects ensure that memory is freed on exit
  548. //
  549. CAutoTask<DBCOLUMNINFO> argColumnInfo = rgColumnInfo;
  550. CAutoTask<WCHAR> awszColumnInfo = wszColumnInfo;
  551. // Setup a buffer to hold the string.
  552. // The output buffer holds a 4byte length, followed by the string column.
  553. //
  554. // "bufferSize" is in units of characters (not bytes)
  555. // Note that the "ulColumnSize" is in characters.
  556. // 1 char is used for the null term and we actually allocate one additional
  557. // char (hidden), just incase our provider gets the boundary condition wrong.
  558. //
  559. ULONG bufferSize = 1 + rgColumnInfo[0].ulColumnSize + (sizeof(ULONG)/sizeof(WCHAR));
  560. std::auto_ptr<WCHAR> rowBuffer(new WCHAR[bufferSize+1]);
  561. // Describe the binding for our single column of interest
  562. //
  563. DBBINDING rgbind[1];
  564. unsigned cBindings = 1;
  565. rgbind[0].dwPart = DBPART_VALUE|DBPART_LENGTH;
  566. rgbind[0].wType = DBTYPE_WSTR; // retrieve a
  567. rgbind[0].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
  568. rgbind[0].eParamIO = DBPARAMIO_NOTPARAM;
  569. rgbind[0].pObject = NULL;
  570. rgbind[0].pBindExt = NULL;
  571. rgbind[0].pTypeInfo = NULL;
  572. rgbind[0].dwFlags = 0;
  573. rgbind[0].obLength = 0; // offset to the length field
  574. rgbind[0].iOrdinal = 1; // column id's start at 1
  575. rgbind[0].obValue = sizeof(ULONG); // offset to the string field
  576. rgbind[0].cbMaxLen = (unsigned) (bufferSize*sizeof(WCHAR)-sizeof(ULONG));
  577. CComPtr<IAccessor> apAccessor;
  578. ft.hr = m_pRowset->QueryInterface(IID_IAccessor, (void **) &apAccessor);
  579. if (ft.HrFailed())
  580. {
  581. DumpErrorInfo (m_pRowset, IID_IRowset, msg);
  582. LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::QueryInterface", msg);
  583. }
  584. HACCESSOR hacc;
  585. ft.hr = apAccessor->CreateAccessor (
  586. DBACCESSOR_ROWDATA,
  587. cBindings,
  588. rgbind,
  589. 0,
  590. &hacc,
  591. NULL);
  592. if (ft.HrFailed())
  593. {
  594. DumpErrorInfo(apAccessor, IID_IAccessor, msg);
  595. LogOledbError(ft, VSSDBG_SQLLIB, L"IAccessor::CreateAccessor", msg);
  596. }
  597. // loop through rows, generating a vector of strings.
  598. //
  599. HROW hrow;
  600. HROW *rghrow = &hrow;
  601. DBCOUNTITEM crow;
  602. std::auto_ptr<StringVector> aVec (new StringVector);
  603. // pString points into the output buffer for the string column
  604. //
  605. WCHAR* pString = (WCHAR*)((BYTE*)rowBuffer.get () + sizeof (ULONG));
  606. while(TRUE)
  607. {
  608. ft.hr = m_pRowset->GetNextRows(NULL, 0, 1, &crow, &rghrow);
  609. if (ft.hr == DB_S_ENDOFROWSET)
  610. break;
  611. if (ft.HrFailed())
  612. {
  613. DumpErrorInfo (m_pRowset, IID_IRowset, msg);
  614. LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::GetNextRows", msg);
  615. }
  616. ft.hr = m_pRowset->GetData (hrow, hacc, rowBuffer.get());
  617. if (ft.HrFailed())
  618. {
  619. DumpErrorInfo(m_pRowset, IID_IRowset, msg);
  620. m_pRowset->ReleaseRows (1, rghrow, NULL, NULL, NULL);
  621. LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::GetData", msg);
  622. }
  623. unsigned tempChars = (*(ULONG*)rowBuffer.get ())/sizeof (WCHAR);
  624. WString tempStr (pString, tempChars);
  625. aVec->push_back (tempStr);
  626. ft.Trace(VSSDBG_SQLLIB, L"StringColumn: %s\n", tempStr.c_str ());
  627. m_pRowset->ReleaseRows(1, rghrow, NULL, NULL, NULL);
  628. }
  629. // UNDONE...make this an auto object to avoid leaks
  630. //
  631. apAccessor->ReleaseAccessor (hacc, NULL);
  632. return aVec.release ();
  633. }
  634. //---------------------------------------------------------------------
  635. // Fetch the first row of the result.
  636. //
  637. BOOL
  638. SqlConnection::FetchFirst ()
  639. {
  640. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConnection::FetchFirst");
  641. CLogMsg msg;
  642. // UNDONE...make this nicely restep back to the first row.
  643. //
  644. if (m_pBindings)
  645. {
  646. throw HRESULT(E_SQLLIB_PROTO);
  647. }
  648. CComPtr<IColumnsInfo> apColumnsInfo;
  649. ft.hr = m_pRowset->QueryInterface(IID_IColumnsInfo, (void **) &apColumnsInfo);
  650. if (ft.HrFailed())
  651. {
  652. DumpErrorInfo (m_pRowset, IID_IRowset, msg);
  653. LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::QueryInteface", msg);
  654. }
  655. // get columns info
  656. //
  657. DBCOLUMNINFO *rgColumnInfo;
  658. DBORDINAL cColumns;
  659. WCHAR *wszColumnInfo;
  660. ft.hr = apColumnsInfo->GetColumnInfo(&cColumns, &rgColumnInfo, &wszColumnInfo);
  661. if (ft.HrFailed())
  662. {
  663. DumpErrorInfo (apColumnsInfo, IID_IColumnsInfo, msg);
  664. LogOledbError(ft, VSSDBG_SQLLIB, L"IColumnsInfo::GetColumnInfo", msg);
  665. }
  666. // Auto objects ensure that memory is freed on exit
  667. //
  668. CAutoTask<DBCOLUMNINFO> argColumnInfo = rgColumnInfo;
  669. CAutoTask<WCHAR> awszColumnInfo = wszColumnInfo;
  670. // allocate bindings
  671. unsigned m_cBindings = (unsigned) cColumns;
  672. m_pBindings = new DBBINDING[m_cBindings];
  673. // Set up the bindings onto a buffer we'll allocate
  674. // UNDONE: do this properly for alignment & handling null indicators
  675. //
  676. unsigned cb = 0;
  677. for (unsigned icol = 0; icol < m_cBindings; icol++)
  678. {
  679. unsigned maxBytes;
  680. m_pBindings[icol].iOrdinal = icol + 1;
  681. m_pBindings[icol].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
  682. m_pBindings[icol].eParamIO = DBPARAMIO_NOTPARAM;
  683. m_pBindings[icol].pObject = NULL;
  684. m_pBindings[icol].pBindExt = NULL;
  685. m_pBindings[icol].pTypeInfo = NULL;
  686. m_pBindings[icol].dwFlags = 0;
  687. m_pBindings[icol].bPrecision = rgColumnInfo[icol].bPrecision;
  688. m_pBindings[icol].bScale = rgColumnInfo[icol].bScale;
  689. m_pBindings[icol].obStatus = 0; // no status info
  690. if (rgColumnInfo[icol].wType == DBTYPE_WSTR)
  691. { // do we need the length?
  692. m_pBindings[icol].dwPart = DBPART_VALUE; //|DBPART_LENGTH;
  693. m_pBindings[icol].wType = DBTYPE_WSTR;
  694. m_pBindings[icol].obLength = 0; //icol * sizeof(DBLENGTH);
  695. maxBytes = rgColumnInfo[icol].ulColumnSize * sizeof(WCHAR);
  696. }
  697. else
  698. {
  699. m_pBindings[icol].dwPart = DBPART_VALUE;
  700. m_pBindings[icol].wType = rgColumnInfo[icol].wType;
  701. m_pBindings[icol].obLength = 0; // no length
  702. maxBytes = rgColumnInfo[icol].ulColumnSize;
  703. }
  704. m_pBindings[icol].obValue = cb;
  705. m_pBindings[icol].cbMaxLen = maxBytes;
  706. cb += maxBytes;
  707. }
  708. // allocate data buffer
  709. //
  710. m_pBuffer = new BYTE[cb];
  711. m_BufferSize = cb;
  712. ft.hr = m_pRowset->QueryInterface(IID_IAccessor, (void **) &m_pAccessor);
  713. if (ft.HrFailed())
  714. {
  715. DumpErrorInfo (m_pRowset, IID_IRowset, msg);
  716. LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::QueryInterface", msg);
  717. }
  718. ft.hr = m_pAccessor->CreateAccessor (
  719. DBACCESSOR_ROWDATA,
  720. m_cBindings,
  721. m_pBindings,
  722. 0,
  723. &m_hAcc,
  724. NULL);
  725. if (ft.HrFailed())
  726. {
  727. DumpErrorInfo(m_pAccessor, IID_IAccessor, msg);
  728. LogOledbError(ft, VSSDBG_SQLLIB, L"IAccessor::CreateAccessor", msg);
  729. }
  730. // Fetch the first row
  731. //
  732. HROW hrow;
  733. HROW *rghrow = &hrow;
  734. DBCOUNTITEM crow;
  735. ft.hr = m_pRowset->GetNextRows(NULL, 0, 1, &crow, &rghrow);
  736. if (ft.hr == DB_S_ENDOFROWSET)
  737. {
  738. // No rows in this set
  739. //
  740. return FALSE;
  741. }
  742. if (ft.HrFailed())
  743. {
  744. DumpErrorInfo (m_pRowset, IID_IRowset, msg);
  745. LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::GetNextRows", msg);
  746. }
  747. ft.hr = m_pRowset->GetData (hrow, m_hAcc, m_pBuffer);
  748. if (ft.HrFailed())
  749. {
  750. DumpErrorInfo(m_pRowset, IID_IRowset, msg);
  751. m_pRowset->ReleaseRows (1, rghrow, NULL, NULL, NULL);
  752. LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::GetData", msg);
  753. }
  754. m_pRowset->ReleaseRows(1, rghrow, NULL, NULL, NULL);
  755. return TRUE;
  756. }
  757. //---------------------------------------------------------------------
  758. // Fetch the next row of the result.
  759. //
  760. BOOL
  761. SqlConnection::FetchNext ()
  762. {
  763. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlConnection::FetchNext");
  764. HROW hrow;
  765. HROW *rghrow = &hrow;
  766. DBCOUNTITEM crow;
  767. CLogMsg msg;
  768. ft.hr = m_pRowset->GetNextRows(NULL, 0, 1, &crow, &rghrow);
  769. if (ft.hr == DB_S_ENDOFROWSET)
  770. {
  771. return FALSE;
  772. }
  773. if (ft.HrFailed())
  774. {
  775. DumpErrorInfo (m_pRowset, IID_IRowset, msg);
  776. LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::GetNextRows", msg);
  777. }
  778. ft.hr = m_pRowset->GetData (hrow, m_hAcc, m_pBuffer);
  779. if (ft.HrFailed())
  780. {
  781. DumpErrorInfo(m_pRowset, IID_IRowset, msg);
  782. m_pRowset->ReleaseRows (1, rghrow, NULL, NULL, NULL);
  783. LogOledbError(ft, VSSDBG_SQLLIB, L"IRowset::GetData", msg);
  784. }
  785. m_pRowset->ReleaseRows(1, rghrow, NULL, NULL, NULL);
  786. return TRUE;
  787. }
  788. //-----------------------------------------------------------
  789. // Provide a pointer to the n'th column.
  790. //
  791. void*
  792. SqlConnection::AccessColumn (int colid)
  793. {
  794. return m_pBuffer + m_pBindings[colid-1].obValue;
  795. }