Source code of Windows XP (NT5)
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.

618 lines
15 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 1997, Microsoft Corp. All rights reserved.
  4. //
  5. // FILE
  6. //
  7. // SimTable.cpp
  8. //
  9. // SYNOPSIS
  10. //
  11. // This file implements the class CSimpleTable
  12. //
  13. // MODIFICATION HISTORY
  14. //
  15. // 10/31/1997 Original version.
  16. // 02/09/1998 Reorganized some things to make is easier to extend.
  17. // 02/27/1998 Changes to support moving it into the iasutil.lib
  18. // 10/16/1998 Support DBTYPE_WSTR.
  19. //
  20. ///////////////////////////////////////////////////////////////////////////////
  21. #include <windows.h>
  22. #include <comdef.h>
  23. #include <iasutil.h>
  24. #include <oledberr.h>
  25. #include <SimTable.h>
  26. //////////
  27. // Stack version of the new operator.
  28. //////////
  29. #define stack_new(obj, num) new (_alloca(sizeof(obj)*num)) obj[num]
  30. ///////////////////////////////////////////////////////////////////////////////
  31. //
  32. // STRUCT
  33. //
  34. // DBBinding
  35. //
  36. // DESCRIPTION
  37. //
  38. // This struct extends the DBBINDING struct to provide functionality
  39. // to initialize the struct from a DBCOLUMNINFO struct.
  40. //
  41. ///////////////////////////////////////////////////////////////////////////////
  42. struct DBBinding : DBBINDING
  43. {
  44. //////////
  45. // 'offset' is the offset in bytes of this column's data within the
  46. // row buffer.
  47. //////////
  48. void Initialize(DBCOLUMNINFO& columnInfo, DBBYTEOFFSET& offset)
  49. {
  50. iOrdinal = columnInfo.iOrdinal;
  51. obValue = offset;
  52. obLength = offset + columnInfo.ulColumnSize;
  53. obStatus = obLength + sizeof(DBLENGTH);
  54. pTypeInfo = NULL;
  55. pObject = NULL;
  56. pBindExt = NULL;
  57. dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS;
  58. eParamIO = DBPARAMIO_NOTPARAM;
  59. dwMemOwner = (columnInfo.wType & DBTYPE_BYREF) ? DBMEMOWNER_PROVIDEROWNED
  60. : DBMEMOWNER_CLIENTOWNED;
  61. cbMaxLen = columnInfo.ulColumnSize;
  62. dwFlags = 0;
  63. wType = columnInfo.wType;
  64. bPrecision = columnInfo.bPrecision;
  65. bScale = columnInfo.bScale;
  66. offset = obStatus + sizeof(DBSTATUS);
  67. }
  68. };
  69. ///////////////////////////////////////////////////////////////////////////////
  70. //
  71. // METHOD
  72. //
  73. // CSimpleTable::CSimpleTable
  74. //
  75. // DESCRIPTION
  76. //
  77. // Constructor.
  78. //
  79. ///////////////////////////////////////////////////////////////////////////////
  80. CSimpleTable::CSimpleTable()
  81. : numColumns(0),
  82. columnInfo(NULL),
  83. stringsBuffer(NULL),
  84. columnBinding(NULL),
  85. readAccess(NULL),
  86. buffer(NULL),
  87. numRows(0),
  88. currentRow(0),
  89. endOfRowset(false)
  90. {
  91. }
  92. ///////////////////////////////////////////////////////////////////////////////
  93. //
  94. // METHOD
  95. //
  96. // CSimpleTable::~CSimpleTable
  97. //
  98. // DESCRIPTION
  99. //
  100. // Destructor.
  101. //
  102. ///////////////////////////////////////////////////////////////////////////////
  103. CSimpleTable::~CSimpleTable()
  104. {
  105. Detach();
  106. }
  107. ///////////////////////////////////////////////////////////////////////////////
  108. //
  109. // METHOD
  110. //
  111. // CSimpleTable::Attach
  112. //
  113. // DESCRIPTION
  114. //
  115. // This method binds the table object to a new rowset. The previous rowset
  116. // (if any) will be detached.
  117. //
  118. ///////////////////////////////////////////////////////////////////////////////
  119. HRESULT CSimpleTable::Attach(IRowset* pRowset)
  120. {
  121. // Make sure we didn't get a null pointer.
  122. if (!pRowset) { return E_POINTER; }
  123. // Detach the current rowset.
  124. Detach();
  125. // We don't care if this returns an error. It will just prevent
  126. // the user from updating.
  127. pRowset->QueryInterface(IID_IRowsetChange, (void**)&rowsetChange);
  128. //////////
  129. // Get the column information for the table.
  130. //////////
  131. CComPtr<IColumnsInfo> ColumnsInfo;
  132. RETURN_ERROR(pRowset->QueryInterface(IID_IColumnsInfo,
  133. (void**)&ColumnsInfo));
  134. RETURN_ERROR(ColumnsInfo->GetColumnInfo(&numColumns,
  135. &columnInfo,
  136. &stringsBuffer));
  137. //////////
  138. // Allocate the per-column data.
  139. //////////
  140. try
  141. {
  142. columnBinding = new DBBinding[numColumns];
  143. dirty.resize(numColumns);
  144. }
  145. catch (std::bad_alloc)
  146. {
  147. return E_OUTOFMEMORY;
  148. }
  149. //////////
  150. // Create a binding for each column.
  151. //////////
  152. bufferLength = 0;
  153. for (DBORDINAL i = 0; i < numColumns; ++i)
  154. {
  155. // Compute the width of the column.
  156. DBLENGTH width = columnInfo[i].ulColumnSize;
  157. // Add room for the null terminator.
  158. if (columnInfo[i].wType == DBTYPE_STR)
  159. {
  160. width += 1;
  161. }
  162. else if (columnInfo[i].wType == DBTYPE_WSTR)
  163. {
  164. width = (width + 1) * sizeof(WCHAR);
  165. }
  166. // Round to an 8-byte boundary (could peek ahead and be more efficient).
  167. width = (width + 7) >> 3 << 3;
  168. columnInfo[i].ulColumnSize = width;
  169. // We're using the pTypeInfo element to store the offset to our data.
  170. // We have to store the offset now, since it will be overwritten by
  171. // DBBinding::Initialize.
  172. columnInfo[i].pTypeInfo = (ITypeInfo*)bufferLength;
  173. columnBinding[i].Initialize(columnInfo[i], bufferLength);
  174. }
  175. //////////
  176. // Allocate a buffer for the row data.
  177. //////////
  178. buffer = new (std::nothrow) BYTE[bufferLength];
  179. if (!buffer) { return E_OUTOFMEMORY; }
  180. //////////
  181. // Create an accessor.
  182. //////////
  183. RETURN_ERROR(pRowset->QueryInterface(IID_IAccessor,
  184. (void**)&accessor));
  185. RETURN_ERROR(accessor->CreateAccessor(DBACCESSOR_ROWDATA,
  186. numColumns,
  187. columnBinding,
  188. bufferLength,
  189. &readAccess,
  190. NULL));
  191. // I used this hokey method of assigning the pointer to avoid a
  192. // dependency on atlimpl.cpp
  193. //
  194. // We do this assignment last, so that the presence of a rowset means the
  195. // entire initialization succeeded.
  196. (rowset.p = pRowset)->AddRef();
  197. endOfRowset = false;
  198. return S_OK;
  199. }
  200. ///////////////////////////////////////////////////////////////////////////////
  201. //
  202. // METHOD
  203. //
  204. // CSimpleTable::Detach
  205. //
  206. // DESCRIPTION
  207. //
  208. // Frees all the resources associated with the current rowset.
  209. //
  210. ///////////////////////////////////////////////////////////////////////////////
  211. IRowset* CSimpleTable::Detach()
  212. {
  213. ReleaseRows();
  214. delete[] buffer;
  215. buffer = NULL;
  216. delete[] columnBinding;
  217. columnBinding = NULL;
  218. CoTaskMemFree(columnInfo);
  219. columnInfo = NULL;
  220. CoTaskMemFree(stringsBuffer);
  221. stringsBuffer = NULL;
  222. accessor.Release();
  223. rowsetChange.Release();
  224. IRowset* temp = rowset;
  225. rowset.Release();
  226. return temp;
  227. }
  228. ///////////////////////////////////////////////////////////////////////////////
  229. //
  230. // METHOD
  231. //
  232. // CSimpleTable::MoveFirst
  233. //
  234. // DESCRIPTION
  235. //
  236. // Positions the cursor over the first row in the rowset.
  237. //
  238. ///////////////////////////////////////////////////////////////////////////////
  239. HRESULT CSimpleTable::MoveFirst()
  240. {
  241. if (rowset == NULL) return E_FAIL;
  242. ReleaseRows();
  243. RETURN_ERROR(rowset->RestartPosition(NULL));
  244. endOfRowset = false;
  245. return MoveNext();
  246. }
  247. ///////////////////////////////////////////////////////////////////////////////
  248. //
  249. // METHOD
  250. //
  251. // CSimpleTable::MoveNext
  252. //
  253. // DESCRIPTION
  254. //
  255. // Positions the cursor over the next row in the rowset.
  256. //
  257. ///////////////////////////////////////////////////////////////////////////////
  258. HRESULT CSimpleTable::MoveNext()
  259. {
  260. // If the data wasn't opened successfully then fail
  261. if (rowset == NULL) return E_FAIL;
  262. // Too late to save any changes.
  263. DiscardChanges();
  264. // If we've used all the rows from the last fetch, then get some more.
  265. if (++currentRow >= numRows)
  266. {
  267. ReleaseRows();
  268. // We have to do this check here, since some providers automatically
  269. // reset to the beginning of the rowset.
  270. if (endOfRowset) { return DB_S_ENDOFROWSET; }
  271. HROW* pRow = row;
  272. HRESULT hr = rowset->GetNextRows(NULL,
  273. 0,
  274. FETCH_QUANTUM,
  275. &numRows,
  276. &pRow);
  277. if (hr == DB_S_ENDOFROWSET)
  278. {
  279. // Mark that we've reached the end of the rowset.
  280. endOfRowset = true;
  281. // If we didn't get any rows, then we're really at the end.
  282. if (numRows == 0) { return DB_S_ENDOFROWSET; }
  283. }
  284. else if (FAILED(hr))
  285. {
  286. return hr;
  287. }
  288. }
  289. // Load the data into the buffer.
  290. RETURN_ERROR(rowset->GetData(row[currentRow], readAccess, buffer));
  291. return S_OK;
  292. }
  293. ///////////////////////////////////////////////////////////////////////////////
  294. //
  295. // METHOD
  296. //
  297. // CSimpleTable::Insert
  298. //
  299. // DESCRIPTION
  300. //
  301. // Inserts the contents of the accessor buffer into the rowset.
  302. //
  303. ///////////////////////////////////////////////////////////////////////////////
  304. HRESULT CSimpleTable::Insert()
  305. {
  306. // Is a rowset attached?
  307. if (!rowset) { return E_FAIL; }
  308. // Does this rowset support changes?
  309. if (!rowsetChange) { return E_NOINTERFACE; }
  310. // Get an accessor for the dirty columns.
  311. HACCESSOR writeAccess;
  312. RETURN_ERROR(CreateAccessorForWrite(&writeAccess));
  313. // Release the existing rows to make room for the new one.
  314. ReleaseRows();
  315. HRESULT hr = rowsetChange->InsertRow(NULL, writeAccess, buffer, row);
  316. if (SUCCEEDED(hr))
  317. {
  318. // The changes were save successfully, so reset the dirty vector.
  319. DiscardChanges();
  320. // We now have exactly one row in our buffer.
  321. numRows = 1;
  322. }
  323. // Release the accessor.
  324. accessor->ReleaseAccessor(writeAccess, NULL);
  325. return hr;
  326. }
  327. ///////////////////////////////////////////////////////////////////////////////
  328. //
  329. // METHOD
  330. //
  331. // CSimpleTable::Delete
  332. //
  333. // DESCRIPTION
  334. //
  335. // Deletes the current row from the rowset.
  336. //
  337. ///////////////////////////////////////////////////////////////////////////////
  338. HRESULT CSimpleTable::Delete()
  339. {
  340. // Are we positioned over a valid row?
  341. if (!rowset || currentRow >= numRows) { return E_FAIL; }
  342. // Does this rowset support changes?
  343. if (!rowsetChange) { return E_NOINTERFACE; }
  344. DBROWSTATUS rowStatus[1];
  345. return rowsetChange->DeleteRows(NULL, 1, row + currentRow, rowStatus);
  346. }
  347. ///////////////////////////////////////////////////////////////////////////////
  348. //
  349. // METHOD
  350. //
  351. // CSimpleTable::SetData
  352. //
  353. // DESCRIPTION
  354. //
  355. // Updates the current row with the data in the accessor buffer.
  356. //
  357. ///////////////////////////////////////////////////////////////////////////////
  358. HRESULT CSimpleTable::SetData()
  359. {
  360. // Are we positioned over a valid row?
  361. if (!rowset || currentRow >= numRows) { return E_FAIL; }
  362. // Does this rowset support changes?
  363. if (!rowsetChange) { return E_NOINTERFACE; }
  364. // Get an accessor for the dirty columns.
  365. HACCESSOR writeAccess;
  366. RETURN_ERROR(CreateAccessorForWrite(&writeAccess));
  367. HRESULT hr = rowsetChange->SetData(row[currentRow], writeAccess, buffer);
  368. if (SUCCEEDED(hr))
  369. {
  370. // The changes were save successfully, so reset the dirty vector.
  371. DiscardChanges();
  372. }
  373. // Release the accessor.
  374. accessor->ReleaseAccessor(writeAccess, NULL);
  375. return hr;
  376. }
  377. ///////////////////////////////////////////////////////////////////////////////
  378. //
  379. // METHOD
  380. //
  381. // CSimpleTable::GetLength
  382. //
  383. // DESCRIPTION
  384. //
  385. // Returns the length of the current value for a given column.
  386. //
  387. ///////////////////////////////////////////////////////////////////////////////
  388. DBLENGTH CSimpleTable::GetLength(DBORDINAL nOrdinal) const
  389. {
  390. return *(DBLENGTH*)((BYTE*)_GetDataPtr(nOrdinal) +
  391. columnInfo[OrdinalToColumn(nOrdinal)].ulColumnSize);
  392. }
  393. ///////////////////////////////////////////////////////////////////////////////
  394. //
  395. // METHOD
  396. //
  397. // CSimpleTable::GetOrdinal
  398. //
  399. // DESCRIPTION
  400. //
  401. // Returns the ordinal for a given column name.
  402. //
  403. ///////////////////////////////////////////////////////////////////////////////
  404. bool CSimpleTable::GetOrdinal(LPCWSTR szColumnName, DBORDINAL* pOrdinal) const
  405. {
  406. for (DBORDINAL i = 0; i < numColumns; ++i)
  407. {
  408. if (lstrcmpW(columnInfo[i].pwszName, szColumnName) == 0)
  409. {
  410. *pOrdinal = columnInfo[i].iOrdinal;
  411. return true;
  412. }
  413. }
  414. return false;
  415. }
  416. ///////////////////////////////////////////////////////////////////////////////
  417. //
  418. // METHOD
  419. //
  420. // CSimpleTable::GetStatus
  421. //
  422. // DESCRIPTION
  423. //
  424. // Returns the status code associated with the current value of a column.
  425. //
  426. ///////////////////////////////////////////////////////////////////////////////
  427. DBSTATUS CSimpleTable::GetStatus(DBORDINAL nOrdinal) const
  428. {
  429. return *(DBSTATUS*)((BYTE*)_GetDataPtr(nOrdinal) +
  430. columnInfo[OrdinalToColumn(nOrdinal)].ulColumnSize +
  431. sizeof(DBLENGTH));
  432. }
  433. ///////////////////////////////////////////////////////////////////////////////
  434. //
  435. // METHOD
  436. //
  437. // CSimpleTable::CreateAccessorForWrite
  438. //
  439. // DESCRIPTION
  440. //
  441. // Creates an accessor that is only to bound to columns that have been
  442. // modified.
  443. //
  444. ///////////////////////////////////////////////////////////////////////////////
  445. HRESULT CSimpleTable::CreateAccessorForWrite(HACCESSOR* phAccessor)
  446. {
  447. //////////
  448. // Allocate temporary space for the bindings.
  449. //////////
  450. DBBINDING* writeBind = stack_new(DBBINDING, dirty.count());
  451. //////////
  452. // Load in all the dirty columns.
  453. //////////
  454. size_t total = 0;
  455. for (size_t i = 0; total < dirty.count(); ++i)
  456. {
  457. if (dirty.test(i))
  458. {
  459. // We only want to bind the value.
  460. (writeBind[total++] = columnBinding[i]).dwPart = DBPART_VALUE;
  461. }
  462. }
  463. //////////
  464. // Create the accessor.
  465. //////////
  466. return accessor->CreateAccessor(DBACCESSOR_ROWDATA,
  467. dirty.count(),
  468. writeBind,
  469. bufferLength,
  470. phAccessor,
  471. NULL);
  472. }
  473. ///////////////////////////////////////////////////////////////////////////////
  474. //
  475. // METHOD
  476. //
  477. // CSimpleTable::_GetDataPtr
  478. //
  479. // DESCRIPTION
  480. //
  481. // Non-const version of _GetDataPtr. Marks the target column as dirty.
  482. //
  483. ///////////////////////////////////////////////////////////////////////////////
  484. void* CSimpleTable::_GetDataPtr(DBORDINAL nOrdinal)
  485. {
  486. DBORDINAL nColumn = OrdinalToColumn(nOrdinal);
  487. dirty.set(nColumn);
  488. return buffer + (ULONG_PTR)columnInfo[nColumn].pTypeInfo;
  489. }
  490. ///////////////////////////////////////////////////////////////////////////////
  491. //
  492. // METHOD
  493. //
  494. // CSimpleTable::ReleaseRows
  495. //
  496. // DESCRIPTION
  497. //
  498. // Releases all the rows returned by the last fetch.
  499. //
  500. ///////////////////////////////////////////////////////////////////////////////
  501. HRESULT CSimpleTable::ReleaseRows()
  502. {
  503. if (rowset != NULL)
  504. {
  505. HRESULT hr = rowset->ReleaseRows(numRows, row, NULL, NULL, NULL);
  506. currentRow = numRows = 0;
  507. return hr;
  508. }
  509. return S_OK;
  510. }