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.

10249 lines
282 KiB

  1. //--------------------------------------------------------------------------
  2. // Database.cpp
  3. //--------------------------------------------------------------------------
  4. #include "pch.hxx"
  5. #include "database.h"
  6. #include "stream.h"
  7. #include "types.h"
  8. #include "listen.h"
  9. #include "resource.h"
  10. #include "shlwapi.h"
  11. #include "strconst.h"
  12. #include "query.h"
  13. #include "wrapwide.h"
  14. //--------------------------------------------------------------------------
  15. // Use Heap and/or Cache
  16. //--------------------------------------------------------------------------
  17. //#ifndef DEBUG
  18. #define USEHEAP 1
  19. #define HEAPCACHE 1
  20. //#endif // DEBUG
  21. //--------------------------------------------------------------------------
  22. // Storage Block Access Macros
  23. //--------------------------------------------------------------------------
  24. #define PSTRING(_pBlock) ((LPSTR)((LPBYTE)_pBlock + sizeof(BLOCKHEADER)))
  25. #define PUSERDATA(_pHeader) (LPBYTE)((LPBYTE)_pHeader + sizeof(TABLEHEADER))
  26. //--------------------------------------------------------------------------
  27. // g_rgcbBlockSize - Block Sizes
  28. //--------------------------------------------------------------------------
  29. const static WORD g_rgcbBlockSize[BLOCK_LAST] = {
  30. sizeof(RECORDBLOCK), // BLOCK_RECORD
  31. sizeof(BLOCKHEADER), // BLOCK_FILTER
  32. 0, // BLOCK_RESERVED1
  33. sizeof(TRANSACTIONBLOCK), // BLOCK_TRANSACTION
  34. sizeof(CHAINBLOCK), // BLOCK_CHAIN
  35. sizeof(STREAMBLOCK), // BLOCK_STREAM
  36. sizeof(FREEBLOCK), // BLOCK_FREE
  37. sizeof(BLOCKHEADER), // BLOCK_ENDOFPAGE
  38. 0, // BLOCK_RESERVED2
  39. 0 // BLOCK_RESERVED3
  40. };
  41. //--------------------------------------------------------------------------
  42. // ZeroBlock
  43. //--------------------------------------------------------------------------
  44. inline void ZeroBlock(LPBLOCKHEADER pBlock, DWORD cbSize) {
  45. ZeroMemory((LPBYTE)pBlock + sizeof(BLOCKHEADER), cbSize - sizeof(BLOCKHEADER));
  46. }
  47. //--------------------------------------------------------------------------
  48. // CopyBlock
  49. //--------------------------------------------------------------------------
  50. inline void CopyBlock(LPBLOCKHEADER pDest, LPBLOCKHEADER pSource, DWORD cbSize) {
  51. CopyMemory((LPBYTE)pDest + sizeof(BLOCKHEADER), (LPBYTE)pSource + sizeof(BLOCKHEADER), cbSize - sizeof(BLOCKHEADER));
  52. }
  53. //--------------------------------------------------------------------------
  54. // SafeFreeBinding
  55. //--------------------------------------------------------------------------
  56. #define SafeFreeBinding(_pBinding) \
  57. if (_pBinding) { \
  58. FreeRecord(_pBinding); \
  59. HeapFree(_pBinding); \
  60. _pBinding = NULL; \
  61. } else
  62. //--------------------------------------------------------------------------
  63. // SafeHeapFree
  64. //--------------------------------------------------------------------------
  65. #define SafeHeapFree(_pvBuffer) \
  66. if (_pvBuffer) { \
  67. HeapFree(_pvBuffer); \
  68. _pvBuffer = NULL; \
  69. } else
  70. //--------------------------------------------------------------------------
  71. // UnmapViewOfFileWithFlush
  72. //--------------------------------------------------------------------------
  73. inline void UnmapViewOfFileWithFlush(BOOL fFlush, LPVOID pView, DWORD cbView)
  74. {
  75. // If we have a view
  76. if (pView)
  77. {
  78. // Flush It ?
  79. if (fFlush)
  80. {
  81. // Flush
  82. SideAssert(0 != FlushViewOfFile(pView, cbView));
  83. }
  84. // UnMap It
  85. SideAssert(0 != UnmapViewOfFile(pView));
  86. }
  87. }
  88. //--------------------------------------------------------------------------
  89. // PTagFromOrdinal
  90. //--------------------------------------------------------------------------
  91. inline LPCOLUMNTAG PTagFromOrdinal(LPRECORDMAP pMap, COLUMNORDINAL iColumn)
  92. {
  93. // Locals
  94. LONG lLower=0;
  95. LONG lUpper=pMap->cTags-1;
  96. LONG lCompare;
  97. WORD wMiddle;
  98. LPCOLUMNTAG pTag;
  99. // Do binary search / insert
  100. while (lLower <= lUpper)
  101. {
  102. // Set lMiddle
  103. wMiddle = (WORD)((lLower + lUpper) / 2);
  104. // Compute middle record to compare against
  105. pTag = &pMap->prgTag[(WORD)wMiddle];
  106. // Get string to compare against
  107. lCompare = (iColumn - pTag->iColumn);
  108. // If Equal, then were done
  109. if (lCompare == 0)
  110. return(pTag);
  111. // Compute upper and lower
  112. if (lCompare > 0)
  113. lLower = (LONG)(wMiddle + 1);
  114. else
  115. lUpper = (LONG)(wMiddle - 1);
  116. }
  117. // Not Found
  118. return(NULL);
  119. }
  120. //--------------------------------------------------------------------------
  121. // CDatabase::CDatabase
  122. //--------------------------------------------------------------------------
  123. CDatabase::CDatabase(void)
  124. {
  125. TraceCall("CDatabase::CDatabase");
  126. Assert(9404 == sizeof(TABLEHEADER));
  127. IF_DEBUG(DWORD dw);
  128. IF_DEBUG(dw = offsetof(TABLEHEADER, rgdwReserved2));
  129. IF_DEBUG(dw = offsetof(TABLEHEADER, rgIndexInfo));
  130. IF_DEBUG(dw = offsetof(TABLEHEADER, rgdwReserved3));
  131. Assert(offsetof(TABLEHEADER, rgdwReserved2) == 444);
  132. Assert(offsetof(TABLEHEADER, rgIndexInfo) == 8892);
  133. Assert(offsetof(TABLEHEADER, rgdwReserved3) == 9294);
  134. m_cRef = 1;
  135. m_cExtRefs = 0;
  136. m_pSchema = NULL;
  137. m_pStorage = NULL;
  138. m_pShare = NULL;
  139. m_hMutex = NULL;
  140. m_pHeader = NULL;
  141. m_hHeap = NULL;
  142. m_fDeconstruct = FALSE;
  143. m_fInMoveFile = FALSE;
  144. m_fExclusive = FALSE;
  145. m_dwProcessId = GetCurrentProcessId();
  146. m_dwQueryVersion = 0;
  147. m_pExtension = NULL;
  148. m_pUnkRelease = NULL;
  149. m_fDirty = FALSE;
  150. #ifdef BACKGROUND_MONITOR
  151. m_hMonitor = NULL;
  152. #endif
  153. m_fCompactYield = FALSE;
  154. ZeroMemory(m_rgpRecycle, sizeof(m_rgpRecycle));
  155. ZeroMemory(m_rghFilter, sizeof(m_rghFilter));
  156. InitializeCriticalSection(&m_csHeap);
  157. IF_DEBUG(m_cbHeapFree = m_cbHeapAlloc = 0);
  158. ListenThreadAddRef();
  159. DllAddRef();
  160. }
  161. //--------------------------------------------------------------------------
  162. // CDatabase::~CDatabase
  163. //--------------------------------------------------------------------------
  164. CDatabase::~CDatabase(void)
  165. {
  166. // Locals
  167. DWORD cClients=0;
  168. DWORD i;
  169. // Trace
  170. TraceCall("CDatabase::~CDatabase");
  171. // Release the Extension
  172. SafeRelease(m_pUnkRelease);
  173. // Decrement Thread Count
  174. if (NULL == m_hMutex)
  175. goto exit;
  176. #ifdef BACKGROUND_MONITOR
  177. // UnRegister...
  178. if (m_hMonitor)
  179. {
  180. // Unregister
  181. SideAssert(SUCCEEDED(UnregisterFromMonitor(this, &m_hMonitor)));
  182. }
  183. #endif
  184. // Wait for the Mutex
  185. WaitForSingleObject(m_hMutex, INFINITE);
  186. // If I have a m_pShare
  187. if (m_pStorage)
  188. {
  189. // If we have an m_pShare
  190. if (m_pShare)
  191. {
  192. // Decrement the thread Count
  193. if (m_pHeader && m_pHeader->cActiveThreads > 0)
  194. {
  195. // Decrement Thread Count
  196. m_pHeader->cActiveThreads--;
  197. }
  198. // Set State that we are de-constructing
  199. m_fDeconstruct = TRUE;
  200. // Remove Client From Array
  201. SideAssert(SUCCEEDED(_RemoveClientFromArray(m_dwProcessId, (DWORD_PTR)this)));
  202. // Save Client Count
  203. cClients = m_pShare->cClients;
  204. // No more client
  205. Assert(0 == cClients ? 0 == m_pShare->Rowsets.cUsed : TRUE);
  206. }
  207. // _CloseFileViews
  208. _CloseFileViews(FALSE);
  209. // Close the File
  210. SafeCloseHandle(m_pStorage->hMap);
  211. // Unmap the view of the memory mapped file
  212. SafeUnmapViewOfFile(m_pShare);
  213. // Unmap the view of the memory mapped file
  214. SafeCloseHandle(m_pStorage->hShare);
  215. // Close the File
  216. if(m_pStorage->hFile /*&& m_fDirty*/)
  217. {
  218. FILETIME systime;
  219. GetSystemTimeAsFileTime(&systime);
  220. SetFileTime(m_pStorage->hFile, NULL, &systime, &systime);
  221. }
  222. SafeCloseHandle(m_pStorage->hFile);
  223. // Free the mapping name
  224. SafeMemFree(m_pStorage->pszMap);
  225. // Free m_pStorage
  226. SafeMemFree(m_pStorage);
  227. }
  228. // Close all the Query Handles
  229. for (i=0; i<CMAX_INDEXES; i++)
  230. {
  231. // Close the Query
  232. CloseQuery(&m_rghFilter[i], this);
  233. }
  234. // Free the Heap Cache
  235. for (i=0; i<CC_HEAP_BUCKETS; i++)
  236. {
  237. // Locals
  238. LPMEMORYTAG pTag;
  239. LPVOID pNext;
  240. LPVOID pCurrent=(LPVOID)m_rgpRecycle[i];
  241. // While we have something to free
  242. while(pCurrent)
  243. {
  244. // Set Tag
  245. pTag = (LPMEMORYTAG)pCurrent;
  246. // Debug
  247. IF_DEBUG(m_cbHeapFree += pTag->cbSize);
  248. // Save Next
  249. pNext = pTag->pNext;
  250. // Free Current
  251. #ifdef USEHEAP
  252. ::HeapFree(m_hHeap, HEAP_NO_SERIALIZE, pCurrent);
  253. #else
  254. g_pMalloc->Free(pCurrent);
  255. #endif
  256. // Set Current
  257. pCurrent = pNext;
  258. }
  259. // Null It
  260. m_rgpRecycle[i] = NULL;
  261. }
  262. // Leaks ?
  263. Assert(m_cbHeapAlloc == m_cbHeapFree);
  264. // Release the Heap
  265. if (m_hHeap)
  266. {
  267. // HeapDestroy
  268. HeapDestroy(m_hHeap);
  269. // Don't free again
  270. m_hHeap = NULL;
  271. }
  272. // Reset Locals
  273. m_pSchema = NULL;
  274. // Release the mutex
  275. ReleaseMutex(m_hMutex);
  276. // Close the Table Mutex
  277. CloseHandle(m_hMutex);
  278. // Delete Crit Sect.
  279. DeleteCriticalSection(&m_csHeap);
  280. exit:
  281. // Release the listen thread
  282. ListenThreadRelease();
  283. // Release Dll
  284. DllRelease();
  285. // Done
  286. return;
  287. }
  288. //--------------------------------------------------------------------------
  289. // CDatabase::AddRef
  290. //--------------------------------------------------------------------------
  291. STDMETHODIMP_(ULONG) CDatabase::AddRef(void)
  292. {
  293. // Trace
  294. TraceCall("CDatabase::AddRef");
  295. // AddRef the Extension...
  296. if (m_pExtension && NULL == m_pUnkRelease)
  297. {
  298. // Keep Track of how many times I have addref'ed the extension
  299. InterlockedIncrement(&m_cExtRefs);
  300. // AddRef It
  301. m_pExtension->AddRef();
  302. }
  303. // Increment My Ref Count
  304. return InterlockedIncrement(&m_cRef);
  305. }
  306. //--------------------------------------------------------------------------
  307. // CDatabase::Release
  308. //--------------------------------------------------------------------------
  309. STDMETHODIMP_(ULONG) CDatabase::Release(void)
  310. {
  311. // Trace
  312. TraceCall("CDatabase::Release");
  313. // Release the Extension...
  314. if (m_pExtension && NULL == m_pUnkRelease && m_cExtRefs > 0)
  315. {
  316. // Keep Track of how many times I have addref'ed the extension
  317. InterlockedDecrement(&m_cExtRefs);
  318. // AddRef It
  319. m_pExtension->Release();
  320. }
  321. // Do My Release
  322. LONG cRef = InterlockedDecrement(&m_cRef);
  323. // If zero, delete
  324. if (0 == cRef)
  325. delete this;
  326. // Return Ref Count
  327. return (ULONG)cRef;
  328. }
  329. //--------------------------------------------------------------------------
  330. // CDatabase::QueryInterface
  331. //--------------------------------------------------------------------------
  332. STDMETHODIMP CDatabase::QueryInterface(REFIID riid, LPVOID *ppv)
  333. {
  334. // Locals
  335. HRESULT hr=S_OK;
  336. // Stack
  337. TraceCall("CDatabase::QueryInterface");
  338. // Find IID
  339. if (IID_IUnknown == riid)
  340. *ppv = (IUnknown *)(IDatabase *)this;
  341. else if (IID_IDatabase == riid)
  342. *ppv = (IDatabase *)this;
  343. else if (IID_CDatabase == riid)
  344. *ppv = (CDatabase *)this;
  345. else
  346. {
  347. *ppv = NULL;
  348. hr = TraceResult(E_NOINTERFACE);
  349. goto exit;
  350. }
  351. // AddRef It
  352. ((IUnknown *)*ppv)->AddRef();
  353. exit:
  354. // Done
  355. return(hr);
  356. }
  357. //--------------------------------------------------------------------------
  358. // CDatabase::Open
  359. //--------------------------------------------------------------------------
  360. HRESULT CDatabase::Open(LPCWSTR pszFile, OPENDATABASEFLAGS dwFlags,
  361. LPCTABLESCHEMA pSchema, IDatabaseExtension *pExtension)
  362. {
  363. // Locals
  364. HRESULT hr=S_OK;
  365. LPWSTR pszShare=NULL;
  366. LPWSTR pszMutex=NULL;
  367. LPWSTR pszFilePath=NULL;
  368. BOOL fNewShare;
  369. BOOL fNewFileMap;
  370. BOOL fFileCreated;
  371. BOOL fFileCorrupt = FALSE;
  372. DWORD cbInitialSize;
  373. DWORD cbMinFileSize;
  374. DWORD cchFilePath;
  375. LPCLIENTENTRY pClient;
  376. // Trace
  377. TraceCall("CDatabase::Open");
  378. // Invalid Args
  379. Assert(pszFile && pSchema);
  380. // Already Open ?
  381. if (m_hMutex)
  382. return(TraceResult(DB_E_ALREADYOPEN));
  383. // Get the Full Path
  384. IF_FAILEXIT(hr = DBGetFullPath(pszFile, &pszFilePath, &cchFilePath));
  385. // Failure
  386. if (cchFilePath >= CCHMAX_DB_FILEPATH)
  387. {
  388. SafeMemFree(pszFilePath);
  389. return(TraceResult(E_INVALIDARG));
  390. }
  391. // Don't use pszFile again
  392. pszFile = NULL;
  393. // Create the Mutex Object
  394. IF_FAILEXIT(hr = CreateSystemHandleName(pszFilePath, L"_DirectDBMutex", &pszMutex));
  395. // Create the Mutex
  396. IF_NULLEXIT(m_hMutex = CreateMutexWrapW(NULL, FALSE, pszMutex));
  397. // Wait for the Mutex
  398. WaitForSingleObject(m_hMutex, INFINITE);
  399. // Create the Heap
  400. IF_NULLEXIT(m_hHeap = HeapCreate(0, 8096, 0));
  401. // No Listen Window Yet ?
  402. IF_FAILEXIT(hr = CreateListenThread());
  403. // Save the Record Format, this should be global const data, so no need to duplicate it
  404. m_pSchema = pSchema;
  405. cbMinFileSize = sizeof(TABLEHEADER) + m_pSchema->cbUserData;
  406. // Validate
  407. IF_DEBUG(_DebugValidateRecordFormat());
  408. // Allocate m_pStorage
  409. IF_NULLEXIT(m_pStorage = (LPSTORAGEINFO)ZeroAllocate(sizeof(STORAGEINFO)));
  410. // Exclusive
  411. m_fExclusive = (ISFLAGSET(dwFlags, OPEN_DATABASE_EXCLUSEIVE) ? TRUE : FALSE);
  412. // Open the File
  413. IF_FAILEXIT(hr = DBOpenFile(pszFilePath, ISFLAGSET(dwFlags, OPEN_DATABASE_NOCREATE), m_fExclusive, &fFileCreated, &m_pStorage->hFile));
  414. // Create the Mutex Object
  415. IF_FAILEXIT(hr = CreateSystemHandleName(pszFilePath, L"_DirectDBShare", &pszShare));
  416. // Open the file mapping
  417. IF_FAILEXIT(hr = DBOpenFileMapping(INVALID_HANDLE_VALUE, pszShare, sizeof(SHAREDDATABASE), &fNewShare, &m_pStorage->hShare, (LPVOID *)&m_pShare));
  418. // New Share
  419. if (TRUE == fNewShare)
  420. {
  421. // Zero Out m_pShare
  422. ZeroMemory(m_pShare, sizeof(SHAREDDATABASE));
  423. // Copy the file name
  424. StrCpyNW(m_pShare->szFile, pszFilePath, ARRAYSIZE(m_pShare->szFile));
  425. // Fixup the Query Table Version
  426. m_pShare->dwQueryVersion = 1;
  427. }
  428. // Too Many clients ?
  429. if (m_pShare->cClients == CMAX_CLIENTS)
  430. {
  431. hr = TraceResult(E_FAIL);
  432. goto exit;
  433. }
  434. // Readability
  435. pClient = &m_pShare->rgClient[m_pShare->cClients];
  436. // Initialize the Entry
  437. ZeroMemory(pClient, sizeof(CLIENTENTRY));
  438. // Get the listen window
  439. GetListenWindow(&pClient->hwndListen);
  440. // Register Myself
  441. pClient->dwProcessId = m_dwProcessId;
  442. pClient->pDB = (DWORD_PTR)this;
  443. // Incrment Count
  444. m_pShare->cClients++;
  445. // Create the Mutex Object
  446. IF_FAILEXIT(hr = CreateSystemHandleName(m_pShare->szFile, L"_DirectDBFileMap", &m_pStorage->pszMap));
  447. // Get the file size
  448. IF_FAILEXIT(hr = DBGetFileSize(m_pStorage->hFile, &cbInitialSize));
  449. // If the file is too small to handle the header, then we either have a corrupt file
  450. // that is way too corrupt to be saved, or we have an invalid file. Take some
  451. // defensive measures to make sure we can continue
  452. if (!fFileCreated && (cbInitialSize < cbMinFileSize))
  453. {
  454. fFileCorrupt = TRUE;
  455. // If we can't reset or create, then must exit
  456. if (ISFLAGSET(dwFlags, OPEN_DATABASE_NORESET) || ISFLAGSET(dwFlags, OPEN_DATABASE_NOCREATE))
  457. {
  458. IF_FAILEXIT(hr = HRESULT_FROM_WIN32(ERROR_FILE_CORRUPT));
  459. }
  460. // If we can reset, let's make sure the mapping file is the appropriate size
  461. else
  462. {
  463. cbInitialSize = 0;
  464. }
  465. }
  466. // Validate
  467. Assert(fFileCreated ? 0 == cbInitialSize : TRUE);
  468. // If zero, must be a new file or corrupt one(lets create with 1 byte header)
  469. if (0 == cbInitialSize)
  470. {
  471. Assert(fFileCorrupt || fFileCreated);
  472. // Initial Size
  473. m_pStorage->cbFile = cbMinFileSize;
  474. }
  475. // Otherwise, Set m_pStorage->cbFile
  476. else
  477. m_pStorage->cbFile = cbInitialSize;
  478. // Open the file mapping
  479. IF_FAILEXIT(hr = DBOpenFileMapping(m_pStorage->hFile, m_pStorage->pszMap, m_pStorage->cbFile, &fNewFileMap, &m_pStorage->hMap, NULL));
  480. // _InitializeFileViews
  481. IF_FAILEXIT(hr = _InitializeFileViews());
  482. // New File or corrupt?
  483. if (fFileCreated || fFileCorrupt)
  484. {
  485. // Validate
  486. Assert ((fFileCreated && fNewFileMap) || fFileCorrupt);
  487. // Reset Table Header
  488. IF_FAILEXIT(hr = _ResetTableHeader());
  489. }
  490. // Otherwise
  491. else
  492. {
  493. // Adjust pHeader->faNextAllocate for previous versions...
  494. if (0 == m_pHeader->faNextAllocate)
  495. {
  496. // The next storage grow address
  497. m_pHeader->faNextAllocate = m_pStorage->cbFile;
  498. }
  499. // faNextAllocate is Invalid
  500. else if (m_pHeader->faNextAllocate > m_pStorage->cbFile)
  501. {
  502. // Assert
  503. AssertSz(FALSE, "m_pHeader->faNextAllocate is beyond the end of the file.");
  504. // The next storage grow address
  505. m_pHeader->faNextAllocate = m_pStorage->cbFile;
  506. // Check for Corruption
  507. m_pHeader->fCorruptCheck = FALSE;
  508. }
  509. }
  510. // Validate File Versions and Signatures
  511. IF_FAILEXIT(hr = _ValidateFileVersions(dwFlags));
  512. // Reload query table
  513. IF_FAILEXIT(hr = _BuildQueryTable());
  514. // No Indexes, must need to initialize index info
  515. if (0 == m_pHeader->cIndexes)
  516. {
  517. // Copy Primary Index Information...
  518. CopyMemory(&m_pHeader->rgIndexInfo[IINDEX_PRIMARY], m_pSchema->pPrimaryIndex, sizeof(TABLEINDEX));
  519. // We now have one index
  520. m_pHeader->cIndexes = 1;
  521. // Validate
  522. Assert(IINDEX_PRIMARY == m_pHeader->rgiIndex[0] && 0 == m_pHeader->rgcRecords[IINDEX_PRIMARY] && 0 == m_pHeader->rgfaIndex[IINDEX_PRIMARY]);
  523. }
  524. // Otherwise, if definition of primary index has changed!!!
  525. else if (S_FALSE == CompareTableIndexes(&m_pHeader->rgIndexInfo[IINDEX_PRIMARY], m_pSchema->pPrimaryIndex))
  526. {
  527. // Copy Primary Index Information...
  528. CopyMemory(&m_pHeader->rgIndexInfo[IINDEX_PRIMARY], m_pSchema->pPrimaryIndex, sizeof(TABLEINDEX));
  529. // Rebuild the Primary Index...
  530. IF_FAILEXIT(hr = _RebuildIndex(IINDEX_PRIMARY));
  531. }
  532. // New Share
  533. if (TRUE == fNewFileMap)
  534. {
  535. // Don't Free Transact list if transaction block size has changed
  536. if (m_pHeader->wTransactSize == sizeof(TRANSACTIONBLOCK))
  537. {
  538. // Transaction Trail should be free
  539. _CleanupTransactList();
  540. }
  541. // Reset Everything
  542. m_pHeader->faTransactHead = m_pHeader->faTransactTail = m_pHeader->cTransacts = 0;
  543. // Set Transaction Block Size
  544. m_pHeader->wTransactSize = sizeof(TRANSACTIONBLOCK);
  545. // Bad close ?
  546. if (m_pHeader->cActiveThreads > 0)
  547. {
  548. // Increment Bad Close Count
  549. m_pHeader->cBadCloses++;
  550. // Reset Process Count
  551. m_pHeader->cActiveThreads = 0;
  552. }
  553. }
  554. // Otherwise, if the corrupt bit is set, run the repair code
  555. if (TRUE == m_pHeader->fCorrupt || FALSE == m_pHeader->fCorruptCheck)
  556. {
  557. // Lets validate the tree
  558. IF_FAILEXIT(hr = _CheckForCorruption());
  559. // Better not be corrupt
  560. Assert(FALSE == m_pHeader->fCorrupt);
  561. // Checked for Corruption
  562. m_pHeader->fCorruptCheck = TRUE;
  563. }
  564. // Initialize Database Extension
  565. _InitializeExtension(dwFlags, pExtension);
  566. #ifdef BACKGROUND_MONITOR
  567. // If nomonitor is not set
  568. if (!ISFLAGSET(dwFlags, OPEN_DATABASE_NOMONITOR))
  569. {
  570. // Keep on eye on me...
  571. IF_FAILEXIT(hr = RegisterWithMonitor(this, &m_hMonitor));
  572. }
  573. #endif
  574. // Increment Number of Processes
  575. m_pHeader->cActiveThreads++;
  576. exit:
  577. // Release the Mutex
  578. if (m_hMutex)
  579. ReleaseMutex(m_hMutex);
  580. // Cleanup
  581. SafeMemFree(pszShare);
  582. SafeMemFree(pszMutex);
  583. SafeMemFree(pszFilePath);
  584. // Done
  585. return(hr);
  586. }
  587. #ifdef BACKGROUND_MONITOR
  588. //--------------------------------------------------------------------------
  589. // CDatabase::DoBackgroundMonitor
  590. //--------------------------------------------------------------------------
  591. HRESULT CDatabase::DoBackgroundMonitor(void)
  592. {
  593. // Locals
  594. HRESULT hr=S_OK;
  595. DWORD i;
  596. LPFILEVIEW pView;
  597. LPFILEVIEW pNext;
  598. BOOL fUnmapViews=TRUE;
  599. // bobn, 7/8/99
  600. // Occasionally, m_pSchema can be invalid due to a race condition in SMAPI
  601. // We need to protect against that. We are too close to ship to
  602. // find the race condition and re-architect the product to fix completely
  603. // this corner case.
  604. if (IsBadReadPtr(m_pSchema, sizeof(TABLESCHEMA)))
  605. return(TraceResult(E_FAIL));
  606. // No Mutex
  607. if (NULL == m_hMutex)
  608. return(TraceResult(E_FAIL));
  609. // Leave Spin Lock
  610. if (WAIT_OBJECT_0 != WaitForSingleObject(m_hMutex, 500))
  611. return(S_OK);
  612. // No Header ?
  613. if (NULL == m_pHeader)
  614. {
  615. hr = TraceResult(E_FAIL);
  616. goto exit;
  617. }
  618. // No Storage
  619. if (NULL == m_pStorage)
  620. {
  621. hr = TraceResult(E_FAIL);
  622. goto exit;
  623. }
  624. #if 0
  625. // Un-map file views ?
  626. if (0 == m_pStorage->tcMonitor)
  627. {
  628. // I will unmap all views
  629. fUnmapViews = FALSE;
  630. }
  631. #endif
  632. // Always Flush the header
  633. m_fDirty = TRUE;
  634. if (0 == FlushViewOfFile(m_pHeader, sizeof(TABLEHEADER) + m_pSchema->cbUserData))
  635. {
  636. hr = TraceResult(DB_E_FLUSHVIEWOFFILE);
  637. goto exit;
  638. }
  639. // Walk through prgView
  640. for (i=0; i<m_pStorage->cAllocated; i++)
  641. {
  642. // Readability
  643. pView = &m_pStorage->prgView[i];
  644. // Is Mapped ?
  645. if (pView->pbView)
  646. {
  647. // Flush the header
  648. if (0 == FlushViewOfFile(pView->pbView, pView->cbView))
  649. {
  650. hr = TraceResult(DB_E_FLUSHVIEWOFFILE);
  651. goto exit;
  652. }
  653. // Flush and UnMap the Views...
  654. if (TRUE == fUnmapViews)
  655. {
  656. // Unmap
  657. UnmapViewOfFile(pView->pbView);
  658. // No view
  659. pView->pbView = NULL;
  660. // No view
  661. pView->faView = pView->cbView = 0;
  662. }
  663. }
  664. }
  665. // Walk through pSpecial
  666. pView = m_pStorage->pSpecial;
  667. // While we have a Current
  668. while (pView)
  669. {
  670. // Save pNext
  671. pNext = pView->pNext;
  672. // Is Mapped ?
  673. if (pView->pbView)
  674. {
  675. // Flush the header
  676. if (0 == FlushViewOfFile(pView->pbView, pView->cbView))
  677. {
  678. hr = TraceResult(DB_E_FLUSHVIEWOFFILE);
  679. goto exit;
  680. }
  681. // Flush and UnMap the Views...
  682. if (TRUE == fUnmapViews)
  683. {
  684. // Unmap
  685. UnmapViewOfFile(pView->pbView);
  686. // No view
  687. pView->pbView = NULL;
  688. // No view
  689. pView->faView = pView->cbView = 0;
  690. // Free pView
  691. HeapFree(pView);
  692. }
  693. }
  694. // Goto Next
  695. pView = pNext;
  696. }
  697. // Reset Head
  698. if (TRUE == fUnmapViews)
  699. {
  700. // No more special views
  701. m_pStorage->pSpecial = NULL;
  702. // No more special views
  703. m_pStorage->cSpecial = 0;
  704. // No Mapped Views
  705. m_pStorage->cbMappedViews = 0;
  706. // No Mapped Special Views
  707. m_pStorage->cbMappedSpecial = 0;
  708. }
  709. // Save tcMonitor
  710. m_pStorage->tcMonitor = GetTickCount();
  711. exit:
  712. // Release the Mutex
  713. ReleaseMutex(m_hMutex);
  714. // Done
  715. return(hr);
  716. }
  717. #endif
  718. //--------------------------------------------------------------------------
  719. // CDatabase::_CloseFileViews
  720. //--------------------------------------------------------------------------
  721. HRESULT CDatabase::_CloseFileViews(BOOL fFlush)
  722. {
  723. // Locals
  724. LPFILEVIEW pCurrent;
  725. LPFILEVIEW pNext;
  726. DWORD i;
  727. // Trace
  728. TraceCall("CDatabase::_CloseFileViews");
  729. // Unmap the view of the header
  730. UnmapViewOfFileWithFlush(fFlush, m_pHeader, sizeof(TABLEHEADER) + m_pSchema->cbUserData);
  731. // Walk through prgView
  732. for (i = 0; i < m_pStorage->cAllocated; i++)
  733. {
  734. // Readability
  735. pCurrent = &m_pStorage->prgView[i];
  736. // Unmap with possible flush
  737. UnmapViewOfFileWithFlush(fFlush, pCurrent->pbView, pCurrent->cbView);
  738. }
  739. // Free prgView
  740. SafeHeapFree(m_pStorage->prgView);
  741. // No Views are mapped
  742. m_pStorage->cbMappedViews = 0;
  743. // Zero cAllocate
  744. m_pStorage->cAllocated = 0;
  745. // Walk through pSpecial
  746. pCurrent = m_pStorage->pSpecial;
  747. // While we have a Current
  748. while (pCurrent)
  749. {
  750. // Save pNext
  751. pNext = pCurrent->pNext;
  752. // Unmap the view
  753. UnmapViewOfFileWithFlush(fFlush, pCurrent->pbView, pCurrent->cbView);
  754. // Free pCurrent
  755. HeapFree(pCurrent);
  756. // Goto Next
  757. pCurrent = pNext;
  758. }
  759. // Reset Head
  760. m_pStorage->pSpecial = NULL;
  761. // No Special Mapped
  762. m_pStorage->cbMappedSpecial = 0;
  763. // No Special
  764. m_pStorage->cSpecial = 0;
  765. // Done
  766. return(S_OK);
  767. }
  768. //--------------------------------------------------------------------------
  769. // CDatabase::_InitializeFileViews
  770. //--------------------------------------------------------------------------
  771. HRESULT CDatabase::_InitializeFileViews(void)
  772. {
  773. // Locals
  774. HRESULT hr=S_OK;
  775. FILEADDRESS faView;
  776. DWORD cbView;
  777. // Trace
  778. TraceCall("CDatabase::_InitializeFileViews");
  779. // Validate State
  780. Assert(NULL == m_pStorage->prgView && NULL == m_pStorage->pSpecial);
  781. // Set cAllocated
  782. m_pStorage->cAllocated = (m_pStorage->cbFile / CB_MAPPED_VIEW) + 1;
  783. // Allocate prgView
  784. IF_NULLEXIT(m_pStorage->prgView = (LPFILEVIEW)PHeapAllocate(HEAP_ZERO_MEMORY, sizeof(FILEVIEW) * m_pStorage->cAllocated));
  785. // Set faView
  786. faView = 0;
  787. // Set cbView
  788. cbView = (sizeof(TABLEHEADER) + m_pSchema->cbUserData);
  789. // Map m_pHeader into its own view...
  790. IF_FAILEXIT(hr = DBMapViewOfFile(m_pStorage->hMap, m_pStorage->cbFile, &faView, &cbView, (LPVOID *)&m_pHeader));
  791. exit:
  792. // Done
  793. return(hr);
  794. }
  795. //--------------------------------------------------------------------------
  796. // CDatabase::_InitializeExtension
  797. //--------------------------------------------------------------------------
  798. HRESULT CDatabase::_InitializeExtension(OPENDATABASEFLAGS dwFlags,
  799. IDatabaseExtension *pExtension)
  800. {
  801. // Locals
  802. HRESULT hr=S_OK;
  803. // Trace
  804. TraceCall("CDatabase::_InitializeExtension");
  805. // Extension Allowed
  806. if (ISFLAGSET(dwFlags, OPEN_DATABASE_NOEXTENSION))
  807. goto exit;
  808. // Doesn't have an extension ?
  809. if (FALSE == ISFLAGSET(m_pSchema->dwFlags, TSF_HASEXTENSION))
  810. goto exit;
  811. // Create the Extension Object
  812. if (pExtension)
  813. {
  814. // Assume It
  815. m_pExtension = pExtension;
  816. // Can I add ref it ?
  817. if (FALSE == ISFLAGSET(dwFlags, OPEN_DATABASE_NOADDREFEXT))
  818. {
  819. // Release It
  820. IF_FAILEXIT(hr = m_pExtension->QueryInterface(IID_IUnknown, (LPVOID *)&m_pUnkRelease));
  821. }
  822. }
  823. // Otherwise, CoCreate... the extension
  824. else
  825. {
  826. // CoCreate the Extension Object
  827. IF_FAILEXIT(hr = CoCreateInstance(*m_pSchema->pclsidExtension, NULL, CLSCTX_INPROC_SERVER, IID_IDatabaseExtension, (LPVOID *)&m_pExtension));
  828. // Release It
  829. IF_FAILEXIT(hr = m_pExtension->QueryInterface(IID_IUnknown, (LPVOID *)&m_pUnkRelease));
  830. // Release m_pExtension
  831. m_pExtension->Release();
  832. }
  833. // Initialize the Extension
  834. m_pExtension->Initialize(this);
  835. exit:
  836. // Must have succeeded
  837. Assert(SUCCEEDED(hr));
  838. // Done
  839. return(hr);
  840. }
  841. //--------------------------------------------------------------------------
  842. // CDatabase::GetClientCount
  843. //--------------------------------------------------------------------------
  844. STDMETHODIMP CDatabase::GetClientCount(LPDWORD pcClients)
  845. {
  846. // Trace
  847. TraceCall("CDatabase::GetClientCount");
  848. // Multiple Clients ?
  849. if (m_pShare)
  850. *pcClients = m_pShare->cClients;
  851. else
  852. *pcClients = 0;
  853. // Done
  854. return(S_OK);
  855. }
  856. //--------------------------------------------------------------------------
  857. // CDatabase::Lock
  858. //--------------------------------------------------------------------------
  859. STDMETHODIMP CDatabase::Lock(LPHLOCK phLock)
  860. {
  861. // Locals
  862. HRESULT hr=S_OK;
  863. BYTE fDecWaiting=FALSE;
  864. // Trace
  865. TraceCall("CDatabase::Lock");
  866. // Initialize
  867. *phLock = NULL;
  868. // If Compacting...
  869. if (TRUE == m_pShare->fCompacting)
  870. {
  871. // Increment Waiting for Lock
  872. InterlockedIncrement(&m_pShare->cWaitingForLock);
  873. // fDecWaiting
  874. fDecWaiting = TRUE;
  875. }
  876. // Leave Spin Lock
  877. WaitForSingleObject(m_hMutex, INFINITE);
  878. // Decrement Waiting for lock ?
  879. if (fDecWaiting)
  880. {
  881. // Increment Waiting for Lock
  882. InterlockedDecrement(&m_pShare->cWaitingForLock);
  883. }
  884. // No Header ?
  885. if (NULL == m_pHeader)
  886. {
  887. // Try to re-open the file
  888. hr = DoInProcessInvoke(INVOKE_CREATEMAP);
  889. // Failure
  890. if (FAILED(hr))
  891. {
  892. // Leave Spin Lock
  893. ReleaseMutex(m_hMutex);
  894. // Trace
  895. TraceResult(hr);
  896. // Done
  897. goto exit;
  898. }
  899. }
  900. // Extension
  901. if (m_pExtension)
  902. {
  903. // OnLock Extension...
  904. m_pExtension->OnLock();
  905. }
  906. // Check for Corruption...
  907. if (TRUE == m_pHeader->fCorrupt)
  908. {
  909. // Try to Repair Corruption
  910. hr = _CheckForCorruption();
  911. // Failure
  912. if (FAILED(hr))
  913. {
  914. // Leave Spin Lock
  915. ReleaseMutex(m_hMutex);
  916. // Trace
  917. TraceResult(hr);
  918. // Done
  919. goto exit;
  920. }
  921. }
  922. // Need to reload quieries
  923. if (m_dwQueryVersion != m_pShare->dwQueryVersion)
  924. {
  925. // Reload query table
  926. IF_FAILEXIT(hr = _BuildQueryTable());
  927. }
  928. // Increment Queue Notify count
  929. m_pShare->cNotifyLock++;
  930. #ifdef BACKGROUND_MONITOR
  931. // Reset tcMonitor
  932. m_pStorage->tcMonitor = 0;
  933. #endif
  934. // Don't Unlock Again
  935. *phLock = (HLOCK)m_hMutex;
  936. exit:
  937. // Done
  938. return(hr);
  939. }
  940. //--------------------------------------------------------------------------
  941. // CDatabase::Unlock
  942. //--------------------------------------------------------------------------
  943. STDMETHODIMP CDatabase::Unlock(LPHLOCK phLock)
  944. {
  945. // Trace
  946. TraceCall("CDatabase::Unlock");
  947. // Not Null
  948. if (*phLock)
  949. {
  950. // Extension
  951. if (m_pExtension)
  952. {
  953. // OnUnlock Extension...
  954. m_pExtension->OnUnlock();
  955. }
  956. // Unlock Notify
  957. m_pShare->cNotifyLock--;
  958. // If there are still refs, don't send notifications yet...
  959. if (0 == m_pShare->cNotifyLock && FALSE == m_pHeader->fCorrupt)
  960. {
  961. // Dispatch Pending
  962. _DispatchPendingNotifications();
  963. }
  964. // Validate phLock
  965. Assert(*phLock == (HLOCK)m_hMutex);
  966. // Leave Spin Lock
  967. ReleaseMutex(m_hMutex);
  968. // Don't Unlock Again
  969. *phLock = NULL;
  970. }
  971. // Done
  972. return(S_OK);
  973. }
  974. //--------------------------------------------------------------------------
  975. // CDatabase::_BuildQueryTable
  976. //--------------------------------------------------------------------------
  977. HRESULT CDatabase::_BuildQueryTable(void)
  978. {
  979. // Locals
  980. HRESULT hr=S_OK;
  981. ULONG i;
  982. INDEXORDINAL iIndex;
  983. LPBLOCKHEADER pBlock;
  984. // Trace
  985. TraceCall("CDatabase::_BuildQueryTable");
  986. // Versions should be different
  987. Assert(m_dwQueryVersion != m_pShare->dwQueryVersion);
  988. // Collapse the Ordinal Array
  989. for (i=0; i<m_pHeader->cIndexes; i++)
  990. {
  991. // Set iIndex
  992. iIndex = m_pHeader->rgiIndex[i];
  993. // Close the Current Filters
  994. CloseQuery(&m_rghFilter[iIndex], this);
  995. // Filter
  996. if (m_pHeader->rgfaFilter[iIndex])
  997. {
  998. // Validate File Address
  999. IF_FAILEXIT(hr = _GetBlock(BLOCK_STRING, m_pHeader->rgfaFilter[iIndex], (LPVOID *)&pBlock));
  1000. // Load the Query String
  1001. if (FAILED(ParseQuery(PSTRING(pBlock), m_pSchema, &m_rghFilter[iIndex], this)))
  1002. {
  1003. // Null
  1004. m_rghFilter[iIndex] = NULL;
  1005. }
  1006. }
  1007. }
  1008. // Save New Version
  1009. m_dwQueryVersion = m_pShare->dwQueryVersion;
  1010. exit:
  1011. // Done
  1012. return(hr);
  1013. }
  1014. //--------------------------------------------------------------------------
  1015. // CDatabase::_ResetTableHeader
  1016. //--------------------------------------------------------------------------
  1017. HRESULT CDatabase::_ResetTableHeader(void)
  1018. {
  1019. // Locals
  1020. HRESULT hr=S_OK;
  1021. // Trace
  1022. TraceCall("CDatabase::_ResetTableHeader");
  1023. // Zero out the Table Header + UserData
  1024. ZeroMemory(m_pHeader, sizeof(TABLEHEADER) + m_pSchema->cbUserData);
  1025. // Set the File Signature
  1026. m_pHeader->dwSignature = BTREE_SIGNATURE;
  1027. // Set the Major Version
  1028. m_pHeader->dwMajorVersion = BTREE_VERSION;
  1029. // Store the size of the user data
  1030. m_pHeader->cbUserData = m_pSchema->cbUserData;
  1031. // Set faNextAllocate
  1032. m_pHeader->faNextAllocate = sizeof(TABLEHEADER) + m_pSchema->cbUserData;
  1033. // Initialize ID Generator
  1034. m_pHeader->dwNextId = 1;
  1035. // No need to do the corruption check, its a new file...
  1036. m_pHeader->fCorruptCheck = TRUE;
  1037. // Store the clsidExtension
  1038. CopyMemory(&m_pHeader->clsidExtension, m_pSchema->pclsidExtension, sizeof(CLSID));
  1039. // Store the Version
  1040. m_pHeader->dwMinorVersion = m_pSchema->dwMinorVersion;
  1041. // Done
  1042. return(S_OK);
  1043. }
  1044. //--------------------------------------------------------------------------
  1045. // CDatabase::_ValidateFileVersions
  1046. //--------------------------------------------------------------------------
  1047. HRESULT CDatabase::_ValidateFileVersions(OPENDATABASEFLAGS dwFlags)
  1048. {
  1049. // Locals
  1050. HRESULT hr=S_OK;
  1051. // Trace
  1052. TraceCall("CDatabase::_ValidateFileVersions");
  1053. // Signature better match
  1054. if (m_pHeader->dwSignature != BTREE_SIGNATURE)
  1055. {
  1056. hr = TraceResult(DB_E_INVALIDFILESIGNATURE);
  1057. goto exit;
  1058. }
  1059. // Validate the Major Version
  1060. if (m_pHeader->dwMajorVersion != BTREE_VERSION)
  1061. {
  1062. hr = TraceResult(DB_E_BADMAJORVERSION);
  1063. goto exit;
  1064. }
  1065. // Validate the Minor Version
  1066. if (m_pHeader->dwMinorVersion != m_pSchema->dwMinorVersion)
  1067. {
  1068. hr = TraceResult(DB_E_BADMINORVERSION);
  1069. goto exit;
  1070. }
  1071. // Validate the Minor Version
  1072. if (FALSE == IsEqualCLSID(m_pHeader->clsidExtension, *m_pSchema->pclsidExtension))
  1073. {
  1074. hr = TraceResult(DB_E_BADEXTENSIONCLSID);
  1075. goto exit;
  1076. }
  1077. exit:
  1078. // Can I Reset
  1079. if (FALSE == ISFLAGSET(dwFlags, OPEN_DATABASE_NORESET))
  1080. {
  1081. // Failed and reset if bad version ?
  1082. if (FAILED(hr) && ISFLAGSET(m_pSchema->dwFlags, TSF_RESETIFBADVERSION))
  1083. {
  1084. // Reset the Table Header
  1085. hr = _ResetTableHeader();
  1086. }
  1087. }
  1088. // Done
  1089. return(hr);
  1090. }
  1091. //--------------------------------------------------------------------------
  1092. // CDatabase::PHeapAllocate
  1093. //--------------------------------------------------------------------------
  1094. LPVOID CDatabase::PHeapAllocate(DWORD dwFlags, DWORD cbSize)
  1095. {
  1096. // Locals
  1097. LPMEMORYTAG pTag;
  1098. // Trace
  1099. TraceCall("CDatabase::PHeapAllocate");
  1100. // Increment the Size Enough to Store a Header
  1101. cbSize += sizeof(MEMORYTAG);
  1102. // Block is a too big to recycle ?
  1103. #ifdef HEAPCACHE
  1104. if (cbSize >= CB_MAX_HEAP_BUCKET)
  1105. {
  1106. #endif
  1107. // Thread Safety
  1108. EnterCriticalSection(&m_csHeap);
  1109. // Allocate the Block
  1110. #ifdef USEHEAP
  1111. LPVOID pBlock = HeapAlloc(m_hHeap, dwFlags | HEAP_NO_SERIALIZE, cbSize);
  1112. #else
  1113. LPVOID pBlock = ZeroAllocate(cbSize);
  1114. #endif
  1115. // Debug
  1116. IF_DEBUG(m_cbHeapAlloc += cbSize);
  1117. // Thread Safety
  1118. LeaveCriticalSection(&m_csHeap);
  1119. // Set pTag
  1120. pTag = (LPMEMORYTAG)pBlock;
  1121. #ifdef HEAPCACHE
  1122. }
  1123. // Otherwise
  1124. else
  1125. {
  1126. // Compute Free Block Bucket
  1127. WORD iBucket = ((WORD)(cbSize / CB_HEAP_BUCKET));
  1128. // Decrement iBucket ?
  1129. if (0 == (cbSize % CB_HEAP_BUCKET))
  1130. {
  1131. // Previous Bucket
  1132. iBucket--;
  1133. }
  1134. // Adjust cbBlock to fit completly into it's bucket
  1135. cbSize = ((iBucket + 1) * CB_HEAP_BUCKET);
  1136. // Thread Safety
  1137. EnterCriticalSection(&m_csHeap);
  1138. // Is there a block in this Bucket ?
  1139. if (m_rgpRecycle[iBucket])
  1140. {
  1141. // Use this block
  1142. pTag = (LPMEMORYTAG)m_rgpRecycle[iBucket];
  1143. // Validate Size
  1144. Assert(cbSize == pTag->cbSize);
  1145. // Fixup m_rgpRecycle
  1146. m_rgpRecycle[iBucket] = (LPBYTE)pTag->pNext;
  1147. // Zero
  1148. if (ISFLAGSET(dwFlags, HEAP_ZERO_MEMORY))
  1149. {
  1150. // Zero
  1151. ZeroMemory((LPBYTE)pTag, cbSize);
  1152. }
  1153. }
  1154. // Otherwise, allocate
  1155. else
  1156. {
  1157. // Allocate the Block
  1158. #ifdef USEHEAP
  1159. LPVOID pBlock = HeapAlloc(m_hHeap, dwFlags | HEAP_NO_SERIALIZE, cbSize);
  1160. #else
  1161. LPVOID pBlock = ZeroAllocate(cbSize);
  1162. #endif
  1163. // Debug
  1164. IF_DEBUG(m_cbHeapAlloc += cbSize);
  1165. // Set pTag
  1166. pTag = (LPMEMORYTAG)pBlock;
  1167. }
  1168. // Thread Safety
  1169. LeaveCriticalSection(&m_csHeap);
  1170. }
  1171. #endif
  1172. // No pTag
  1173. if (NULL == pTag)
  1174. return(NULL);
  1175. // Fixup the Block Size
  1176. pTag->cbSize = cbSize;
  1177. // Set Signature
  1178. pTag->dwSignature = MEMORY_GUARD_SIGNATURE;
  1179. // Done
  1180. return((LPBYTE)pTag + sizeof(MEMORYTAG));
  1181. }
  1182. //--------------------------------------------------------------------------
  1183. // CDatabase::HeapFree
  1184. //--------------------------------------------------------------------------
  1185. STDMETHODIMP CDatabase::HeapFree(LPVOID pBlock)
  1186. {
  1187. // Locals
  1188. LPMEMORYTAG pTag;
  1189. // Trace
  1190. TraceCall("CDatabase::HeapFree");
  1191. // No Buffer
  1192. if (NULL == pBlock)
  1193. return(S_OK);
  1194. // Set pTag
  1195. pTag = (LPMEMORYTAG)((LPBYTE)pBlock - sizeof(MEMORYTAG));
  1196. // Is Valid Block ?
  1197. Assert(pTag->dwSignature == MEMORY_GUARD_SIGNATURE);
  1198. // Block is a too big to recycle ?
  1199. #ifdef HEAPCACHE
  1200. if (pTag->cbSize >= CB_MAX_HEAP_BUCKET)
  1201. {
  1202. #endif
  1203. // Thread Safety
  1204. EnterCriticalSection(&m_csHeap);
  1205. // Debug
  1206. IF_DEBUG(m_cbHeapFree += pTag->cbSize);
  1207. // Allocate the Block
  1208. #ifdef USEHEAP
  1209. ::HeapFree(m_hHeap, HEAP_NO_SERIALIZE, pTag);
  1210. #else
  1211. g_pMalloc->Free(pTag);
  1212. #endif
  1213. // Thread Safety
  1214. LeaveCriticalSection(&m_csHeap);
  1215. #ifdef HEAPCACHE
  1216. }
  1217. // Otherwise, cache It
  1218. else
  1219. {
  1220. // Compute Free Block Bucket
  1221. WORD iBucket = ((WORD)(pTag->cbSize / CB_HEAP_BUCKET)) - 1;
  1222. // Must be an integral size of a bucket
  1223. Assert((pTag->cbSize % CB_HEAP_BUCKET) == 0);
  1224. // Thread Safety
  1225. EnterCriticalSection(&m_csHeap);
  1226. // Set Next
  1227. pTag->pNext = m_rgpRecycle[iBucket];
  1228. // Set the Head
  1229. m_rgpRecycle[iBucket] = (LPBYTE)pTag;
  1230. // Thread Safety
  1231. LeaveCriticalSection(&m_csHeap);
  1232. }
  1233. #endif
  1234. // Done
  1235. return(S_OK);
  1236. }
  1237. //--------------------------------------------------------------------------
  1238. // CDatabase::GetIndexInfo
  1239. //--------------------------------------------------------------------------
  1240. STDMETHODIMP CDatabase::GetIndexInfo(INDEXORDINAL iIndex, LPSTR *ppszFilter,
  1241. LPTABLEINDEX pIndex)
  1242. {
  1243. // Locals
  1244. HRESULT hr=S_OK;
  1245. DWORD i;
  1246. HLOCK hLock=NULL;
  1247. LPBLOCKHEADER pBlock;
  1248. // Trace
  1249. TraceCall("CDatabase::GetIndexInfo");
  1250. // Lock
  1251. IF_FAILEXIT(hr = Lock(&hLock));
  1252. // Collapse the Ordinal Array
  1253. for (i=0; i<m_pHeader->cIndexes; i++)
  1254. {
  1255. // Get iIndex
  1256. if (iIndex == m_pHeader->rgiIndex[i])
  1257. {
  1258. // Copy the Index Information
  1259. CopyMemory(pIndex, &m_pHeader->rgIndexInfo[iIndex], sizeof(TABLEINDEX));
  1260. // Get the Filters ?
  1261. if (ppszFilter && m_pHeader->rgfaFilter[iIndex])
  1262. {
  1263. // Corrupt
  1264. IF_FAILEXIT(hr = _GetBlock(BLOCK_STRING, m_pHeader->rgfaFilter[iIndex], (LPVOID *)&pBlock));
  1265. // Duplicate
  1266. IF_NULLEXIT(*ppszFilter = DuplicateStringA(PSTRING(pBlock)));
  1267. }
  1268. // Done
  1269. goto exit;
  1270. }
  1271. }
  1272. // Failure
  1273. hr = E_FAIL;
  1274. exit:
  1275. // Lock
  1276. Unlock(&hLock);
  1277. // Done
  1278. return(hr);
  1279. }
  1280. //--------------------------------------------------------------------------
  1281. // CDatabase::ModifyIndex
  1282. //--------------------------------------------------------------------------
  1283. STDMETHODIMP CDatabase::ModifyIndex(INDEXORDINAL iIndex, LPCSTR pszFilter,
  1284. LPCTABLEINDEX pIndex)
  1285. {
  1286. // Locals
  1287. HRESULT hr=S_OK;
  1288. HLOCK hLock=NULL;
  1289. HQUERY hFilter=NULL;
  1290. FILEADDRESS faFilter=0;
  1291. LPBLOCKHEADER pFilter=NULL;
  1292. BOOL fFound=FALSE;
  1293. DWORD i;
  1294. DWORD cb;
  1295. BOOL fVersionChange=FALSE;
  1296. // Trace
  1297. TraceCall("CDatabase::ModifyIndex");
  1298. // Invalid Args
  1299. if (IINDEX_PRIMARY == iIndex || iIndex > CMAX_INDEXES || NULL == pIndex || pIndex->cKeys > CMAX_KEYS)
  1300. return TraceResult(E_INVALIDARG);
  1301. // Lock
  1302. IF_FAILEXIT(hr = Lock(&hLock));
  1303. // Filter
  1304. if (pszFilter)
  1305. {
  1306. // Parse the Query
  1307. IF_FAILEXIT(hr = ParseQuery(pszFilter, m_pSchema, &hFilter, this));
  1308. // Initialize the String Block
  1309. cb = lstrlen(pszFilter) + 1;
  1310. // Try to Store the Query String
  1311. IF_FAILEXIT(hr = _AllocateBlock(BLOCK_STRING, cb, (LPVOID *)&pFilter));
  1312. // Write the String
  1313. CopyMemory(PSTRING(pFilter), pszFilter, cb);
  1314. // Set faFilter
  1315. faFilter = pFilter->faBlock;
  1316. // Query Version Change
  1317. fVersionChange = TRUE;
  1318. }
  1319. // Free This Index
  1320. IF_FAILEXIT(hr = DeleteIndex(iIndex));
  1321. // Copy the Index Information
  1322. CopyMemory(&m_pHeader->rgIndexInfo[iIndex], pIndex, sizeof(TABLEINDEX));
  1323. // Filter
  1324. if (hFilter)
  1325. {
  1326. // Validate
  1327. Assert(NULL == m_rghFilter[iIndex] && 0 == m_pHeader->rgfaFilter[iIndex] && hFilter && faFilter);
  1328. // Store the hFilter
  1329. m_rghFilter[iIndex] = hFilter;
  1330. // Don't Free hFilter
  1331. hFilter = NULL;
  1332. // Store filter string address
  1333. m_pHeader->rgfaFilter[iIndex] = faFilter;
  1334. // Don't Free the Filter
  1335. faFilter = 0;
  1336. }
  1337. // Update Query Versions
  1338. if (fVersionChange)
  1339. {
  1340. // Update the Shared Query Version Count
  1341. m_pShare->dwQueryVersion++;
  1342. // I'm Up-to-date
  1343. m_dwQueryVersion = m_pShare->dwQueryVersion;
  1344. }
  1345. // Is iIndex already in rgiIndex ?
  1346. for (i=0; i<m_pHeader->cIndexes; i++)
  1347. {
  1348. // Is this it ?
  1349. if (iIndex == m_pHeader->rgiIndex[i])
  1350. {
  1351. // Its already in there
  1352. fFound = TRUE;
  1353. // Done
  1354. break;
  1355. }
  1356. }
  1357. // No Found
  1358. if (FALSE == fFound)
  1359. {
  1360. // Insert into Index Ordinal Array
  1361. m_pHeader->rgiIndex[m_pHeader->cIndexes] = iIndex;
  1362. // Increment Count
  1363. m_pHeader->cIndexes++;
  1364. }
  1365. // Rebuild the Index
  1366. IF_FAILEXIT(hr = _RebuildIndex(iIndex));
  1367. exit:
  1368. // Close Filters
  1369. CloseQuery(&hFilter, this);
  1370. // Free faFilter1
  1371. if (0 != faFilter)
  1372. {
  1373. // Free the block
  1374. SideAssert(SUCCEEDED(_FreeBlock(BLOCK_STRING, faFilter)));
  1375. }
  1376. // Lock
  1377. Unlock(&hLock);
  1378. // Done
  1379. return(hr);
  1380. }
  1381. //--------------------------------------------------------------------------
  1382. // CDatabase::DeleteIndex
  1383. //--------------------------------------------------------------------------
  1384. STDMETHODIMP CDatabase::DeleteIndex(INDEXORDINAL iIndex)
  1385. {
  1386. // Locals
  1387. HRESULT hr=S_OK;
  1388. DWORD i;
  1389. BOOL fFound=FALSE;
  1390. HLOCK hLock=NULL;
  1391. // Trace
  1392. TraceCall("CDatabase::DeleteIndex");
  1393. // Invalid Args
  1394. if (IINDEX_PRIMARY == iIndex || iIndex > CMAX_INDEXES)
  1395. return TraceResult(E_INVALIDARG);
  1396. // Lock
  1397. IF_FAILEXIT(hr = Lock(&hLock));
  1398. // Collapse the Ordinal Array
  1399. for (i = 0; i < m_pHeader->cIndexes; i++)
  1400. {
  1401. // Is this the Index to delete ?
  1402. if (m_pHeader->rgiIndex[i] == iIndex)
  1403. {
  1404. // Found
  1405. fFound = TRUE;
  1406. // Collapse the Array
  1407. MoveMemory(&m_pHeader->rgiIndex[i], &m_pHeader->rgiIndex[i + 1], sizeof(INDEXORDINAL) * (m_pHeader->cIndexes - (i + 1)));
  1408. // Decrement Index Count
  1409. m_pHeader->cIndexes--;
  1410. // Done
  1411. break;
  1412. }
  1413. }
  1414. // Not Found
  1415. if (FALSE == fFound)
  1416. {
  1417. // No Filter and no Exception
  1418. Assert(0 == m_pHeader->rgfaFilter[iIndex]);
  1419. // No Filter Handle
  1420. Assert(NULL == m_rghFilter[iIndex]);
  1421. // No Record
  1422. Assert(0 == m_pHeader->rgcRecords[iIndex]);
  1423. // Done
  1424. goto exit;
  1425. }
  1426. // If this Index is Currently In Use...
  1427. if (m_pHeader->rgfaIndex[iIndex])
  1428. {
  1429. // Free This Index
  1430. _FreeIndex(m_pHeader->rgfaIndex[iIndex]);
  1431. // Null It Out
  1432. m_pHeader->rgfaIndex[iIndex] = 0;
  1433. // No Record
  1434. m_pHeader->rgcRecords[iIndex] = 0;
  1435. }
  1436. // Delete Filter
  1437. if (m_pHeader->rgfaFilter[iIndex])
  1438. {
  1439. // Free the Block
  1440. IF_FAILEXIT(hr = _FreeBlock(BLOCK_STRING, m_pHeader->rgfaFilter[iIndex]));
  1441. // Close the filter handle
  1442. CloseQuery(&m_rghFilter[iIndex], this);
  1443. // Set to NULL
  1444. m_pHeader->rgfaFilter[iIndex] = 0;
  1445. // Update the Shared Query Version Count
  1446. m_pShare->dwQueryVersion++;
  1447. }
  1448. // I'm Up-to-date
  1449. m_dwQueryVersion = m_pShare->dwQueryVersion;
  1450. // Handle Should be closed
  1451. Assert(NULL == m_rghFilter[iIndex]);
  1452. // Send Notifications ?
  1453. if (m_pShare->rgcIndexNotify[iIndex] > 0)
  1454. {
  1455. // Build the Update Notification Package
  1456. _LogTransaction(TRANSACTION_INDEX_DELETED, iIndex, NULL, 0, 0);
  1457. }
  1458. exit:
  1459. // Lock
  1460. Unlock(&hLock);
  1461. // Done
  1462. return(hr);
  1463. }
  1464. //--------------------------------------------------------------------------
  1465. // CDatabase::GenerateId
  1466. //--------------------------------------------------------------------------
  1467. STDMETHODIMP CDatabase::GenerateId(LPDWORD pdwId)
  1468. {
  1469. // Locals
  1470. HRESULT hr=S_OK;
  1471. HLOCK hLock=NULL;
  1472. // Trace
  1473. TraceCall("CDatabase::GenerateId");
  1474. // Lock
  1475. IF_FAILEXIT(hr = Lock(&hLock));
  1476. // Loop Until I create a valid Id ?
  1477. while (1)
  1478. {
  1479. // Increment next id
  1480. m_pHeader->dwNextId++;
  1481. // Invalid Id ?
  1482. if (0 == m_pHeader->dwNextId)
  1483. continue;
  1484. // In Invalid Range
  1485. if (m_pHeader->dwNextId >= RESERVED_ID_MIN && m_pHeader->dwNextId <= RESERVED_ID_MAX)
  1486. continue;
  1487. // Its Good
  1488. break;
  1489. }
  1490. // Set pdwId
  1491. *pdwId = m_pHeader->dwNextId;
  1492. exit:
  1493. // Lock
  1494. Unlock(&hLock);
  1495. // Done
  1496. return(hr);
  1497. }
  1498. //--------------------------------------------------------------------------
  1499. // CDatabase::GetFile
  1500. //--------------------------------------------------------------------------
  1501. STDMETHODIMP CDatabase::GetFile(LPWSTR *ppszFile)
  1502. {
  1503. // Locals
  1504. HRESULT hr=S_OK;
  1505. HLOCK hLock=NULL;
  1506. // Trace
  1507. TraceCall("CDatabase::GetFile");
  1508. // Lock
  1509. IF_FAILEXIT(hr = Lock(&hLock));
  1510. // Dupp
  1511. IF_NULLEXIT(*ppszFile = DuplicateStringW(m_pShare->szFile));
  1512. exit:
  1513. // Unlock
  1514. Unlock(&hLock);
  1515. // Done
  1516. return(hr);
  1517. }
  1518. //--------------------------------------------------------------------------
  1519. // CDatabase::_DispatchNotification
  1520. //--------------------------------------------------------------------------
  1521. HRESULT CDatabase::_DispatchNotification(HTRANSACTION hTransaction)
  1522. {
  1523. // Locals
  1524. HRESULT hr=S_OK;
  1525. DWORD iClient;
  1526. LPCLIENTENTRY pClient;
  1527. DWORD iRecipient;
  1528. LPNOTIFYRECIPIENT pRecipient;
  1529. // Trace
  1530. TraceCall("CDatabase::_DispatchNotification");
  1531. // Walk through the List
  1532. for (iClient=0; iClient<m_pShare->cClients; iClient++)
  1533. {
  1534. // De-reference the client
  1535. pClient = &m_pShare->rgClient[iClient];
  1536. // Loop through cNotify
  1537. for (iRecipient=0; iRecipient<pClient->cRecipients; iRecipient++)
  1538. {
  1539. // De-Ref pEntry
  1540. pRecipient = &pClient->rgRecipient[iRecipient];
  1541. // If the Recipient isn't suspending...
  1542. if (FALSE == pRecipient->fSuspended)
  1543. {
  1544. // Should have a Thunking Window
  1545. Assert(pRecipient->hwndNotify && IsWindow(pRecipient->hwndNotify));
  1546. // Post the Notification
  1547. if (0 == PostMessage(pRecipient->hwndNotify, WM_ONTRANSACTION, (WPARAM)pRecipient->dwCookie, (LPARAM)hTransaction))
  1548. {
  1549. hr = TraceResult(E_FAIL);
  1550. goto exit;
  1551. }
  1552. }
  1553. }
  1554. }
  1555. exit:
  1556. // Done
  1557. return(hr);
  1558. }
  1559. //--------------------------------------------------------------------------
  1560. // CDatabase::DoInProcessInvoke
  1561. //--------------------------------------------------------------------------
  1562. HRESULT CDatabase::DoInProcessInvoke(INVOKETYPE tyInvoke)
  1563. {
  1564. // Locals
  1565. HRESULT hr=S_OK;
  1566. BOOL fNew;
  1567. // Trace
  1568. TraceCall("CDatabase::DoInProcessNotify");
  1569. // INVOKE_RELEASEMAP
  1570. if (INVOKE_RELEASEMAP == tyInvoke)
  1571. {
  1572. // Validate
  1573. _CloseFileViews(FALSE);
  1574. // Close the File
  1575. SafeCloseHandle(m_pStorage->hMap);
  1576. }
  1577. // INVOKE_CREATEMAP
  1578. else if (INVOKE_CREATEMAP == tyInvoke)
  1579. {
  1580. // Validation
  1581. Assert(NULL == m_pStorage->hMap);
  1582. // Get the file size
  1583. IF_FAILEXIT(hr = DBGetFileSize(m_pStorage->hFile, &m_pStorage->cbFile));
  1584. // Open the file mapping
  1585. IF_FAILEXIT(hr = DBOpenFileMapping(m_pStorage->hFile, m_pStorage->pszMap, m_pStorage->cbFile, &fNew, &m_pStorage->hMap, NULL));
  1586. // Initialize File Views
  1587. IF_FAILEXIT(hr = _InitializeFileViews());
  1588. }
  1589. // INVOKE_CLOSEFILE
  1590. else if (INVOKE_CLOSEFILE == tyInvoke)
  1591. {
  1592. // Validate
  1593. _CloseFileViews(TRUE);
  1594. // Close the File
  1595. if(m_pStorage->hFile /*&& m_fDirty*/)
  1596. {
  1597. FILETIME systime;
  1598. GetSystemTimeAsFileTime(&systime);
  1599. SetFileTime(m_pStorage->hFile, NULL, &systime, &systime);
  1600. }
  1601. SafeCloseHandle(m_pStorage->hMap);
  1602. // Close the file
  1603. SafeCloseHandle(m_pStorage->hFile);
  1604. }
  1605. // INVOKE_OPENFILE
  1606. else if (INVOKE_OPENFILE == tyInvoke || INVOKE_OPENMOVEDFILE == tyInvoke)
  1607. {
  1608. // Validation
  1609. Assert(NULL == m_pStorage->hFile && NULL == m_pStorage->hMap);
  1610. // Open Moved File ?
  1611. if (INVOKE_OPENMOVEDFILE == tyInvoke)
  1612. {
  1613. // _HandleOpenMovedFile
  1614. IF_FAILEXIT(hr = _HandleOpenMovedFile());
  1615. }
  1616. // Open the File
  1617. IF_FAILEXIT(hr = DBOpenFile(m_pShare->szFile, FALSE, m_fExclusive, &fNew, &m_pStorage->hFile));
  1618. // Better not be new
  1619. Assert(FALSE == fNew);
  1620. // Get the file size
  1621. IF_FAILEXIT(hr = DBGetFileSize(m_pStorage->hFile, &m_pStorage->cbFile));
  1622. // Open the file mapping
  1623. IF_FAILEXIT(hr = DBOpenFileMapping(m_pStorage->hFile, m_pStorage->pszMap, m_pStorage->cbFile, &fNew, &m_pStorage->hMap, NULL));
  1624. // Initialize File Views
  1625. IF_FAILEXIT(hr = _InitializeFileViews());
  1626. }
  1627. // Uhoh
  1628. else
  1629. AssertSz(FALSE, "Invalid invoke type passed into CDatabase::DoInProcessInvoke");
  1630. exit:
  1631. // Done
  1632. return(hr);
  1633. }
  1634. //--------------------------------------------------------------------------
  1635. // CDatabase::_HandleOpenMovedFile
  1636. //--------------------------------------------------------------------------
  1637. HRESULT CDatabase::_HandleOpenMovedFile(void)
  1638. {
  1639. // Locals
  1640. HRESULT hr=S_OK;
  1641. LPWSTR pszMutex=NULL;
  1642. LPWSTR pszShare=NULL;
  1643. BOOL fNewShare;
  1644. WCHAR szFile[CCHMAX_DB_FILEPATH];
  1645. // Trace
  1646. TraceCall("CDatabase::_HandleOpenMovedFile");
  1647. // Save New File Path
  1648. StrCpyNW(szFile, m_pShare->szFile, ARRAYSIZE(szFile));
  1649. // Free pszMap
  1650. SafeMemFree(m_pStorage->pszMap);
  1651. // Create the Mutex Object
  1652. IF_FAILEXIT(hr = CreateSystemHandleName(szFile, L"_DirectDBFileMap", &m_pStorage->pszMap));
  1653. // Create the Mutex Object
  1654. IF_FAILEXIT(hr = CreateSystemHandleName(szFile, L"_DirectDBMutex", &pszMutex));
  1655. // Close the current mutex
  1656. SafeCloseHandle(m_hMutex);
  1657. // Create the Mutex
  1658. IF_NULLEXIT(m_hMutex = CreateMutexWrapW(NULL, FALSE, pszMutex));
  1659. // If not in move file
  1660. if (FALSE == m_fInMoveFile)
  1661. {
  1662. // Create the Mutex Object
  1663. IF_FAILEXIT(hr = CreateSystemHandleName(szFile, L"_DirectDBShare", &pszShare));
  1664. // Unmap the view of the memory mapped file
  1665. SafeUnmapViewOfFile(m_pShare);
  1666. // Unmap the view of the memory mapped file
  1667. SafeCloseHandle(m_pStorage->hShare);
  1668. // Open the file mapping
  1669. IF_FAILEXIT(hr = DBOpenFileMapping(INVALID_HANDLE_VALUE, pszShare, sizeof(SHAREDDATABASE), &fNewShare, &m_pStorage->hShare, (LPVOID *)&m_pShare));
  1670. // Better not be new
  1671. Assert(!fNewShare);
  1672. }
  1673. else
  1674. Assert(StrCmpW(szFile, m_pShare->szFile) == 0);
  1675. exit:
  1676. // Cleanup
  1677. SafeMemFree(pszMutex);
  1678. SafeMemFree(pszShare);
  1679. // Done
  1680. return(hr);
  1681. }
  1682. //--------------------------------------------------------------------------
  1683. // CDatabase::_DispatchInvoke
  1684. //--------------------------------------------------------------------------
  1685. HRESULT CDatabase::_DispatchInvoke(INVOKETYPE tyInvoke)
  1686. {
  1687. // Locals
  1688. HRESULT hr=S_OK;
  1689. DWORD iClient=0;
  1690. LPCLIENTENTRY pClient;
  1691. DWORD_PTR dwResult;
  1692. DWORD dwThreadId=GetCurrentThreadId();
  1693. INVOKEPACKAGE Package;
  1694. COPYDATASTRUCT CopyData;
  1695. // Trace
  1696. TraceCall("CDatabase::_DispatchInvoke");
  1697. // Set Invoke Type
  1698. Package.tyInvoke = tyInvoke;
  1699. // Walk through the List
  1700. while (iClient < m_pShare->cClients)
  1701. {
  1702. // Readability
  1703. pClient = &m_pShare->rgClient[iClient++];
  1704. // Better have one
  1705. Package.pDB = pClient->pDB;
  1706. // Is this entry in my process ?
  1707. if (m_dwProcessId == pClient->dwProcessId)
  1708. {
  1709. // Do In Process Notification
  1710. CDatabase *pDB = (CDatabase *)pClient->pDB;
  1711. // Do It
  1712. IF_FAILEXIT(hr = pDB->DoInProcessInvoke(tyInvoke));
  1713. }
  1714. // Otherwise, just process the package
  1715. else
  1716. {
  1717. // If the listener is good
  1718. if (pClient->hwndListen && IsWindow(pClient->hwndListen))
  1719. {
  1720. // Initialize copy data struct
  1721. CopyData.dwData = 0;
  1722. // Store the Size of the Package
  1723. CopyData.cbData = sizeof(INVOKEPACKAGE);
  1724. // Store the Package
  1725. CopyData.lpData = &Package;
  1726. // Send It
  1727. if (0 == SendMessageTimeout(pClient->hwndListen, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&CopyData, SMTO_ABORTIFHUNG, 5000, &dwResult))
  1728. {
  1729. // Remove this client from the list
  1730. SideAssert(SUCCEEDED(_RemoveClientFromArray(pClient->dwProcessId, pClient->pDB)));
  1731. // Decrement iClient
  1732. iClient--;
  1733. }
  1734. }
  1735. // Remove this client
  1736. else
  1737. {
  1738. // Remove this client from the list
  1739. SideAssert(SUCCEEDED(_RemoveClientFromArray(pClient->dwProcessId, pClient->pDB)));
  1740. // Decrement iClient
  1741. iClient--;
  1742. }
  1743. }
  1744. }
  1745. exit:
  1746. // Done
  1747. return(hr);
  1748. }
  1749. //--------------------------------------------------------------------------
  1750. // CDatabase::_RemoveClientFromArray
  1751. //--------------------------------------------------------------------------
  1752. HRESULT CDatabase::_RemoveClientFromArray(DWORD dwProcessId,
  1753. DWORD_PTR dwDB)
  1754. {
  1755. // Locals
  1756. HRESULT hr=S_OK;
  1757. DWORD iRecipient;
  1758. LPNOTIFYRECIPIENT pRecipient;
  1759. DWORD cClients=0;
  1760. DWORD iClient;
  1761. LPCLIENTENTRY pClient;
  1762. // Trace
  1763. TraceCall("CDatabase::_RemoveClientFromArray");
  1764. // Initialize i
  1765. iClient = 0;
  1766. // Find this Client
  1767. IF_FAILEXIT(hr = _FindClient(dwProcessId, dwDB, &iClient, &pClient));
  1768. // Release Registered Notification Objects
  1769. for (iRecipient=0; iRecipient<pClient->cRecipients; iRecipient++)
  1770. {
  1771. // Readability
  1772. pRecipient = &pClient->rgRecipient[iRecipient];
  1773. // Same Process ?
  1774. if (dwProcessId == m_dwProcessId)
  1775. {
  1776. // Remove and messages from the notificaton queue
  1777. _CloseNotificationWindow(pRecipient);
  1778. // Release ?
  1779. if (TRUE == pRecipient->fRelease)
  1780. {
  1781. // Cast pNotify
  1782. IDatabaseNotify *pNotify = (IDatabaseNotify *)pRecipient->pNotify;
  1783. // Cast to pRecipient
  1784. pNotify->Release();
  1785. }
  1786. }
  1787. // If Not Suspended
  1788. if (FALSE == pRecipient->fSuspended)
  1789. {
  1790. // _AdjustNotifyCounts
  1791. _AdjustNotifyCounts(pRecipient, -1);
  1792. }
  1793. }
  1794. // Remove MySelf
  1795. MoveMemory(&m_pShare->rgClient[iClient], &m_pShare->rgClient[iClient + 1], sizeof(CLIENTENTRY) * (m_pShare->cClients - (iClient + 1)));
  1796. // Decrement Client Count
  1797. m_pShare->cClients--;
  1798. exit:
  1799. // Done
  1800. return(hr);
  1801. }
  1802. //--------------------------------------------------------------------------
  1803. // CDatabase::_CloseNotificationWindow
  1804. //--------------------------------------------------------------------------
  1805. HRESULT CDatabase::_CloseNotificationWindow(LPNOTIFYRECIPIENT pRecipient)
  1806. {
  1807. // Trace
  1808. TraceCall("CDatabase::_CloseNotificationWindow");
  1809. // Kill the Window
  1810. DestroyWindow(pRecipient->hwndNotify);
  1811. // Null it Count
  1812. pRecipient->hwndNotify = NULL;
  1813. // Done
  1814. return(S_OK);
  1815. }
  1816. //--------------------------------------------------------------------------
  1817. // CDatabase::_FindClient
  1818. //--------------------------------------------------------------------------
  1819. HRESULT CDatabase::_FindClient(DWORD dwProcessId, DWORD_PTR dwDB,
  1820. LPDWORD piClient, LPCLIENTENTRY *ppClient)
  1821. {
  1822. // Locals
  1823. HRESULT hr=S_OK;
  1824. LPCLIENTENTRY pClient;
  1825. DWORD iClient;
  1826. // Trace
  1827. TraceCall("CDatabase::_FindThisClient");
  1828. // Find myself in the client list
  1829. for (iClient=0; iClient<m_pShare->cClients; iClient++)
  1830. {
  1831. // Readability
  1832. pClient = &m_pShare->rgClient[iClient];
  1833. // Is this me ?
  1834. if (dwProcessId == pClient->dwProcessId && dwDB == pClient->pDB)
  1835. {
  1836. *piClient = iClient;
  1837. *ppClient = pClient;
  1838. goto exit;
  1839. }
  1840. }
  1841. // Not Found
  1842. hr = TraceResult(DB_E_NOTFOUND);
  1843. exit:
  1844. // Done
  1845. return(hr);
  1846. }
  1847. //--------------------------------------------------------------------------
  1848. // CDatabase::_FindNotifyRecipient
  1849. //--------------------------------------------------------------------------
  1850. HRESULT CDatabase::_FindNotifyRecipient(DWORD iClient, IDatabaseNotify *pNotify,
  1851. LPDWORD piRecipient, LPNOTIFYRECIPIENT *ppRecipient)
  1852. {
  1853. // Locals
  1854. HRESULT hr=S_OK;
  1855. LPCLIENTENTRY pClient;
  1856. DWORD iRecipient;
  1857. LPNOTIFYRECIPIENT pRecipient;
  1858. // Trace
  1859. TraceCall("CDatabase::_FindNotifyRecipient");
  1860. // Readability
  1861. pClient = &m_pShare->rgClient[iClient];
  1862. // Walk through client's registered notification entries
  1863. for (iRecipient = 0; iRecipient < m_pShare->rgClient[iClient].cRecipients; iRecipient++)
  1864. {
  1865. // Readability
  1866. pRecipient = &pClient->rgRecipient[iRecipient];
  1867. // Is this me ?
  1868. if ((DWORD_PTR)pNotify == pRecipient->pNotify)
  1869. {
  1870. // This is It
  1871. *piRecipient = iRecipient;
  1872. *ppRecipient = pRecipient;
  1873. goto exit;
  1874. }
  1875. }
  1876. // Not Found
  1877. hr = DB_E_NOTFOUND;
  1878. exit:
  1879. // Done
  1880. return(hr);
  1881. }
  1882. //--------------------------------------------------------------------------
  1883. // CDatabase::_DispatchPendingNotifications
  1884. //--------------------------------------------------------------------------
  1885. HRESULT CDatabase::_DispatchPendingNotifications(void)
  1886. {
  1887. // Are there pending notifications
  1888. if (m_pShare->faTransactLockHead)
  1889. {
  1890. // Dispatch Invoke
  1891. _DispatchNotification((HTRANSACTION)IntToPtr(m_pShare->faTransactLockHead));
  1892. // Null It Out
  1893. m_pShare->faTransactLockTail = m_pShare->faTransactLockHead = 0;
  1894. }
  1895. // Otherwise, validate
  1896. else
  1897. {
  1898. // Tail must be Null
  1899. Assert(0 == m_pShare->faTransactLockTail);
  1900. }
  1901. // Done
  1902. return(S_OK);
  1903. }
  1904. //--------------------------------------------------------------------------
  1905. // CDatabase::DispatchNotify
  1906. //--------------------------------------------------------------------------
  1907. STDMETHODIMP CDatabase::DispatchNotify(IDatabaseNotify *pNotify)
  1908. {
  1909. // Locals
  1910. HRESULT hr=S_OK;
  1911. LPCLIENTENTRY pClient;
  1912. DWORD iClient;
  1913. DWORD iRecipient;
  1914. LPNOTIFYRECIPIENT pRecipient;
  1915. HLOCK hLock=NULL;
  1916. MSG msg;
  1917. // Trace
  1918. TraceCall("CDatabase::DispatchNotify");
  1919. // Lock
  1920. IF_FAILEXIT(hr = Lock(&hLock));
  1921. // Find this Client
  1922. IF_FAILEXIT(hr = _FindClient(m_dwProcessId, (DWORD_PTR)this, &iClient, &pClient));
  1923. // Find this recipient
  1924. IF_FAILEXIT(hr = _FindNotifyRecipient(iClient, pNotify, &iRecipient, &pRecipient));
  1925. // Need to dish out pending notifications....
  1926. _DispatchPendingNotifications();
  1927. // Processing the Pending Notifications for this recipient...
  1928. if (pRecipient->dwThreadId != GetCurrentThreadId())
  1929. {
  1930. Assert(FALSE);
  1931. hr = TraceResult(DB_E_WRONGTHREAD);
  1932. goto exit;
  1933. }
  1934. // Pump Messages
  1935. while (PeekMessage(&msg, pRecipient->hwndNotify, WM_ONTRANSACTION, WM_ONTRANSACTION, PM_REMOVE))
  1936. {
  1937. // Translate the Message
  1938. TranslateMessage(&msg);
  1939. // Dispatch the Message
  1940. DispatchMessage(&msg);
  1941. }
  1942. exit:
  1943. // Lock
  1944. Unlock(&hLock);
  1945. // Done
  1946. return(hr);
  1947. }
  1948. //--------------------------------------------------------------------------
  1949. // CDatabase::SuspendNotify
  1950. //--------------------------------------------------------------------------
  1951. STDMETHODIMP CDatabase::SuspendNotify(IDatabaseNotify *pNotify)
  1952. {
  1953. // Locals
  1954. HRESULT hr=S_OK;
  1955. LPCLIENTENTRY pClient;
  1956. DWORD iClient;
  1957. DWORD iRecipient;
  1958. LPNOTIFYRECIPIENT pRecipient;
  1959. HLOCK hLock=NULL;
  1960. MSG msg;
  1961. // Trace
  1962. TraceCall("CDatabase::SuspendNotify");
  1963. // Lock
  1964. IF_FAILEXIT(hr = Lock(&hLock));
  1965. // Find this Client
  1966. IF_FAILEXIT(hr = _FindClient(m_dwProcessId, (DWORD_PTR)this, &iClient, &pClient));
  1967. // Find this recipient
  1968. IF_FAILEXIT(hr = _FindNotifyRecipient(iClient, pNotify, &iRecipient, &pRecipient));
  1969. // If Not Suspended yet
  1970. if (pRecipient->fSuspended)
  1971. goto exit;
  1972. // Need to dish out pending notifications....
  1973. _DispatchPendingNotifications();
  1974. // Processing the Pending Notifications for this recipient...
  1975. if (pRecipient->dwThreadId == GetCurrentThreadId())
  1976. {
  1977. // Pump Messages
  1978. while (PeekMessage(&msg, pRecipient->hwndNotify, WM_ONTRANSACTION, WM_ONTRANSACTION, PM_REMOVE))
  1979. {
  1980. // Translate the Message
  1981. TranslateMessage(&msg);
  1982. // Dispatch the Message
  1983. DispatchMessage(&msg);
  1984. }
  1985. }
  1986. // Otherwise, can't pump out pending notifications...
  1987. else
  1988. Assert(FALSE);
  1989. // Set Suspended
  1990. pRecipient->fSuspended = TRUE;
  1991. // Adjust Notify Counts
  1992. _AdjustNotifyCounts(pRecipient, -1);
  1993. exit:
  1994. // Lock
  1995. Unlock(&hLock);
  1996. // Done
  1997. return(hr);
  1998. }
  1999. //--------------------------------------------------------------------------
  2000. // CDatabase::ResumeNotify
  2001. //--------------------------------------------------------------------------
  2002. STDMETHODIMP CDatabase::ResumeNotify(IDatabaseNotify *pNotify)
  2003. {
  2004. // Locals
  2005. HRESULT hr=S_OK;
  2006. LPCLIENTENTRY pClient;
  2007. DWORD iClient;
  2008. DWORD iRecipient;
  2009. LPNOTIFYRECIPIENT pRecipient;
  2010. HLOCK hLock=NULL;
  2011. // Trace
  2012. TraceCall("CDatabase::ResumeNotify");
  2013. // Lock
  2014. IF_FAILEXIT(hr = Lock(&hLock));
  2015. // Find this Client
  2016. IF_FAILEXIT(hr = _FindClient(m_dwProcessId, (DWORD_PTR)this, &iClient, &pClient));
  2017. // Find this recipient
  2018. IF_FAILEXIT(hr = _FindNotifyRecipient(iClient, pNotify, &iRecipient, &pRecipient));
  2019. // If Not Suspended yet
  2020. if (FALSE == pRecipient->fSuspended)
  2021. goto exit;
  2022. // Need to dish out pending notifications....
  2023. _DispatchPendingNotifications();
  2024. // Remove fSuspended
  2025. pRecipient->fSuspended = FALSE;
  2026. // Adjust Notify Counts
  2027. _AdjustNotifyCounts(pRecipient, 1);
  2028. exit:
  2029. // Lock
  2030. Unlock(&hLock);
  2031. // Done
  2032. return(hr);
  2033. }
  2034. //--------------------------------------------------------------------------
  2035. // CDatabase::_AdjustNotifyCounts
  2036. //--------------------------------------------------------------------------
  2037. HRESULT CDatabase::_AdjustNotifyCounts(LPNOTIFYRECIPIENT pRecipient,
  2038. LONG lChange)
  2039. {
  2040. // Trace
  2041. TraceCall("CDatabase::_AdjustNotifyCounts");
  2042. // Ordinals Only
  2043. if (pRecipient->fOrdinalsOnly)
  2044. {
  2045. // Validate the Count
  2046. Assert((LONG)(m_pShare->cNotifyOrdinalsOnly + lChange) >= 0);
  2047. // Update Ordinals Only Count
  2048. m_pShare->cNotifyOrdinalsOnly += lChange;
  2049. }
  2050. // Otherwise, update notify with data count
  2051. else
  2052. {
  2053. // Validate the Count
  2054. Assert((LONG)(m_pShare->cNotifyWithData + lChange) >= 0);
  2055. // Update
  2056. m_pShare->cNotifyWithData += lChange;
  2057. }
  2058. // Validate the Count
  2059. Assert((LONG)(m_pShare->cNotify + lChange) >= 0);
  2060. // Update Total cNotify
  2061. m_pShare->cNotify += lChange;
  2062. // Validate the Count
  2063. Assert((LONG)(m_pShare->rgcIndexNotify[pRecipient->iIndex] + lChange) >= 0);
  2064. // Decrement Number of Recipients for Index
  2065. m_pShare->rgcIndexNotify[pRecipient->iIndex] += lChange;
  2066. // Done
  2067. return(S_OK);
  2068. }
  2069. //--------------------------------------------------------------------------
  2070. // CDatabase::UnregisterNotify
  2071. //--------------------------------------------------------------------------
  2072. STDMETHODIMP CDatabase::UnregisterNotify(IDatabaseNotify *pNotify)
  2073. {
  2074. // Locals
  2075. HRESULT hr=S_OK;
  2076. HLOCK hLock=NULL;
  2077. LPCLIENTENTRY pClient;
  2078. DWORD iClient;
  2079. DWORD iRecipient;
  2080. LPNOTIFYRECIPIENT pRecipient;
  2081. // Trace
  2082. TraceCall("CDatabase::UnregisterNotify");
  2083. // Lock
  2084. IF_FAILEXIT(hr = Lock(&hLock));
  2085. // Find this Client
  2086. IF_FAILEXIT(hr = _FindClient(m_dwProcessId, (DWORD_PTR)this, &iClient, &pClient));
  2087. // Find this recipient
  2088. hr = _FindNotifyRecipient(iClient, pNotify, &iRecipient, &pRecipient);
  2089. if (FAILED(hr))
  2090. {
  2091. hr = S_OK;
  2092. goto exit;
  2093. }
  2094. // Remove and messages from the notificaton queue
  2095. _CloseNotificationWindow(pRecipient);
  2096. // Release ?
  2097. if (TRUE == pRecipient->fRelease)
  2098. {
  2099. // Cast to pRecipient
  2100. pNotify->Release();
  2101. }
  2102. // If Not Suspended
  2103. if (FALSE == pRecipient->fSuspended)
  2104. {
  2105. // _AdjustNotifyCounts
  2106. _AdjustNotifyCounts(pRecipient, -1);
  2107. }
  2108. // Remove MySelf
  2109. MoveMemory(&pClient->rgRecipient[iRecipient], &pClient->rgRecipient[iRecipient + 1], sizeof(NOTIFYRECIPIENT) * (pClient->cRecipients - (iRecipient + 1)));
  2110. // Decrement Client Count
  2111. pClient->cRecipients--;
  2112. exit:
  2113. // Lock
  2114. Unlock(&hLock);
  2115. // Done
  2116. return(hr);
  2117. }
  2118. //--------------------------------------------------------------------------
  2119. // CDatabase::RegisterNotify
  2120. //--------------------------------------------------------------------------
  2121. STDMETHODIMP CDatabase::RegisterNotify(INDEXORDINAL iIndex,
  2122. REGISTERNOTIFYFLAGS dwFlags, DWORD_PTR dwCookie,
  2123. IDatabaseNotify *pNotify)
  2124. {
  2125. // Locals
  2126. HRESULT hr=S_OK;
  2127. LPCLIENTENTRY pClient;
  2128. DWORD iClient;
  2129. DWORD iRecipient;
  2130. LPNOTIFYRECIPIENT pRecipient;
  2131. HLOCK hLock=NULL;
  2132. // Trace
  2133. TraceCall("CDatabase::RegisterNotify");
  2134. // Invalid Args
  2135. if (NULL == pNotify || iIndex > CMAX_INDEXES)
  2136. return TraceResult(E_INVALIDARG);
  2137. // If Deconstructing, just return
  2138. if (m_fDeconstruct)
  2139. return(S_OK);
  2140. // Thread Safety
  2141. IF_FAILEXIT(hr = Lock(&hLock));
  2142. // Find this Client
  2143. IF_FAILEXIT(hr = _FindClient(m_dwProcessId, (DWORD_PTR)this, &iClient, &pClient));
  2144. // See if this client is already registered...
  2145. if (SUCCEEDED(_FindNotifyRecipient(iClient, pNotify, &iRecipient, &pRecipient)))
  2146. {
  2147. hr = TraceResult(DB_E_ALREADYREGISTERED);
  2148. goto exit;
  2149. }
  2150. // Need to dish out pending notifications....
  2151. _DispatchPendingNotifications();
  2152. // Room for one more
  2153. if (pClient->cRecipients + 1 >= CMAX_RECIPIENTS)
  2154. {
  2155. hr = TraceResult(E_FAIL);
  2156. goto exit;
  2157. }
  2158. // Readability
  2159. pRecipient = &pClient->rgRecipient[pClient->cRecipients];
  2160. // Store the ThreadId
  2161. pRecipient->dwThreadId = GetCurrentThreadId();
  2162. // Save the Cookie
  2163. pRecipient->dwCookie = dwCookie;
  2164. // Get a Thunking Window for that thread
  2165. IF_FAILEXIT(hr = CreateNotifyWindow(this, pNotify, &pRecipient->hwndNotify));
  2166. // Only addref if the client wants me to
  2167. if (!ISFLAGSET(dwFlags, REGISTER_NOTIFY_NOADDREF))
  2168. {
  2169. // AddRef the notification object
  2170. pNotify->AddRef();
  2171. // Release it
  2172. pRecipient->fRelease = TRUE;
  2173. }
  2174. // Register It
  2175. pRecipient->pNotify = (DWORD_PTR)pNotify;
  2176. // Save the Index that they are intereseted in
  2177. pRecipient->iIndex = iIndex;
  2178. // Increment Notify Count
  2179. pClient->cRecipients++;
  2180. // Ordinals Only
  2181. pRecipient->fOrdinalsOnly = (ISFLAGSET(dwFlags, REGISTER_NOTIFY_ORDINALSONLY) ? TRUE : FALSE);
  2182. // _AdjustNotifyCounts
  2183. _AdjustNotifyCounts(pRecipient, 1);
  2184. exit:
  2185. // Thread Safety
  2186. Unlock(&hLock);
  2187. // Done
  2188. return(hr);
  2189. }
  2190. //--------------------------------------------------------------------------
  2191. // CDatabase::_SetStorageSize
  2192. //--------------------------------------------------------------------------
  2193. HRESULT CDatabase::_SetStorageSize(DWORD cbSize)
  2194. {
  2195. // Locals
  2196. HRESULT hr=S_OK;
  2197. HRESULT hrCreate;
  2198. // Trace
  2199. TraceCall("CDatabase::_SetStorageSize");
  2200. // Only if sizes are different
  2201. if (cbSize == m_pStorage->cbFile)
  2202. return(S_OK);
  2203. // Do It
  2204. _DispatchInvoke(INVOKE_RELEASEMAP);
  2205. // Set the File Pointer
  2206. if (0xFFFFFFFF == SetFilePointer(m_pStorage->hFile, cbSize, NULL, FILE_BEGIN))
  2207. {
  2208. hr = TraceResult(DB_E_SETFILEPOINTER);
  2209. goto exit;
  2210. }
  2211. // Set End of file
  2212. if (0 == SetEndOfFile(m_pStorage->hFile))
  2213. {
  2214. // Get LastError
  2215. DWORD dwLastError = GetLastError();
  2216. // Access Denied ?
  2217. if (ERROR_ACCESS_DENIED == dwLastError)
  2218. {
  2219. hr = TraceResult(DB_E_ACCESSDENIED);
  2220. goto exit;
  2221. }
  2222. // Otherwise, assume disk is full
  2223. else
  2224. {
  2225. hr = TraceResult(DB_E_DISKFULL);
  2226. goto exit;
  2227. }
  2228. }
  2229. exit:
  2230. // Do It
  2231. hrCreate = _DispatchInvoke(INVOKE_CREATEMAP);
  2232. // Done
  2233. return(SUCCEEDED(hr) ? hrCreate : hr);
  2234. }
  2235. //--------------------------------------------------------------------------
  2236. // CDatabase::SetSize
  2237. //--------------------------------------------------------------------------
  2238. STDMETHODIMP CDatabase::SetSize(DWORD cbSize)
  2239. {
  2240. // Locals
  2241. HRESULT hr=S_OK;
  2242. HLOCK hLock=NULL;
  2243. // Trace
  2244. TraceCall("CDatabase::SetSize");
  2245. // Lock
  2246. IF_FAILEXIT(hr = Lock(&hLock));
  2247. // Size can only be larger than my current size
  2248. if (cbSize < m_pStorage->cbFile)
  2249. goto exit;
  2250. // If the size of the file is currently zero...
  2251. IF_FAILEXIT(hr = _SetStorageSize(cbSize));
  2252. exit:
  2253. // Invalid Args
  2254. Unlock(&hLock);
  2255. // Done
  2256. return(hr);
  2257. }
  2258. //--------------------------------------------------------------------------
  2259. // CDatabase::_AllocatePage
  2260. //--------------------------------------------------------------------------
  2261. HRESULT CDatabase::_AllocatePage(DWORD cbPage, LPFILEADDRESS pfaAddress)
  2262. {
  2263. // Locals
  2264. HRESULT hr=S_OK;
  2265. // Trace
  2266. TraceCall("CDatabase::_AllocatePage");
  2267. // Quick Validation
  2268. Assert(m_pHeader->faNextAllocate && m_pHeader->faNextAllocate <= m_pStorage->cbFile);
  2269. // Need to grow the file ?
  2270. if (m_pStorage->cbFile - m_pHeader->faNextAllocate < cbPage)
  2271. {
  2272. // Compute cbNeeded
  2273. DWORD cbNeeded = cbPage - (m_pStorage->cbFile - m_pHeader->faNextAllocate);
  2274. // Grow in at least 64k chunks.
  2275. cbNeeded = max(cbNeeded, 65536);
  2276. // If the size of the file is currently zero...
  2277. IF_FAILEXIT(hr = _SetStorageSize(m_pStorage->cbFile + cbNeeded));
  2278. }
  2279. // Return this address
  2280. *pfaAddress = m_pHeader->faNextAllocate;
  2281. // Adjust faNextAllocate
  2282. m_pHeader->faNextAllocate += cbPage;
  2283. exit:
  2284. // Done
  2285. return(hr);
  2286. }
  2287. //--------------------------------------------------------------------------
  2288. // CDatabase::_MarkBlock
  2289. //--------------------------------------------------------------------------
  2290. HRESULT CDatabase::_MarkBlock(BLOCKTYPE tyBlock, FILEADDRESS faBlock,
  2291. DWORD cbBlock, LPVOID *ppvBlock)
  2292. {
  2293. // Locals
  2294. HRESULT hr=S_OK;
  2295. MARKBLOCK Mark;
  2296. LPBLOCKHEADER pBlock;
  2297. // Trace
  2298. TraceCall("CDatabase::_MarkBlock");
  2299. // Validate
  2300. Assert(cbBlock >= g_rgcbBlockSize[tyBlock]);
  2301. // Set Mark
  2302. Mark.cbBlock = cbBlock;
  2303. // De-Ref the Header
  2304. IF_FAILEXIT(hr = _GetBlock(tyBlock, faBlock, (LPVOID *)&pBlock, &Mark));
  2305. // Zero the Header
  2306. ZeroBlock(pBlock, g_rgcbBlockSize[tyBlock]);
  2307. // Return ppvBlock
  2308. if (ppvBlock)
  2309. *ppvBlock = (LPVOID)pBlock;
  2310. exit:
  2311. // Done
  2312. return(hr);
  2313. }
  2314. //--------------------------------------------------------------------------
  2315. // CDatabase::_AllocateFromPage
  2316. //--------------------------------------------------------------------------
  2317. HRESULT CDatabase::_AllocateFromPage(BLOCKTYPE tyBlock, LPALLOCATEPAGE pPage,
  2318. DWORD cbPage, DWORD cbBlock, LPVOID *ppvBlock)
  2319. {
  2320. // Locals
  2321. HRESULT hr=S_OK;
  2322. DWORD cbLeft;
  2323. FILEADDRESS faBlock;
  2324. DWORD iBucket;
  2325. // Trace
  2326. TraceCall("CDatabase::_AllocateFromPage");
  2327. // Is Page Valid ?
  2328. if (pPage->faPage + pPage->cbPage > m_pStorage->cbFile)
  2329. {
  2330. // Kill the page
  2331. ZeroMemory(pPage, sizeof(ALLOCATEPAGE));
  2332. }
  2333. // Otherwise
  2334. else
  2335. {
  2336. // Compute cbLeft
  2337. cbLeft = pPage->cbPage - pPage->cbUsed;
  2338. }
  2339. // Requesting a large block
  2340. if (cbBlock > cbPage || (cbLeft > 0 && cbLeft < cbBlock && cbLeft >= CB_MAX_FREE_BUCKET))
  2341. {
  2342. // Allocate space in the file
  2343. IF_FAILEXIT(hr = _AllocatePage(cbBlock, &faBlock));
  2344. // Mark the block
  2345. IF_FAILEXIT(hr = _MarkBlock(tyBlock, faBlock, cbBlock, ppvBlock));
  2346. }
  2347. // Invalid Page ?
  2348. else
  2349. {
  2350. // Block is too small...
  2351. if (cbLeft > 0 && cbLeft < cbBlock)
  2352. {
  2353. // Must be a BLOCK_RECORD
  2354. Assert(BLOCK_STREAM != tyBlock && BLOCK_CHAIN != tyBlock);
  2355. // Better fit into block
  2356. Assert(cbLeft <= CB_MAX_FREE_BUCKET && cbLeft >= CB_MIN_FREE_BUCKET && (cbLeft % 4) == 0);
  2357. // Mark the block
  2358. IF_FAILEXIT(hr = _MarkBlock(BLOCK_ENDOFPAGE, (pPage->faPage + pPage->cbUsed), cbLeft, NULL));
  2359. // Increment cbAllocated
  2360. m_pHeader->cbAllocated += cbLeft;
  2361. // Increment
  2362. m_pHeader->rgcbAllocated[BLOCK_ENDOFPAGE] += cbLeft;
  2363. // Lets Free This block
  2364. IF_FAILEXIT(hr = _FreeBlock(BLOCK_ENDOFPAGE, (pPage->faPage + pPage->cbUsed)));
  2365. // Nothgin Left
  2366. cbLeft = 0;
  2367. }
  2368. // Use the entire page
  2369. else if (cbLeft != cbBlock && cbLeft - cbBlock < CB_MIN_FREE_BUCKET)
  2370. {
  2371. // Must be a BLOCK_RECORD
  2372. Assert(BLOCK_STREAM != tyBlock && BLOCK_CHAIN != tyBlock);
  2373. // Adjust cbBlock
  2374. cbBlock += (cbLeft - cbBlock);
  2375. }
  2376. // Need to allocate a page
  2377. if (0 == pPage->faPage || 0 == cbLeft)
  2378. {
  2379. // Kill the page
  2380. ZeroMemory(pPage, sizeof(ALLOCATEPAGE));
  2381. // Allocate space in the file
  2382. IF_FAILEXIT(hr = _AllocatePage(cbPage, &pPage->faPage));
  2383. // Set cbChainPageLeft
  2384. pPage->cbPage = cbPage;
  2385. }
  2386. // Mark the block
  2387. IF_FAILEXIT(hr = _MarkBlock(tyBlock, (pPage->faPage + pPage->cbUsed), cbBlock, ppvBlock));
  2388. // Set Next Allocation
  2389. pPage->cbUsed += cbBlock;
  2390. // Validate
  2391. Assert(pPage->cbUsed <= pPage->cbPage);
  2392. }
  2393. exit:
  2394. // Done
  2395. return(hr);
  2396. }
  2397. //--------------------------------------------------------------------------
  2398. // CDatabase::_SetCorrupt
  2399. //--------------------------------------------------------------------------
  2400. HRESULT CDatabase::_SetCorrupt(BOOL fGoCorrupt, INT nLine,
  2401. CORRUPTREASON tyReason, BLOCKTYPE tyBlock, FILEADDRESS faExpected,
  2402. FILEADDRESS faActual, DWORD cbBlock)
  2403. {
  2404. // Trace
  2405. TraceCall("CDatabase::_SetCorrupt");
  2406. // Go Corrupt
  2407. if (fGoCorrupt)
  2408. {
  2409. // Store it in the header
  2410. m_pHeader->fCorrupt = TRUE;
  2411. }
  2412. // Done - This is always return to get the calling operation to abort
  2413. return(DB_E_CORRUPT);
  2414. }
  2415. //--------------------------------------------------------------------------
  2416. // CDatabase::_AllocateSpecialView
  2417. //--------------------------------------------------------------------------
  2418. HRESULT CDatabase::_AllocateSpecialView(FILEADDRESS faView,
  2419. DWORD cbView, LPFILEVIEW *ppSpecial)
  2420. {
  2421. // Locals
  2422. HRESULT hr=S_OK;
  2423. LPFILEVIEW pView=NULL;
  2424. // Trace
  2425. TraceCall("CDatabase::_AllocateSpecialView");
  2426. // Try to find existing special view where faView / cbView fits...
  2427. for (pView = m_pStorage->pSpecial; pView != NULL; pView = pView->pNext)
  2428. {
  2429. // Fit into this view ?
  2430. if (faView >= pView->faView && faView + cbView <= pView->faView + pView->cbView)
  2431. {
  2432. // This is good...
  2433. *ppSpecial = pView;
  2434. // Don't Freep
  2435. pView = NULL;
  2436. // Done
  2437. goto exit;
  2438. }
  2439. }
  2440. // Create a Special View
  2441. IF_NULLEXIT(pView = (LPFILEVIEW)PHeapAllocate(0, sizeof(FILEVIEW)));
  2442. // Set faView
  2443. pView->faView = faView;
  2444. // Set cbView
  2445. pView->cbView = cbView;
  2446. // Map the View
  2447. IF_FAILEXIT(hr = DBMapViewOfFile(m_pStorage->hMap, m_pStorage->cbFile, &pView->faView, &pView->cbView, (LPVOID *)&pView->pbView));
  2448. // Increment Statistic
  2449. m_pStorage->cSpecial++;
  2450. // Increment cbMappedSpecial
  2451. m_pStorage->cbMappedSpecial += pView->cbView;
  2452. // Link pView into Special List
  2453. pView->pNext = m_pStorage->pSpecial;
  2454. // Set pSpecial
  2455. m_pStorage->pSpecial = pView;
  2456. // Set Return
  2457. *ppSpecial = pView;
  2458. // Don't Free It
  2459. pView = NULL;
  2460. exit:
  2461. // Cleanup
  2462. SafeHeapFree(pView);
  2463. // Done
  2464. return(hr);
  2465. }
  2466. //--------------------------------------------------------------------------
  2467. // CDatabase::_GetBlock
  2468. //--------------------------------------------------------------------------
  2469. HRESULT CDatabase::_GetBlock(BLOCKTYPE tyExpected, FILEADDRESS faBlock,
  2470. LPVOID *ppvBlock, LPMARKBLOCK pMark /* =NULL */, BOOL fGoCorrupt /* TRUE */)
  2471. {
  2472. // Locals
  2473. HRESULT hr=S_OK;
  2474. DWORD iViewStart;
  2475. DWORD iViewEnd;
  2476. LPFILEVIEW pView;
  2477. DWORD cbBlock;
  2478. LPBLOCKHEADER pBlock;
  2479. LPFILEVIEW pSpecial=NULL;
  2480. // Trace
  2481. TraceCall("CDatabase::_CheckBlock");
  2482. // Invalid Args
  2483. IxpAssert(faBlock > 0 && ppvBlock);
  2484. // Storage is Setup ?
  2485. IxpAssert(m_pStorage->hMap && m_pStorage->prgView);
  2486. // faBlock is Out-of-Range
  2487. if (faBlock + sizeof(BLOCKHEADER) >= m_pStorage->cbFile)
  2488. {
  2489. hr = _SetCorrupt(fGoCorrupt, __LINE__, REASON_BLOCKSTARTOUTOFRANGE, tyExpected, faBlock, 0xFFFFFFFF, 0xFFFFFFFF);
  2490. goto exit;
  2491. }
  2492. // Determine iView
  2493. iViewStart = (faBlock / CB_MAPPED_VIEW);
  2494. // Set iViewend
  2495. iViewEnd = (faBlock + sizeof(BLOCKHEADER)) / CB_MAPPED_VIEW;
  2496. // If the Header Straddles a view boundary...
  2497. if (iViewStart != iViewEnd)
  2498. {
  2499. // Allocate a Special View
  2500. IF_FAILEXIT(hr = _AllocateSpecialView(faBlock, g_SystemInfo.dwAllocationGranularity, &pSpecial));
  2501. // Set pView
  2502. pView = pSpecial;
  2503. }
  2504. // Otherwise, use a view
  2505. else
  2506. {
  2507. // Validate iView
  2508. IxpAssert(iViewStart < m_pStorage->cAllocated);
  2509. // Readability
  2510. pView = &m_pStorage->prgView[iViewStart];
  2511. // Is this View Mapped yet ?
  2512. if (NULL == pView->pbView)
  2513. {
  2514. // Validate the Entry
  2515. IxpAssert(0 == pView->faView && 0 == pView->cbView && NULL == pView->pNext);
  2516. // Set faView
  2517. pView->faView = (iViewStart * CB_MAPPED_VIEW);
  2518. // Set cbView
  2519. pView->cbView = min(m_pStorage->cbFile - pView->faView, CB_MAPPED_VIEW);
  2520. // Map the View
  2521. IF_FAILEXIT(hr = DBMapViewOfFile(m_pStorage->hMap, m_pStorage->cbFile, &pView->faView, &pView->cbView, (LPVOID *)&pView->pbView));
  2522. // Increment cbMappedSpecial
  2523. m_pStorage->cbMappedViews += pView->cbView;
  2524. }
  2525. }
  2526. // De-Ref the Block (Offset from start of the view)
  2527. pBlock = (LPBLOCKHEADER)(pView->pbView + (faBlock - pView->faView));
  2528. // Mark the block
  2529. if (pMark)
  2530. {
  2531. // Set the Address
  2532. pBlock->faBlock = faBlock;
  2533. // Set cbBlock
  2534. cbBlock = pMark->cbBlock;
  2535. // Adjust cbSize
  2536. pBlock->cbSize = cbBlock - g_rgcbBlockSize[tyExpected];
  2537. }
  2538. // Otherwise, validate the block
  2539. else
  2540. {
  2541. // Get Block Size
  2542. cbBlock = pBlock->cbSize + g_rgcbBlockSize[tyExpected];
  2543. // Check the Block Start Address
  2544. if (faBlock != pBlock->faBlock)
  2545. {
  2546. hr = _SetCorrupt(fGoCorrupt, __LINE__, REASON_UMATCHINGBLOCKADDRESS, tyExpected, faBlock, pBlock->faBlock, cbBlock);
  2547. goto exit;
  2548. }
  2549. // Size of Block is Out-of-range
  2550. if (pBlock->faBlock + cbBlock > m_pStorage->cbFile)
  2551. {
  2552. hr = _SetCorrupt(fGoCorrupt, __LINE__, REASON_BLOCKSIZEOUTOFRANGE, tyExpected, faBlock, pBlock->faBlock, cbBlock);
  2553. goto exit;
  2554. }
  2555. }
  2556. // Compute iViewEnd
  2557. iViewEnd = ((faBlock + cbBlock) / CB_MAPPED_VIEW);
  2558. // Does this block end within the same view, or is the block larger than my view size ?
  2559. if (iViewStart != iViewEnd)
  2560. {
  2561. // If I already allocated a special view...
  2562. if (pSpecial)
  2563. {
  2564. // Validate
  2565. IxpAssert(pView == pSpecial);
  2566. // Does faBlock + cbBlock fit into pSpecial ?
  2567. if ((faBlock - pView->faView) + cbBlock > pView->cbView)
  2568. {
  2569. // Validate
  2570. IxpAssert(pView->pbView);
  2571. // Lets Flush It
  2572. FlushViewOfFile(pView->pbView, 0);
  2573. // Unmap this view
  2574. SafeUnmapViewOfFile(pView->pbView);
  2575. // Decrement cbMappedSpecial
  2576. m_pStorage->cbMappedSpecial -= pView->cbView;
  2577. // Set faView
  2578. pView->faView = faBlock;
  2579. // Set cbView
  2580. pView->cbView = cbBlock;
  2581. // Map the View
  2582. IF_FAILEXIT(hr = DBMapViewOfFile(m_pStorage->hMap, m_pStorage->cbFile, &pView->faView, &pView->cbView, (LPVOID *)&pView->pbView));
  2583. // Increment cbMappedSpecial
  2584. m_pStorage->cbMappedSpecial += pView->cbView;
  2585. }
  2586. }
  2587. // Otherwise, create a special view
  2588. else
  2589. {
  2590. // Allocate a Special View
  2591. IF_FAILEXIT(hr = _AllocateSpecialView(faBlock, cbBlock, &pSpecial));
  2592. // Set pView
  2593. pView = pSpecial;
  2594. }
  2595. }
  2596. // Validate
  2597. IxpAssert((faBlock - pView->faView) + cbBlock <= pView->cbView);
  2598. // Return the Block (offset from start of block)
  2599. *ppvBlock = (LPVOID)(pView->pbView + (faBlock - pView->faView));
  2600. exit:
  2601. // Done
  2602. return(hr);
  2603. }
  2604. //--------------------------------------------------------------------------
  2605. // CDatabase::_ReuseFixedFreeBlock
  2606. //--------------------------------------------------------------------------
  2607. HRESULT CDatabase::_ReuseFixedFreeBlock(LPFILEADDRESS pfaFreeHead,
  2608. BLOCKTYPE tyBlock, DWORD cbExpected, LPVOID *ppvBlock)
  2609. {
  2610. // Locals
  2611. HRESULT hr=S_OK;
  2612. FILEADDRESS faHead=(*pfaFreeHead);
  2613. DWORD cbBlock;
  2614. LPFREEBLOCK pFree;
  2615. // Is there a free block
  2616. if (0 == faHead)
  2617. return(S_OK);
  2618. // Get the Free Block
  2619. IF_FAILEXIT(hr = _GetBlock(BLOCK_FREE, faHead, (LPVOID *)&pFree));
  2620. // Validate
  2621. Assert(cbExpected == pFree->cbBlock);
  2622. // Set *ppHeader
  2623. *ppvBlock = (LPVOID)pFree;
  2624. // Set the New Head Free Chain Block
  2625. *pfaFreeHead = pFree->faNext;
  2626. // Change the Size
  2627. pFree->cbSize = cbExpected - g_rgcbBlockSize[tyBlock];
  2628. // Mark the Block
  2629. *ppvBlock = (LPVOID)pFree;
  2630. exit:
  2631. // Done
  2632. return(hr);
  2633. }
  2634. //--------------------------------------------------------------------------
  2635. // CDatabase::_AllocateBlock
  2636. //--------------------------------------------------------------------------
  2637. HRESULT CDatabase::_AllocateBlock(BLOCKTYPE tyBlock, DWORD cbExtra,
  2638. LPVOID *ppvBlock)
  2639. {
  2640. // Locals
  2641. HRESULT hr=S_OK;
  2642. DWORD cbBlock;
  2643. DWORD iBucket;
  2644. // Trace
  2645. TraceCall("CDatabase::_AllocateBlock");
  2646. // Invalid State
  2647. Assert(ppvBlock && BLOCK_ENDOFPAGE != tyBlock && BLOCK_FREE != tyBlock);
  2648. // Initialize
  2649. *ppvBlock = NULL;
  2650. // Add Space Needed to store tyBlock
  2651. cbBlock = (g_rgcbBlockSize[tyBlock] + cbExtra);
  2652. // Dword Align
  2653. cbBlock += DwordAlign(cbBlock);
  2654. // Allocating a Chain Block ?
  2655. if (BLOCK_CHAIN == tyBlock)
  2656. {
  2657. // Reuse Free Block...
  2658. IF_FAILEXIT(hr = _ReuseFixedFreeBlock(&m_pHeader->faFreeChainBlock, BLOCK_CHAIN, cbBlock, ppvBlock));
  2659. }
  2660. // Allocating a Stream Block ?
  2661. else if (BLOCK_STREAM == tyBlock)
  2662. {
  2663. // Append Stream Block Size
  2664. cbBlock += CB_STREAM_BLOCK;
  2665. // Reuse Free Block...
  2666. IF_FAILEXIT(hr = _ReuseFixedFreeBlock(&m_pHeader->faFreeStreamBlock, BLOCK_STREAM, cbBlock, ppvBlock));
  2667. }
  2668. // Otherwise, allocating a record block
  2669. else if (cbBlock <= CB_MAX_FREE_BUCKET)
  2670. {
  2671. // Adjust cbBlock
  2672. if (cbBlock < CB_MIN_FREE_BUCKET)
  2673. cbBlock = CB_MIN_FREE_BUCKET;
  2674. // Compute Free Block Bucket
  2675. iBucket = ((cbBlock - CB_MIN_FREE_BUCKET) / CB_FREE_BUCKET);
  2676. // Validate
  2677. Assert(iBucket < CC_FREE_BUCKETS);
  2678. // Is there a Free Block in this bucket
  2679. if (m_pHeader->rgfaFreeBlock[iBucket])
  2680. {
  2681. // PopFreeBlock
  2682. _ReuseFixedFreeBlock(&m_pHeader->rgfaFreeBlock[iBucket], tyBlock, cbBlock, ppvBlock);
  2683. }
  2684. }
  2685. // Otherwise
  2686. else
  2687. {
  2688. // Locals
  2689. FILEADDRESS faCurrent;
  2690. LPFREEBLOCK pCurrent;
  2691. LPFREEBLOCK pPrevious=NULL;
  2692. // Adjust cbBlock to the next 1k Boundary
  2693. cbBlock = (((cbBlock / CB_ALIGN_LARGE) + 1) * CB_ALIGN_LARGE);
  2694. // Set faCurrent
  2695. faCurrent = m_pHeader->faFreeLargeBlock;
  2696. // Loop through free large blocks (Sorted from smallest to largest)
  2697. while (faCurrent)
  2698. {
  2699. // Get the Current Block
  2700. IF_FAILEXIT(hr = _GetBlock(BLOCK_FREE, faCurrent, (LPVOID *)&pCurrent));
  2701. // If this block is too small...
  2702. if (cbBlock <= pCurrent->cbBlock)
  2703. {
  2704. // Set Next Free Chain Address
  2705. if (NULL == pPrevious)
  2706. {
  2707. // Set First Free Chain
  2708. m_pHeader->faFreeLargeBlock = pCurrent->faNext;
  2709. }
  2710. // Otherwise, relink free chains
  2711. else
  2712. {
  2713. // Set the next block
  2714. pPrevious->faNext = pCurrent->faNext;
  2715. }
  2716. // Reset the Block Types
  2717. IF_FAILEXIT(hr = _MarkBlock(tyBlock, faCurrent, cbBlock, ppvBlock));
  2718. // Done
  2719. break;
  2720. }
  2721. // Save Previous
  2722. pPrevious = pCurrent;
  2723. // Set Current
  2724. faCurrent = pCurrent->faNext;
  2725. }
  2726. }
  2727. // Didn't find a block to allocate
  2728. if (0 == *ppvBlock)
  2729. {
  2730. // Is there a page with some space on it...
  2731. if (BLOCK_CHAIN == tyBlock)
  2732. {
  2733. // Allocate From Page
  2734. ALLOCATEPAGE AllocatePage=m_pHeader->AllocateChain;
  2735. // Allocate From Page
  2736. IF_FAILEXIT(hr = _AllocateFromPage(BLOCK_CHAIN, &AllocatePage, CB_CHAIN_PAGE, cbBlock, ppvBlock));
  2737. // Restore the page info
  2738. m_pHeader->AllocateChain = AllocatePage;
  2739. }
  2740. // Stream Block
  2741. else if (BLOCK_STREAM == tyBlock)
  2742. {
  2743. // Allocate From Page
  2744. ALLOCATEPAGE AllocatePage=m_pHeader->AllocateStream;
  2745. // Allocate From Page
  2746. IF_FAILEXIT(hr = _AllocateFromPage(BLOCK_STREAM, &AllocatePage, CB_STREAM_PAGE, cbBlock, ppvBlock));
  2747. // Restore the page info
  2748. m_pHeader->AllocateStream = AllocatePage;
  2749. }
  2750. // Record Block
  2751. else
  2752. {
  2753. // Allocate From Page
  2754. ALLOCATEPAGE AllocatePage=m_pHeader->AllocateRecord;
  2755. // Allocate From Page
  2756. IF_FAILEXIT(hr = _AllocateFromPage(tyBlock, &AllocatePage, CB_VARIABLE_PAGE, cbBlock, ppvBlock));
  2757. // Restore the page info
  2758. m_pHeader->AllocateRecord = AllocatePage;
  2759. }
  2760. // Metrics
  2761. m_pHeader->cbAllocated += cbBlock;
  2762. }
  2763. // Otherwise
  2764. else
  2765. {
  2766. // Metrics
  2767. m_pHeader->cbFreed -= cbBlock;
  2768. }
  2769. // Increment
  2770. m_pHeader->rgcbAllocated[tyBlock] += cbBlock;
  2771. exit:
  2772. // We should have found something
  2773. Assert(SUCCEEDED(hr) ? *ppvBlock > 0 : TRUE);
  2774. // Done
  2775. return(hr);
  2776. }
  2777. //--------------------------------------------------------------------------
  2778. // CDatabase::_FreeBlock
  2779. //--------------------------------------------------------------------------
  2780. HRESULT CDatabase::_FreeBlock(BLOCKTYPE tyBlock, FILEADDRESS faAddress)
  2781. {
  2782. // Locals
  2783. HRESULT hr=S_OK;
  2784. DWORD iBucket;
  2785. DWORD cbBlock;
  2786. LPFREEBLOCK pFree;
  2787. // Trace
  2788. TraceCall("CDatabase::_FreeBlock");
  2789. // Invalid Args
  2790. Assert(BLOCK_FREE != tyBlock);
  2791. // Never Free faAddress 0?
  2792. if (0 == faAddress)
  2793. {
  2794. Assert(FALSE);
  2795. hr = TraceResult(E_FAIL);
  2796. goto exit;
  2797. }
  2798. #ifdef DEBUG
  2799. #ifdef FREBLOCK_VALIDATION
  2800. if (BLOCK_RECORD == tyBlock)
  2801. _DebugValidateUnrefedRecord(faAddress);
  2802. #endif // FREBLOCK_VALIDATION
  2803. #endif // DEBUG
  2804. // Get the Block
  2805. IF_FAILEXIT(hr = _GetBlock(tyBlock, faAddress, (LPVOID *)&pFree));
  2806. // Save Block Size
  2807. cbBlock = pFree->cbSize + g_rgcbBlockSize[tyBlock];
  2808. // Mark Block as Free
  2809. pFree->cbSize = cbBlock - g_rgcbBlockSize[BLOCK_FREE];
  2810. // Set Block Size
  2811. pFree->cbBlock = cbBlock;
  2812. // Initialize
  2813. pFree->faNext = 0;
  2814. // BLOCK_CHAIN
  2815. if (BLOCK_CHAIN == tyBlock)
  2816. {
  2817. // Fill free node header
  2818. pFree->faNext = m_pHeader->faFreeChainBlock;
  2819. // Set new iFreeChain
  2820. m_pHeader->faFreeChainBlock = pFree->faBlock;
  2821. }
  2822. // BLOCK_STREAM
  2823. else if (BLOCK_STREAM == tyBlock)
  2824. {
  2825. // Fill free node header
  2826. pFree->faNext = m_pHeader->faFreeStreamBlock;
  2827. // Set new iFreeChain
  2828. m_pHeader->faFreeStreamBlock = pFree->faBlock;
  2829. }
  2830. // Other types of variable length blocks
  2831. else if (pFree->cbBlock <= CB_MAX_FREE_BUCKET)
  2832. {
  2833. // Validate
  2834. Assert(pFree->cbBlock >= CB_MIN_FREE_BUCKET && (pFree->cbBlock % 4) == 0);
  2835. // Compute Free Block Bucket
  2836. iBucket = ((pFree->cbBlock - CB_MIN_FREE_BUCKET) / CB_FREE_BUCKET);
  2837. // Fill free node header
  2838. pFree->faNext = m_pHeader->rgfaFreeBlock[iBucket];
  2839. // Set new iFreeChain
  2840. m_pHeader->rgfaFreeBlock[iBucket] = pFree->faBlock;
  2841. }
  2842. // Otherwise, freeing a large block
  2843. else
  2844. {
  2845. // Must be an integral size of a a large block
  2846. Assert((pFree->cbBlock % CB_ALIGN_LARGE) == 0);
  2847. // If there are no blocks yet
  2848. if (0 == m_pHeader->faFreeLargeBlock)
  2849. {
  2850. // Set the Head
  2851. m_pHeader->faFreeLargeBlock = pFree->faBlock;
  2852. }
  2853. // Otherwise, link into the sorted list
  2854. else
  2855. {
  2856. // Put this block in sorted order from smallest to the largest...
  2857. FILEADDRESS faCurrent;
  2858. LPFREEBLOCK pCurrent;
  2859. LPFREEBLOCK pPrevious=NULL;
  2860. // Set faCurrent
  2861. faCurrent = m_pHeader->faFreeLargeBlock;
  2862. // Loop through free large blocks (Sorted from smallest to largest)
  2863. while (faCurrent)
  2864. {
  2865. // Get the Current Block
  2866. IF_FAILEXIT(hr = _GetBlock(BLOCK_FREE, faCurrent, (LPVOID *)&pCurrent));
  2867. // If pBlock is less than pCurrent, then insert after pPreviuos but before pCurrent
  2868. if (pFree->cbBlock <= pCurrent->cbBlock)
  2869. {
  2870. // Previous
  2871. if (pPrevious)
  2872. {
  2873. // Validate
  2874. Assert(pPrevious->faNext == faCurrent);
  2875. // Set Next
  2876. pPrevious->faNext = pFree->faBlock;
  2877. }
  2878. // Otherwise, adjust the head
  2879. else
  2880. {
  2881. // Validate
  2882. Assert(m_pHeader->faFreeLargeBlock == faCurrent);
  2883. // Set the Head
  2884. m_pHeader->faFreeLargeBlock = pFree->faBlock;
  2885. }
  2886. // Set pBlock Next
  2887. pFree->faNext = faCurrent;
  2888. // Done
  2889. break;
  2890. }
  2891. // Next Block is Null ?
  2892. else if (0 == pCurrent->faNext)
  2893. {
  2894. // Append to the End
  2895. pCurrent->faNext = pFree->faBlock;
  2896. // Done
  2897. break;
  2898. }
  2899. // Save Previous
  2900. pPrevious = pCurrent;
  2901. // Set Current
  2902. faCurrent = pCurrent->faNext;
  2903. }
  2904. }
  2905. }
  2906. // Increment
  2907. m_pHeader->rgcbAllocated[tyBlock] -= pFree->cbBlock;
  2908. // Metrics
  2909. m_pHeader->cbFreed += pFree->cbBlock;
  2910. exit:
  2911. // Done
  2912. return(hr);
  2913. }
  2914. //--------------------------------------------------------------------------
  2915. // CDatabase::GetSize
  2916. //--------------------------------------------------------------------------
  2917. STDMETHODIMP CDatabase::GetSize(LPDWORD pcbFile, LPDWORD pcbAllocated,
  2918. LPDWORD pcbFreed, LPDWORD pcbStreams)
  2919. {
  2920. // Locals
  2921. HRESULT hr=S_OK;
  2922. HLOCK hLock=NULL;
  2923. // Trace
  2924. TraceCall("CDatabase::GetSize");
  2925. // Lock
  2926. IF_FAILEXIT(hr = Lock(&hLock));
  2927. // Return pcbFile
  2928. if (pcbFile)
  2929. *pcbFile = m_pStorage->cbFile;
  2930. // Return pcbAllocated
  2931. if (pcbAllocated)
  2932. *pcbAllocated = m_pHeader->cbAllocated;
  2933. // Return pcbFreed
  2934. if (pcbFreed)
  2935. *pcbFreed = (m_pHeader->cbFreed + (m_pStorage->cbFile - m_pHeader->faNextAllocate));
  2936. // Return pcbStreams
  2937. if (pcbStreams)
  2938. *pcbStreams = m_pHeader->rgcbAllocated[BLOCK_STREAM];
  2939. exit:
  2940. // Unlock the Heap
  2941. Unlock(&hLock);
  2942. // Done
  2943. return(hr);
  2944. }
  2945. //--------------------------------------------------------------------------
  2946. // CDatabase::GetRecordCount
  2947. //--------------------------------------------------------------------------
  2948. HRESULT CDatabase::GetRecordCount(INDEXORDINAL iIndex, ULONG *pcRecords)
  2949. {
  2950. // Locals
  2951. HRESULT hr=S_OK;
  2952. HLOCK hLock=NULL;
  2953. // TraceCall
  2954. TraceCall("CDatabase::GetRecordCount");
  2955. // Invalid Args
  2956. Assert(pcRecords && iIndex < CMAX_INDEXES);
  2957. // Lock
  2958. IF_FAILEXIT(hr = Lock(&hLock));
  2959. // Return the Count
  2960. *pcRecords = m_pHeader->rgcRecords[iIndex];
  2961. exit:
  2962. // Unlock the Heap
  2963. Unlock(&hLock);
  2964. // Done
  2965. return(hr);
  2966. }
  2967. //--------------------------------------------------------------------------
  2968. // CDatabase::UpdateRecord
  2969. //--------------------------------------------------------------------------
  2970. STDMETHODIMP CDatabase::UpdateRecord(LPVOID pBinding)
  2971. {
  2972. // Locals
  2973. HRESULT hr=S_OK;
  2974. HRESULT hrVisible;
  2975. INT nCompare;
  2976. DWORD i;
  2977. INDEXORDINAL iIndex;
  2978. FILEADDRESS faChain;
  2979. NODEINDEX iNode;
  2980. ROWORDINAL iRow;
  2981. FILEADDRESS faOldRecord=0;
  2982. FILEADDRESS faNewRecord=0;
  2983. BYTE bVersion;
  2984. LPVOID pBindingOld=NULL;
  2985. LPRECORDBLOCK pRecord;
  2986. ORDINALLIST Ordinals;
  2987. LPCHAINBLOCK pChain;
  2988. RECORDMAP RecordMap;
  2989. HLOCK hLock=NULL;
  2990. DWORD cNotify=0;
  2991. FINDRESULT rgResult[CMAX_INDEXES];
  2992. // Trace
  2993. TraceCall("CDatabase::UpdateRecord");
  2994. // Invalid Args
  2995. Assert(pBinding);
  2996. // Initialize Ordinals (Initializes everything to INVALID_ROWORDINAL)
  2997. FillMemory(&Ordinals, sizeof(ORDINALLIST), 0xFF);
  2998. // Lock
  2999. IF_FAILEXIT(hr = Lock(&hLock));
  3000. // Primary Index Can not change
  3001. rgResult[0].fChanged = FALSE;
  3002. // Try to find the existing record
  3003. IF_FAILEXIT(hr = _FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, pBinding, &rgResult[0].faChain, &rgResult[0].iNode, &Ordinals.rgiRecord1[IINDEX_PRIMARY]));
  3004. // If not found, you can't update it. Use Insert
  3005. if (DB_S_NOTFOUND == hr)
  3006. {
  3007. hr = TraceResult(DB_E_NOTFOUND);
  3008. goto exit;
  3009. }
  3010. // Primary Index Can not change
  3011. rgResult[0].fFound = TRUE;
  3012. // Cast pChain
  3013. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, rgResult[0].faChain, (LPVOID *)&pChain));
  3014. // De-Reference the Record
  3015. IF_FAILEXIT(hr = _GetBlock(BLOCK_RECORD, pChain->rgNode[rgResult[0].iNode].faRecord, (LPVOID *)&pRecord));
  3016. // Get the Version
  3017. bVersion = *((BYTE *)((LPBYTE)pBinding + m_pSchema->ofVersion));
  3018. // Version Difference ?
  3019. if (pRecord->bVersion != bVersion)
  3020. {
  3021. hr = TraceResult(DB_E_RECORDVERSIONCHANGED);
  3022. goto exit;
  3023. }
  3024. // More than one index ?
  3025. if (m_pHeader->cIndexes > 1 || m_pExtension)
  3026. {
  3027. // Allocate a Binding
  3028. IF_NULLEXIT(pBindingOld = PHeapAllocate(HEAP_ZERO_MEMORY, m_pSchema->cbBinding));
  3029. // Read the Record
  3030. IF_FAILEXIT(hr = _ReadRecord(pRecord->faBlock, pBindingOld));
  3031. }
  3032. // Call Extension
  3033. if (m_pExtension)
  3034. {
  3035. // Extend Record Updates
  3036. m_pExtension->OnRecordUpdate(OPERATION_BEFORE, NULL, pBindingOld, pBinding);
  3037. }
  3038. // Loop through the indexes
  3039. for (i = 1; i < m_pHeader->cIndexes; i++)
  3040. {
  3041. // Get iIndex
  3042. iIndex = m_pHeader->rgiIndex[i];
  3043. // Try to find the existing record
  3044. IF_FAILEXIT(hr = _FindRecord(iIndex, COLUMNS_ALL, pBindingOld, &rgResult[i].faChain, &rgResult[i].iNode, &Ordinals.rgiRecord1[iIndex]));
  3045. // If not found, you can't update it. Use Insert
  3046. if (DB_S_FOUND == hr)
  3047. {
  3048. // We Found the Record
  3049. rgResult[i].fFound = TRUE;
  3050. // Did Record's Key Change for this Index ?
  3051. IF_FAILEXIT(hr = _CompareBinding(iIndex, COLUMNS_ALL, pBinding, pRecord->faBlock, &nCompare));
  3052. // Not the Same ?
  3053. if (0 != nCompare)
  3054. {
  3055. // Changed
  3056. rgResult[i].fChanged = TRUE;
  3057. // Otherwise: Decide Where to insert
  3058. IF_FAILEXIT(hr = _FindRecord(iIndex, COLUMNS_ALL, pBinding, &faChain, &iNode, &iRow));
  3059. // If pBinding is already in this index, then its going to be a duplicate
  3060. if (DB_S_FOUND == hr)
  3061. {
  3062. hr = TraceResult(DB_E_DUPLICATE);
  3063. goto exit;
  3064. }
  3065. }
  3066. // Otherwise, the index hasn't changed
  3067. else
  3068. {
  3069. // Assume the Index is Unchanged
  3070. rgResult[i].fChanged = FALSE;
  3071. }
  3072. }
  3073. // Otherwise, not found
  3074. else
  3075. {
  3076. // This Index Must be Filtered
  3077. Assert(m_rghFilter[iIndex]);
  3078. // Not Found
  3079. rgResult[i].fFound = FALSE;
  3080. // Changed
  3081. rgResult[i].fChanged = TRUE;
  3082. // First Record Never Existed
  3083. Ordinals.rgiRecord1[iIndex] = INVALID_ROWORDINAL;
  3084. // See if the new record already exists in this index
  3085. IF_FAILEXIT(hr = _FindRecord(iIndex, COLUMNS_ALL, pBinding, &faChain, &iNode, &iRow));
  3086. // If pBinding is already in this index, then its going to be a duplicate
  3087. if (DB_S_FOUND == hr)
  3088. {
  3089. hr = TraceResult(DB_E_DUPLICATE);
  3090. goto exit;
  3091. }
  3092. }
  3093. }
  3094. // Save the old node
  3095. faOldRecord = pRecord->faBlock;
  3096. // Get the Record Size
  3097. IF_FAILEXIT(hr = _GetRecordSize(pBinding, &RecordMap));
  3098. // Record Shrunk or stayed the same...?
  3099. if (RecordMap.cbData + RecordMap.cbTags <= pRecord->cbSize && 0 == m_pShare->cNotifyWithData)
  3100. {
  3101. // Persist the Record
  3102. IF_FAILEXIT(hr = _SaveRecord(pRecord, &RecordMap, pBinding));
  3103. // Set faNewRecord
  3104. faNewRecord = pRecord->faBlock;
  3105. // Validate the Version
  3106. Assert(bVersion + 1 == pRecord->bVersion || bVersion + 1 == 256);
  3107. }
  3108. // Otherwise, record grew in size
  3109. else
  3110. {
  3111. // Don't Use This Again
  3112. pRecord = NULL;
  3113. // Link the new record into the table
  3114. IF_FAILEXIT(hr = _LinkRecordIntoTable(&RecordMap, pBinding, bVersion, &faNewRecord));
  3115. }
  3116. // Update all the indexes
  3117. for (i = 0; i < m_pHeader->cIndexes; i++)
  3118. {
  3119. // Get Index Ordinal
  3120. iIndex = m_pHeader->rgiIndex[i];
  3121. // Adjustment for filtered indexes
  3122. hrVisible = _IsVisible(m_rghFilter[iIndex], pBinding);
  3123. // Not Changed ?
  3124. if (S_OK == hrVisible && FALSE == rgResult[i].fChanged && TRUE == rgResult[i].fFound)
  3125. {
  3126. // Record Changed Locations ?
  3127. if (faOldRecord != faNewRecord)
  3128. {
  3129. // Just Update the Address of the New Record
  3130. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, rgResult[i].faChain, (LPVOID *)&pChain));
  3131. // Update the Chain
  3132. pChain->rgNode[rgResult[i].iNode].faRecord = faNewRecord;
  3133. }
  3134. // Ordinal is Unchanged
  3135. Ordinals.rgiRecord2[iIndex] = Ordinals.rgiRecord1[iIndex];
  3136. // If Index changed and somebody wanted notifications about this index
  3137. cNotify += m_pShare->rgcIndexNotify[iIndex];
  3138. }
  3139. // Otherwise...
  3140. else
  3141. {
  3142. // If the Record was found, delete it
  3143. if (TRUE == rgResult[i].fFound)
  3144. {
  3145. // Delete the Record from the index
  3146. IF_FAILEXIT(hr = _IndexDeleteRecord(iIndex, rgResult[i].faChain, rgResult[i].iNode));
  3147. // Adjust Open Rowsets
  3148. _AdjustOpenRowsets(iIndex, Ordinals.rgiRecord1[iIndex], OPERATION_DELETE);
  3149. // Update Record Count
  3150. m_pHeader->rgcRecords[iIndex]--;
  3151. // If Index changed and somebody wanted notifications about this index
  3152. cNotify += m_pShare->rgcIndexNotify[iIndex];
  3153. }
  3154. // Visible ?
  3155. if (S_OK == hrVisible)
  3156. {
  3157. // Otherwise: Decide Where to insert
  3158. IF_FAILEXIT(hr = _FindRecord(iIndex, COLUMNS_ALL, pBinding, &rgResult[i].faChain, &rgResult[i].iNode, &Ordinals.rgiRecord2[iIndex], &rgResult[i].nCompare));
  3159. // Not Found
  3160. Assert(DB_S_NOTFOUND == hr);
  3161. // Do the Insertion
  3162. IF_FAILEXIT(hr = _IndexInsertRecord(iIndex, rgResult[i].faChain, faNewRecord, &rgResult[i].iNode, rgResult[i].nCompare));
  3163. // Update Record Count
  3164. m_pHeader->rgcRecords[iIndex]++;
  3165. // Adjust iRow
  3166. Ordinals.rgiRecord2[iIndex] += (rgResult[i].iNode + 1);
  3167. // Adjust Open Rowsets
  3168. _AdjustOpenRowsets(iIndex, Ordinals.rgiRecord2[iIndex], OPERATION_INSERT);
  3169. // If Index changed and somebody wanted notifications about this index
  3170. cNotify += m_pShare->rgcIndexNotify[iIndex];
  3171. }
  3172. // Otherwise...
  3173. else
  3174. {
  3175. // Doesn't Exist
  3176. Ordinals.rgiRecord2[iIndex] = INVALID_ROWORDINAL;
  3177. }
  3178. }
  3179. }
  3180. // Send Notifications ?
  3181. if (cNotify > 0)
  3182. {
  3183. // Send Notifications ?
  3184. if (0 == m_pShare->cNotifyWithData)
  3185. {
  3186. // Build the Update Notification Package
  3187. _LogTransaction(TRANSACTION_UPDATE, INVALID_INDEX_ORDINAL, &Ordinals, 0, 0);
  3188. }
  3189. // Otherwise...
  3190. else
  3191. {
  3192. // Must have copied...
  3193. Assert(faOldRecord != faNewRecord);
  3194. // Build the Update Notification Package
  3195. _LogTransaction(TRANSACTION_UPDATE, INVALID_INDEX_ORDINAL, &Ordinals, faOldRecord, faNewRecord);
  3196. }
  3197. }
  3198. // Otherwise, free the old record
  3199. else if (faOldRecord != faNewRecord)
  3200. {
  3201. // De-allocate the record from the file
  3202. IF_FAILEXIT(hr = _FreeRecordStorage(OPERATION_UPDATE, faOldRecord));
  3203. }
  3204. // Update the Version
  3205. bVersion++;
  3206. // Store the Version back into the record
  3207. *((WORD *)((LPBYTE)pBinding + m_pSchema->ofVersion)) = bVersion;
  3208. // Version Change
  3209. m_pShare->dwVersion++;
  3210. // Call Extension
  3211. if (m_pExtension)
  3212. {
  3213. // Extend Record Updates
  3214. m_pExtension->OnRecordUpdate(OPERATION_AFTER, &Ordinals, pBindingOld, pBinding);
  3215. }
  3216. exit:
  3217. // Cleanup
  3218. SafeFreeBinding(pBindingOld);
  3219. // Unlock
  3220. Unlock(&hLock);
  3221. // Done
  3222. return(hr);
  3223. }
  3224. //--------------------------------------------------------------------------
  3225. // CDatabase::_LinkRecordIntoTable
  3226. //--------------------------------------------------------------------------
  3227. HRESULT CDatabase::_LinkRecordIntoTable(LPRECORDMAP pMap, LPVOID pBinding,
  3228. BYTE bVersion, LPFILEADDRESS pfaRecord)
  3229. {
  3230. // Locals
  3231. HRESULT hr=S_OK;
  3232. LPRECORDBLOCK pCurrent;
  3233. LPRECORDBLOCK pPrevious;
  3234. // Trace
  3235. TraceCall("CDatabase::_LinkRecordIntoTable");
  3236. // Invalid Args
  3237. Assert(pBinding && pfaRecord);
  3238. // Allocate a block in the file for the record
  3239. IF_FAILEXIT(hr = _AllocateBlock(BLOCK_RECORD, pMap->cbData + pMap->cbTags, (LPVOID *)&pCurrent));
  3240. // Set the Version
  3241. pCurrent->bVersion = bVersion;
  3242. // Persist the Record
  3243. IF_FAILEXIT(hr = _SaveRecord(pCurrent, pMap, pBinding));
  3244. // Return *pfaRecord
  3245. *pfaRecord = pCurrent->faBlock;
  3246. exit:
  3247. // Done
  3248. return(hr);
  3249. }
  3250. //--------------------------------------------------------------------------
  3251. // CDatabase::_AdjustParentNodeCount
  3252. //--------------------------------------------------------------------------
  3253. HRESULT CDatabase::_AdjustParentNodeCount(INDEXORDINAL iIndex,
  3254. FILEADDRESS faChain, LONG lCount)
  3255. {
  3256. // De-ref
  3257. HRESULT hr=S_OK;
  3258. LPCHAINBLOCK pParent;
  3259. LPCHAINBLOCK pCurrent;
  3260. // Trace
  3261. TraceCall("CDatabase::_AdjustParentNodeCount");
  3262. // Invalid Arg
  3263. Assert(faChain && (1 == lCount || -1 == lCount));
  3264. // Set pCurrent
  3265. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faChain, (LPVOID *)&pCurrent));
  3266. // Goto the Parent...
  3267. while (1)
  3268. {
  3269. // Goto the Parent
  3270. if (0 == pCurrent->faParent)
  3271. {
  3272. // Better be the root
  3273. Assert(pCurrent->faBlock == m_pHeader->rgfaIndex[iIndex] && 0 == pCurrent->iParent);
  3274. // Done
  3275. break;
  3276. }
  3277. // Set pCurrent
  3278. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, pCurrent->faParent, (LPVOID *)&pParent));
  3279. // Validate
  3280. Assert(pCurrent->iParent < pParent->cNodes);
  3281. // 0 Node
  3282. if (0 == pCurrent->iParent && pParent->faLeftChain == pCurrent->faBlock)
  3283. {
  3284. // Increment or Decrement Count
  3285. pParent->cLeftNodes += lCount;
  3286. }
  3287. // Otherwise, increment cRightNodes
  3288. else
  3289. {
  3290. // Validate
  3291. Assert(pParent->rgNode[pCurrent->iParent].faRightChain == pCurrent->faBlock);
  3292. // Increment Right Node Count
  3293. pParent->rgNode[pCurrent->iParent].cRightNodes += lCount;
  3294. }
  3295. // Update pCurrent
  3296. pCurrent = pParent;
  3297. }
  3298. exit:
  3299. // Done
  3300. return(hr);
  3301. }
  3302. //--------------------------------------------------------------------------
  3303. // CDatabase::LockNotify
  3304. //--------------------------------------------------------------------------
  3305. STDMETHODIMP CDatabase::LockNotify(LOCKNOTIFYFLAGS dwFlags, LPHLOCK phLock)
  3306. {
  3307. // Locals
  3308. HRESULT hr=S_OK;
  3309. HLOCK hLock=NULL;
  3310. // Trace
  3311. TraceCall("CDatabase::LockNotify");
  3312. // Invalid Args
  3313. if (NULL == phLock)
  3314. return TraceResult(E_INVALIDARG);
  3315. // Initialize
  3316. *phLock = NULL;
  3317. // Lock
  3318. IF_FAILEXIT(hr = Lock(&hLock));
  3319. // Increment Queue Notify count
  3320. m_pShare->cNotifyLock++;
  3321. // Store Some Non-null value
  3322. *phLock = (HLOCK)m_hMutex;
  3323. exit:
  3324. // Unlock
  3325. Unlock(&hLock);
  3326. // Done
  3327. return(hr);
  3328. }
  3329. //--------------------------------------------------------------------------
  3330. // CDatabase::UnlockNotify
  3331. //--------------------------------------------------------------------------
  3332. STDMETHODIMP CDatabase::UnlockNotify(LPHLOCK phLock)
  3333. {
  3334. // Locals
  3335. HRESULT hr=S_OK;
  3336. HLOCK hLock=NULL;
  3337. // Trace
  3338. TraceCall("CDatabase::UnlockNotify");
  3339. // Invalid Args
  3340. if (NULL == phLock)
  3341. return TraceResult(E_INVALIDARG);
  3342. // Nothing to Unlock?
  3343. if (NULL == *phLock)
  3344. return(S_OK);
  3345. // Store Some Non-null value
  3346. Assert(*phLock == (HLOCK)m_hMutex);
  3347. // Lock
  3348. IF_FAILEXIT(hr = Lock(&hLock));
  3349. // Increment Queue Notify count
  3350. m_pShare->cNotifyLock--;
  3351. // No more Lock
  3352. *phLock = NULL;
  3353. // If there are still refs, don't send notifications yet...
  3354. if (m_pShare->cNotifyLock)
  3355. goto exit;
  3356. // Dispatch Pending Notifications
  3357. _DispatchPendingNotifications();
  3358. exit:
  3359. // Unlock
  3360. Unlock(&hLock);
  3361. // Done
  3362. return(hr);
  3363. }
  3364. //--------------------------------------------------------------------------
  3365. // CDatabase::GetTransaction
  3366. //--------------------------------------------------------------------------
  3367. HRESULT CDatabase::GetTransaction(LPHTRANSACTION phTransaction,
  3368. LPTRANSACTIONTYPE ptyTransaction, LPVOID pRecord1, LPVOID pRecord2,
  3369. LPINDEXORDINAL piIndex, LPORDINALLIST pOrdinals)
  3370. {
  3371. // Locals
  3372. HRESULT hr=S_OK;
  3373. HLOCK hLock=NULL;
  3374. LPTRANSACTIONBLOCK pTransaction;
  3375. FILEADDRESS faTransaction;
  3376. // Trace
  3377. TraceCall("CDatabase::GetTransaction");
  3378. // Validate
  3379. Assert(phTransaction && ptyTransaction && pOrdinals);
  3380. // No Transaction
  3381. if (NULL == *phTransaction)
  3382. return TraceResult(E_INVALIDARG);
  3383. // Setup faTransaction
  3384. faTransaction = (FILEADDRESS)PtrToUlong((*phTransaction));
  3385. // Lock
  3386. IF_FAILEXIT(hr = Lock(&hLock));
  3387. // Get the Transaction Block
  3388. IF_FAILEXIT(hr = _GetBlock(BLOCK_TRANSACTION, faTransaction, (LPVOID *)&pTransaction));
  3389. // Validate
  3390. IxpAssert(pTransaction->cRefs > 0);
  3391. // Set tyTransaction
  3392. *ptyTransaction = pTransaction->tyTransaction;
  3393. // Copy Index
  3394. *piIndex = pTransaction->iIndex;
  3395. // Copy Ordinals
  3396. CopyMemory(pOrdinals, &pTransaction->Ordinals, sizeof(ORDINALLIST));
  3397. // Set hNext
  3398. (*phTransaction) = (HTRANSACTION)IntToPtr(pTransaction->faNextInBatch);
  3399. // Did the caller want Record 1
  3400. if (pRecord1 && pTransaction->faRecord1)
  3401. {
  3402. // Free pRecord1
  3403. FreeRecord(pRecord1);
  3404. // Read Record 1
  3405. IF_FAILEXIT(hr = _ReadRecord(pTransaction->faRecord1, pRecord1));
  3406. }
  3407. // Read Second Record
  3408. if (pRecord2 && pTransaction->faRecord2)
  3409. {
  3410. // Must be an Update
  3411. Assert(TRANSACTION_UPDATE == pTransaction->tyTransaction);
  3412. // Free pRecord1
  3413. FreeRecord(pRecord2);
  3414. // Read Record 2
  3415. IF_FAILEXIT(hr = _ReadRecord(pTransaction->faRecord2, pRecord2));
  3416. }
  3417. // Decrement Refs on this item
  3418. pTransaction->cRefs--;
  3419. // If hit zero, release it
  3420. if (pTransaction->cRefs > 0)
  3421. goto exit;
  3422. // Free Transact Block
  3423. IF_FAILEXIT(hr = _FreeTransactBlock(pTransaction));
  3424. exit:
  3425. // Unlock
  3426. Unlock(&hLock);
  3427. // Done
  3428. return(hr);
  3429. }
  3430. //--------------------------------------------------------------------------
  3431. // CDatabase::_FreeTransactBlock
  3432. //--------------------------------------------------------------------------
  3433. HRESULT CDatabase::_FreeTransactBlock(LPTRANSACTIONBLOCK pTransaction)
  3434. {
  3435. // Locals
  3436. HRESULT hr=S_OK;
  3437. LPTRANSACTIONBLOCK pPrevious;
  3438. LPTRANSACTIONBLOCK pNext;
  3439. // Trace
  3440. TraceCall("CDatabase::_FreeTransactBlock");
  3441. // Better be Zero
  3442. IxpAssert(0 == pTransaction->cRefs);
  3443. IxpAssert(m_pHeader->cTransacts > 0);
  3444. // Previous ?
  3445. if (pTransaction->faPrevious)
  3446. {
  3447. // Get the Previous
  3448. IF_FAILEXIT(hr = _GetBlock(BLOCK_TRANSACTION, pTransaction->faPrevious, (LPVOID *)&pPrevious));
  3449. // Set Previous Next
  3450. pPrevious->faNext = pTransaction->faNext;
  3451. }
  3452. // Otherwise, adjust head
  3453. else
  3454. {
  3455. // Validate
  3456. IxpAssert(pTransaction->faBlock == m_pHeader->faTransactHead);
  3457. // Adjust Head
  3458. m_pHeader->faTransactHead = pTransaction->faNext;
  3459. }
  3460. // Next ?
  3461. if (pTransaction->faNext)
  3462. {
  3463. // Get the Previous
  3464. IF_FAILEXIT(hr = _GetBlock(BLOCK_TRANSACTION, pTransaction->faNext, (LPVOID *)&pNext));
  3465. // Set Previous Next
  3466. pNext->faPrevious = pTransaction->faPrevious;
  3467. }
  3468. // Otherwise, adjust head
  3469. else
  3470. {
  3471. // Validate
  3472. IxpAssert(pTransaction->faBlock == m_pHeader->faTransactTail);
  3473. // Adjust Head
  3474. m_pHeader->faTransactTail = pTransaction->faPrevious;
  3475. }
  3476. // Decrement cTransacts
  3477. m_pHeader->cTransacts--;
  3478. // If there is a record 1
  3479. if (pTransaction->faRecord1)
  3480. {
  3481. // TRANSACTION_DELETE gets specail case
  3482. if (TRANSACTION_DELETE == pTransaction->tyTransaction)
  3483. {
  3484. // Free the record, we don't need it
  3485. IF_FAILEXIT(hr = _FreeRecordStorage(OPERATION_DELETE, pTransaction->faRecord1));
  3486. }
  3487. // Otherwise, basic freeblock
  3488. else
  3489. {
  3490. // Free Record 1
  3491. IF_FAILEXIT(hr = _FreeBlock(BLOCK_RECORD, pTransaction->faRecord1));
  3492. }
  3493. }
  3494. // Read Second Record
  3495. if (pTransaction->faRecord2)
  3496. {
  3497. // Read Record 2
  3498. IF_FAILEXIT(hr = _FreeBlock(BLOCK_RECORD, pTransaction->faRecord2));
  3499. }
  3500. // Free this block
  3501. IF_FAILEXIT(hr = _FreeBlock(BLOCK_TRANSACTION, pTransaction->faBlock));
  3502. exit:
  3503. // Done
  3504. return(hr);
  3505. }
  3506. //--------------------------------------------------------------------------
  3507. // CDatabase::_CleanupTransactList
  3508. //--------------------------------------------------------------------------
  3509. HRESULT CDatabase::_CleanupTransactList(void)
  3510. {
  3511. // Locals
  3512. HRESULT hr=S_OK;
  3513. FILEADDRESS faCurrent;
  3514. LPTRANSACTIONBLOCK pTransaction;
  3515. // Trace
  3516. TraceCall("CDatabase::_CleanupTransactList");
  3517. // Validate
  3518. Assert(0 == m_pHeader->faTransactHead ? 0 == m_pHeader->faTransactTail && 0 == m_pHeader->cTransacts : TRUE);
  3519. // Set faCurrent
  3520. faCurrent = m_pHeader->faTransactHead;
  3521. // While we have a current
  3522. while (faCurrent)
  3523. {
  3524. // Get Block
  3525. IF_FAILEXIT(hr = _GetBlock(BLOCK_TRANSACTION, faCurrent, (LPVOID *)&pTransaction));
  3526. // Set faCurrent
  3527. faCurrent = pTransaction->faNext;
  3528. // Set cRefs to Zero
  3529. pTransaction->cRefs = 0;
  3530. // Free It
  3531. IF_FAILEXIT(hr = _FreeTransactBlock(pTransaction));
  3532. }
  3533. exit:
  3534. // Done
  3535. return(hr);
  3536. }
  3537. //--------------------------------------------------------------------------
  3538. // CDatabase::_CopyRecord
  3539. //--------------------------------------------------------------------------
  3540. HRESULT CDatabase::_CopyRecord(FILEADDRESS faRecord, LPFILEADDRESS pfaCopy)
  3541. {
  3542. // Locals
  3543. HRESULT hr=S_OK;
  3544. LPRECORDBLOCK pRecordSrc;
  3545. LPRECORDBLOCK pRecordDst;
  3546. // Trace
  3547. TraceCall("CDatabase::_CopyRecord");
  3548. // Get Soruce
  3549. IF_FAILEXIT(hr = _GetBlock(BLOCK_RECORD, faRecord, (LPVOID *)&pRecordSrc));
  3550. // Allocate a New block
  3551. IF_FAILEXIT(hr = _AllocateBlock(BLOCK_RECORD, pRecordSrc->cbSize, (LPVOID *)&pRecordDst));
  3552. // Get Soruce
  3553. IF_FAILEXIT(hr = _GetBlock(BLOCK_RECORD, faRecord, (LPVOID *)&pRecordSrc));
  3554. // Set Version
  3555. pRecordDst->bVersion = pRecordSrc->bVersion;
  3556. // Set cTags
  3557. pRecordDst->cTags = pRecordSrc->cTags;
  3558. // Copy Data
  3559. CopyMemory((LPBYTE)pRecordDst + sizeof(RECORDBLOCK), (LPBYTE)pRecordSrc + sizeof(RECORDBLOCK), pRecordSrc->cbSize);
  3560. // Return Address
  3561. *pfaCopy = pRecordDst->faBlock;
  3562. exit:
  3563. // Done
  3564. return(hr);
  3565. }
  3566. //--------------------------------------------------------------------------
  3567. // CDatabase::_LogTransaction
  3568. //--------------------------------------------------------------------------
  3569. HRESULT CDatabase::_LogTransaction(TRANSACTIONTYPE tyTransaction,
  3570. INDEXORDINAL iIndex, LPORDINALLIST pOrdinals, FILEADDRESS faInRecord1,
  3571. FILEADDRESS faInRecord2)
  3572. {
  3573. // Locals
  3574. HRESULT hr=S_OK;
  3575. LPTRANSACTIONBLOCK pTransaction;
  3576. LPTRANSACTIONBLOCK pTail;
  3577. FILEADDRESS faRecord1=0;
  3578. FILEADDRESS faRecord2=0;
  3579. // Trace
  3580. TraceCall("CDatabase::_LogTransaction");
  3581. // Nobody is registered
  3582. if (0 == m_pShare->cNotify)
  3583. return(S_OK);
  3584. // If there are people who actually want some data...
  3585. if (m_pShare->cNotifyWithData > 0)
  3586. {
  3587. // Initialize
  3588. faRecord1 = faInRecord1;
  3589. faRecord2 = faInRecord2;
  3590. // TRANSACTION_INSERT
  3591. if (TRANSACTION_INSERT == tyTransaction)
  3592. {
  3593. // Copy Record 2
  3594. IF_FAILEXIT(hr = _CopyRecord(faInRecord1, &faRecord1));
  3595. }
  3596. // TRANSACTION_UPDATE
  3597. else if (TRANSACTION_UPDATE == tyTransaction)
  3598. {
  3599. // Copy Record 2
  3600. IF_FAILEXIT(hr = _CopyRecord(faInRecord2, &faRecord2));
  3601. }
  3602. }
  3603. // Otherwise, free some stuff...
  3604. else if (faInRecord1 > 0)
  3605. {
  3606. // TRANSACTION_DELETE
  3607. if (TRANSACTION_DELETE == tyTransaction)
  3608. {
  3609. // Free the record, we don't need it
  3610. IF_FAILEXIT(hr = _FreeRecordStorage(OPERATION_DELETE, faInRecord1));
  3611. }
  3612. // TRANSACTION_UPDATE
  3613. else if (TRANSACTION_UPDATE == tyTransaction)
  3614. {
  3615. // Free the record, we don't need it
  3616. IF_FAILEXIT(hr = _FreeRecordStorage(OPERATION_UPDATE, faInRecord1));
  3617. }
  3618. }
  3619. // Allocate Notification Block
  3620. IF_FAILEXIT(hr = _AllocateBlock(BLOCK_TRANSACTION, 0, (LPVOID *)&pTransaction));
  3621. // Set tyTransaction
  3622. pTransaction->tyTransaction = tyTransaction;
  3623. // Set cRefs
  3624. pTransaction->cRefs = (WORD)m_pShare->cNotify;
  3625. // Copy iIndex
  3626. pTransaction->iIndex = iIndex;
  3627. // If there are ordinals
  3628. if (pOrdinals)
  3629. {
  3630. // Save Sequence
  3631. CopyMemory(&pTransaction->Ordinals, pOrdinals, sizeof(ORDINALLIST));
  3632. }
  3633. // Otherwise, fill ordinals with record counts
  3634. else
  3635. {
  3636. // Validate Transaction Type
  3637. Assert(TRANSACTION_INDEX_CHANGED == tyTransaction || TRANSACTION_INDEX_DELETED == tyTransaction || TRANSACTION_COMPACTED == tyTransaction);
  3638. // Save Sequence
  3639. ZeroMemory(&pTransaction->Ordinals, sizeof(ORDINALLIST));
  3640. }
  3641. // Set the Record Addresses
  3642. pTransaction->faRecord1 = faRecord1;
  3643. pTransaction->faRecord2 = faRecord2;
  3644. // Link Into the Transaction List
  3645. pTransaction->faNext = pTransaction->faPrevious = pTransaction->faNextInBatch = 0;
  3646. // No Head yet
  3647. if (0 == m_pHeader->faTransactHead)
  3648. {
  3649. // Set Head and Tail
  3650. m_pHeader->faTransactHead = pTransaction->faBlock;
  3651. }
  3652. // Otherwise, append to tail
  3653. else
  3654. {
  3655. // Get the Transaction Block
  3656. IF_FAILEXIT(hr = _GetBlock(BLOCK_TRANSACTION, m_pHeader->faTransactTail, (LPVOID *)&pTail));
  3657. // Link Into the Transaction List
  3658. pTail->faNext = pTransaction->faBlock;
  3659. // Set Previous
  3660. pTransaction->faPrevious = pTail->faBlock;
  3661. }
  3662. // Set the Tail
  3663. m_pHeader->faTransactTail = pTransaction->faBlock;
  3664. // Increment cTransacts
  3665. m_pHeader->cTransacts++;
  3666. // Are we Queueing Notifications...
  3667. if (0 == m_pShare->cNotifyLock)
  3668. {
  3669. // Validate
  3670. IxpAssert(0 == m_pShare->faTransactLockHead && 0 == m_pShare->faTransactLockTail);
  3671. // Dispatch Invoke
  3672. IF_FAILEXIT(hr = _DispatchNotification((HTRANSACTION)IntToPtr(pTransaction->faBlock)));
  3673. }
  3674. // Otherwise, build the transaction lock list
  3675. else
  3676. {
  3677. // Set LockHead
  3678. if (0 == m_pShare->faTransactLockHead)
  3679. {
  3680. // Set the header of the locked transactions
  3681. m_pShare->faTransactLockHead = pTransaction->faBlock;
  3682. }
  3683. // Otherwise, append to tail
  3684. else
  3685. {
  3686. // Get the Transaction Block
  3687. IF_FAILEXIT(hr = _GetBlock(BLOCK_TRANSACTION, m_pShare->faTransactLockTail, (LPVOID *)&pTail));
  3688. // Link Into the Transaction List
  3689. pTail->faNextInBatch = pTransaction->faBlock;
  3690. }
  3691. // Set the Tail
  3692. m_pShare->faTransactLockTail = pTransaction->faBlock;
  3693. }
  3694. exit:
  3695. // Done
  3696. return(hr);
  3697. }
  3698. //--------------------------------------------------------------------------
  3699. // CDatabase::_AdjustOpenRowsets
  3700. //--------------------------------------------------------------------------
  3701. HRESULT CDatabase::_AdjustOpenRowsets(INDEXORDINAL iIndex,
  3702. ROWORDINAL iRow, OPERATIONTYPE tyOperation)
  3703. {
  3704. // Locals
  3705. LPROWSETINFO pRowset;
  3706. DWORD j;
  3707. // Trace
  3708. TraceCall("CDatabase::_AdjustOpenRowsets");
  3709. // Invalid Args
  3710. Assert(OPERATION_DELETE == tyOperation || OPERATION_INSERT == tyOperation);
  3711. // Update open rowsets
  3712. for (j=0; j<m_pShare->Rowsets.cUsed; j++)
  3713. {
  3714. // Set iRowset
  3715. pRowset = &m_pShare->Rowsets.rgRowset[m_pShare->Rowsets.rgiUsed[j]];
  3716. // Does this rowset reference this index ?
  3717. if (pRowset->iIndex == iIndex)
  3718. {
  3719. // How does the newly insert/deleted record affect the current position of this rowset ?
  3720. if (iRow <= pRowset->iRow)
  3721. {
  3722. // lAdjust is negative
  3723. if (OPERATION_DELETE == tyOperation && pRowset->iRow > 1)
  3724. pRowset->iRow--;
  3725. // Otherwise, Increment iRow so that we don't duplicate rows...
  3726. else
  3727. pRowset->iRow += 1;
  3728. }
  3729. }
  3730. }
  3731. // Done
  3732. return(S_OK);
  3733. }
  3734. //--------------------------------------------------------------------------
  3735. // CDatabase::InsertRecord
  3736. //--------------------------------------------------------------------------
  3737. STDMETHODIMP CDatabase::InsertRecord(LPVOID pBinding)
  3738. {
  3739. // Locals
  3740. HRESULT hr=S_OK;
  3741. FILEADDRESS faRecord;
  3742. RECORDMAP RecordMap;
  3743. DWORD i;
  3744. DWORD cNotify=0;
  3745. INDEXORDINAL iIndex;
  3746. HLOCK hLock=NULL;
  3747. FINDRESULT rgResult[CMAX_INDEXES];
  3748. ORDINALLIST Ordinals;
  3749. LPRECORDBLOCK pRecord;
  3750. // Trace
  3751. TraceCall("CDatabase::InsertRecord");
  3752. // Invalid Args
  3753. Assert(pBinding);
  3754. // Initialize Ordinals (Initializes everything to INVALID_ROWORDINAL)
  3755. FillMemory(&Ordinals, sizeof(ORDINALLIST), 0xFF);
  3756. // Lock
  3757. IF_FAILEXIT(hr = Lock(&hLock));
  3758. // Watch for Maximum Unique Id ?
  3759. if (0xFFFFFFFF != m_pSchema->ofUniqueId)
  3760. {
  3761. // Get Id
  3762. DWORD dwId = *((DWORD *)((LPBYTE)pBinding + m_pSchema->ofUniqueId));
  3763. // Reset dwNextId if dwId isn't in the invalid range
  3764. if (0 != dwId && dwId > m_pHeader->dwNextId && dwId < RESERVED_ID_MIN)
  3765. m_pHeader->dwNextId = dwId;
  3766. }
  3767. // IDatabaseExtension
  3768. if (m_pExtension)
  3769. {
  3770. // Extend Insert
  3771. m_pExtension->OnRecordInsert(OPERATION_BEFORE, NULL, pBinding);
  3772. }
  3773. // Loop through all the indexes
  3774. for (i = 0; i < m_pHeader->cIndexes; i++)
  3775. {
  3776. // Get Index Ordinal
  3777. iIndex = m_pHeader->rgiIndex[i];
  3778. // Otherwise: Decide Where to insert
  3779. IF_FAILEXIT(hr = _FindRecord(iIndex, COLUMNS_ALL, pBinding, &rgResult[i].faChain, &rgResult[i].iNode, &Ordinals.rgiRecord1[iIndex], &rgResult[i].nCompare));
  3780. // If key already exist, cache list and return
  3781. if (DB_S_FOUND == hr)
  3782. {
  3783. hr = TraceResult(DB_E_DUPLICATE);
  3784. goto exit;
  3785. }
  3786. }
  3787. // Get the Record Size
  3788. IF_FAILEXIT(hr = _GetRecordSize(pBinding, &RecordMap));
  3789. // Link Record Into the Table
  3790. IF_FAILEXIT(hr = _LinkRecordIntoTable(&RecordMap, pBinding, 0, &faRecord));
  3791. // Version Change
  3792. m_pShare->dwVersion++;
  3793. // Insert into the indexes
  3794. for (i = 0; i < m_pHeader->cIndexes; i++)
  3795. {
  3796. // Get Index Ordinal
  3797. iIndex = m_pHeader->rgiIndex[i];
  3798. // Visible in live index
  3799. if (S_OK == _IsVisible(m_rghFilter[iIndex], pBinding))
  3800. {
  3801. // Do the Insertion
  3802. IF_FAILEXIT(hr = _IndexInsertRecord(iIndex, rgResult[i].faChain, faRecord, &rgResult[i].iNode, rgResult[i].nCompare));
  3803. // Update Record Count
  3804. m_pHeader->rgcRecords[iIndex]++;
  3805. // Adjust iRow
  3806. Ordinals.rgiRecord1[iIndex] += (rgResult[i].iNode + 1);
  3807. // AdjustOpenRowsets
  3808. _AdjustOpenRowsets(iIndex, Ordinals.rgiRecord1[iIndex], OPERATION_INSERT);
  3809. // Notification Required ?
  3810. cNotify += m_pShare->rgcIndexNotify[iIndex];
  3811. }
  3812. // Otherwise, adjust the ordinal to indicate that its not in the index
  3813. else
  3814. {
  3815. // Can't be primary
  3816. Assert(IINDEX_PRIMARY != iIndex);
  3817. // Ordinals.rgiRecord1[iIndex]
  3818. Ordinals.rgiRecord1[iIndex] = INVALID_ROWORDINAL;
  3819. }
  3820. }
  3821. // Set the Version
  3822. *((DWORD *)((LPBYTE)pBinding + m_pSchema->ofVersion)) = 1;
  3823. // Build the Notification Package
  3824. if (cNotify > 0)
  3825. {
  3826. // Build the Package
  3827. _LogTransaction(TRANSACTION_INSERT, INVALID_INDEX_ORDINAL, &Ordinals, faRecord, 0);
  3828. }
  3829. // IDatabaseExtension
  3830. if (m_pExtension)
  3831. {
  3832. // Extend Insert
  3833. m_pExtension->OnRecordInsert(OPERATION_AFTER, &Ordinals, pBinding);
  3834. }
  3835. exit:
  3836. // Finish the Operation
  3837. Unlock(&hLock);
  3838. // Done
  3839. return(hr);
  3840. }
  3841. //--------------------------------------------------------------------------
  3842. // CDatabase::_IndexInsertRecord
  3843. //--------------------------------------------------------------------------
  3844. HRESULT CDatabase::_IndexInsertRecord(INDEXORDINAL iIndex,
  3845. FILEADDRESS faChain, FILEADDRESS faRecord, LPNODEINDEX piNode,
  3846. INT nCompare)
  3847. {
  3848. // Locals
  3849. HRESULT hr=S_OK;
  3850. CHAINNODE Node={0};
  3851. LPCHAINBLOCK pChain;
  3852. // Trace
  3853. TraceCall("CDatabase::_IndexInsertRecord");
  3854. // Invalid Args
  3855. Assert(faRecord > 0);
  3856. Assert(nCompare > 0 || nCompare < 0);
  3857. // If we have a root chain, find a place to insert the new record, or see if the record already exists
  3858. if (0 == m_pHeader->rgfaIndex[iIndex])
  3859. {
  3860. // We should not write into 0
  3861. IF_FAILEXIT(hr = _AllocateBlock(BLOCK_CHAIN, 0, (LPVOID *)&pChain));
  3862. // zero out the block
  3863. ZeroBlock(pChain, sizeof(CHAINBLOCK));
  3864. // Set faStart
  3865. m_pHeader->rgfaIndex[iIndex] = pChain->faBlock;
  3866. // Number of nodes in the chain
  3867. pChain->cNodes = 1;
  3868. // Setup the first node
  3869. pChain->rgNode[0].faRecord = faRecord;
  3870. // Validate piNode
  3871. IxpAssert(*piNode == 0);
  3872. // Return piNode
  3873. *piNode = 0;
  3874. }
  3875. // Otherwise
  3876. else
  3877. {
  3878. // De-ref
  3879. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faChain, (LPVOID *)&pChain));
  3880. // Initialize Node
  3881. Node.faRecord = faRecord;
  3882. // This is very a special increment and has to do with the way a BinarySearch can
  3883. // determine the correct insertion point for a node that did not exist in the
  3884. // array in which a binary search was performed on.
  3885. if (nCompare > 0)
  3886. (*piNode)++;
  3887. // Expand the Chain
  3888. IF_FAILEXIT(hr = _ExpandChain(pChain, (*piNode)));
  3889. // Copy the Node
  3890. CopyMemory(&pChain->rgNode[(*piNode)], &Node, sizeof(CHAINNODE));
  3891. // If Node is FULL, we must do a split insert
  3892. if (pChain->cNodes > BTREE_ORDER)
  3893. {
  3894. // Split the chain
  3895. IF_FAILEXIT(hr = _SplitChainInsert(iIndex, faChain));
  3896. }
  3897. // Mark Chain as dirty, cause we are about to cache it away
  3898. else
  3899. {
  3900. // Increment Parent Record Count
  3901. IF_FAILEXIT(hr = _AdjustParentNodeCount(iIndex, faChain, 1));
  3902. }
  3903. }
  3904. exit:
  3905. // Done
  3906. return(hr);
  3907. }
  3908. //--------------------------------------------------------------------------
  3909. // CDatabase::DeleteRecord
  3910. //--------------------------------------------------------------------------
  3911. STDMETHODIMP CDatabase::DeleteRecord(LPVOID pBinding)
  3912. {
  3913. // Locals
  3914. HRESULT hr=S_OK;
  3915. HLOCK hLock=NULL;
  3916. FILEADDRESS faRecord=0;
  3917. LPVOID pBindingOld=NULL;
  3918. DWORD cNotify=0;
  3919. FINDRESULT rgResult[CMAX_INDEXES];
  3920. ORDINALLIST Ordinals;
  3921. DWORD i;
  3922. INDEXORDINAL iIndex;
  3923. LPCHAINBLOCK pChain;
  3924. // Trace
  3925. TraceCall("CDatabase::DeleteRecord");
  3926. // Invalid Args
  3927. Assert(pBinding);
  3928. // Initialize Ordinals (Initializes everything to INVALID_ROWORDINAL)
  3929. FillMemory(&Ordinals, sizeof(ORDINALLIST), 0xFF);
  3930. // Lock
  3931. IF_FAILEXIT(hr = Lock(&hLock));
  3932. // Otherwise: Decide Where to insert
  3933. IF_FAILEXIT(hr = _FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, pBinding, &rgResult[0].faChain, &rgResult[0].iNode, &Ordinals.rgiRecord1[IINDEX_PRIMARY]));
  3934. // If not found, you can't update it. Use Insert
  3935. if (DB_S_NOTFOUND == hr)
  3936. {
  3937. hr = TraceResult(DB_E_NOTFOUND);
  3938. goto exit;
  3939. }
  3940. // Primary Index Can not change
  3941. rgResult[0].fFound = TRUE;
  3942. // Cast pChain
  3943. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, rgResult[0].faChain, (LPVOID *)&pChain));
  3944. // Set faRecord
  3945. faRecord = pChain->rgNode[rgResult[0].iNode].faRecord;
  3946. // If this was the first index and there are more indexes, then read the origina record so that indexes are updated correctly
  3947. if (m_pHeader->cIndexes > 1 || m_pExtension)
  3948. {
  3949. // Allocate a Record
  3950. IF_NULLEXIT(pBindingOld = PHeapAllocate(HEAP_ZERO_MEMORY, m_pSchema->cbBinding));
  3951. // Read the Record
  3952. IF_FAILEXIT(hr = _ReadRecord(faRecord, pBindingOld));
  3953. }
  3954. // IDatabaseExtension
  3955. if (m_pExtension)
  3956. {
  3957. // Extend Delete
  3958. m_pExtension->OnRecordDelete(OPERATION_BEFORE, NULL, pBindingOld);
  3959. }
  3960. // Loop through the indexes
  3961. for (i=1; i<m_pHeader->cIndexes; i++)
  3962. {
  3963. // Get Index Ordinal
  3964. iIndex = m_pHeader->rgiIndex[i];
  3965. // Otherwise: Decide Where to insert
  3966. IF_FAILEXIT(hr = _FindRecord(iIndex, COLUMNS_ALL, pBindingOld, &rgResult[i].faChain, &rgResult[i].iNode, &Ordinals.rgiRecord1[iIndex]));
  3967. // The, record wasn't found, there must be a filter on the index
  3968. if (DB_S_NOTFOUND == hr)
  3969. {
  3970. // Not found
  3971. rgResult[i].fFound = FALSE;
  3972. // Invalid Ordinal
  3973. Ordinals.rgiRecord1[iIndex] = INVALID_ROWORDINAL;
  3974. }
  3975. // Otherwise
  3976. else
  3977. {
  3978. // Found
  3979. rgResult[i].fFound = TRUE;
  3980. // Get the Chain
  3981. Assert(SUCCEEDED(_GetBlock(BLOCK_CHAIN, rgResult[i].faChain, (LPVOID *)&pChain)));
  3982. // Validation
  3983. Assert(faRecord == pChain->rgNode[rgResult[i].iNode].faRecord);
  3984. }
  3985. }
  3986. // Loop through the indexes
  3987. for (i=0; i<m_pHeader->cIndexes; i++)
  3988. {
  3989. // Get Index Ordinal
  3990. iIndex = m_pHeader->rgiIndex[i];
  3991. // Found ?
  3992. if (rgResult[i].fFound)
  3993. {
  3994. // Lets remove the link from the index first
  3995. IF_FAILEXIT(hr = _IndexDeleteRecord(iIndex, rgResult[i].faChain, rgResult[i].iNode));
  3996. // Validate Record Count
  3997. Assert(m_pHeader->rgcRecords[iIndex] > 0);
  3998. // Update Record Count
  3999. m_pHeader->rgcRecords[iIndex]--;
  4000. // AdjustOpenRowsets
  4001. _AdjustOpenRowsets(iIndex, Ordinals.rgiRecord1[iIndex], OPERATION_DELETE);
  4002. // Does somebody want a notification about this index ?
  4003. cNotify += m_pShare->rgcIndexNotify[iIndex];
  4004. }
  4005. }
  4006. // Notify Somebody ?
  4007. if (cNotify > 0)
  4008. {
  4009. // Build the Update Notification Package
  4010. _LogTransaction(TRANSACTION_DELETE, INVALID_INDEX_ORDINAL, &Ordinals, faRecord, 0);
  4011. }
  4012. // Otherwise, free the record
  4013. else
  4014. {
  4015. // De-allocate the record from the file
  4016. IF_FAILEXIT(hr = _FreeRecordStorage(OPERATION_DELETE, faRecord));
  4017. }
  4018. // Version Change
  4019. m_pShare->dwVersion++;
  4020. // IDatabaseExtension
  4021. if (m_pExtension)
  4022. {
  4023. // Extend Delete
  4024. m_pExtension->OnRecordDelete(OPERATION_AFTER, &Ordinals, pBindingOld);
  4025. }
  4026. exit:
  4027. // Cleanup
  4028. SafeFreeBinding(pBindingOld);
  4029. // Unlock
  4030. Unlock(&hLock);
  4031. // Done
  4032. return(hr);
  4033. }
  4034. //--------------------------------------------------------------------------
  4035. // CDatabase::FindRecord
  4036. //--------------------------------------------------------------------------
  4037. STDMETHODIMP CDatabase::FindRecord(INDEXORDINAL iIndex, DWORD cColumns,
  4038. LPVOID pBinding, LPROWORDINAL piRow)
  4039. {
  4040. // Locals
  4041. HRESULT hr=S_OK;
  4042. FILEADDRESS faChain;
  4043. NODEINDEX iNode;
  4044. LPCHAINBLOCK pChain;
  4045. HLOCK hLock=NULL;
  4046. // Trace
  4047. TraceCall("CDatabase::FindRecord");
  4048. // Invalid Args
  4049. Assert(pBinding);
  4050. // Lock
  4051. IF_FAILEXIT(hr = Lock(&hLock));
  4052. // Find the Record
  4053. IF_FAILEXIT(hr = _FindRecord(iIndex, cColumns, pBinding, &faChain, &iNode, piRow));
  4054. // If Found, Copy the record
  4055. if (DB_S_FOUND == hr)
  4056. {
  4057. // Get the Chain
  4058. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faChain, (LPVOID *)&pChain));
  4059. // Open the Record into pBinding
  4060. IF_FAILEXIT(hr = _ReadRecord(pChain->rgNode[iNode].faRecord, pBinding));
  4061. // Found It
  4062. hr = DB_S_FOUND;
  4063. // Done
  4064. goto exit;
  4065. }
  4066. // Not Found
  4067. hr = DB_S_NOTFOUND;
  4068. exit:
  4069. // Unlock the index
  4070. Unlock(&hLock);
  4071. // Done
  4072. return(hr);
  4073. }
  4074. //--------------------------------------------------------------------------
  4075. // CDatabase::_GetChainByIndex
  4076. //--------------------------------------------------------------------------
  4077. HRESULT CDatabase::_GetChainByIndex(INDEXORDINAL iIndex, ROWORDINAL iRow,
  4078. LPFILEADDRESS pfaChain, LPNODEINDEX piNode)
  4079. {
  4080. // Locals
  4081. HRESULT hr=S_OK;
  4082. FILEADDRESS faChain;
  4083. LPCHAINBLOCK pChain;
  4084. DWORD cLeftNodes=0;
  4085. NODEINDEX i;
  4086. // Trace
  4087. TraceCall("CDatabase::_GetChainByIndex");
  4088. // Invalid Args
  4089. Assert(pfaChain && piNode);
  4090. // Initialize
  4091. faChain = m_pHeader->rgfaIndex[iIndex];
  4092. // Loop
  4093. while (faChain)
  4094. {
  4095. // Corrupt
  4096. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faChain, (LPVOID *)&pChain));
  4097. // Are there left nodes ?
  4098. if (pChain->cLeftNodes > 0 && iRow <= (cLeftNodes + pChain->cLeftNodes))
  4099. {
  4100. // Goto the left
  4101. faChain = pChain->faLeftChain;
  4102. }
  4103. // Otherwise...
  4104. else
  4105. {
  4106. // Increment cLeftNodes
  4107. cLeftNodes += pChain->cLeftNodes;
  4108. // Loop throug right chains
  4109. for (i=0; i<pChain->cNodes; i++)
  4110. {
  4111. // Failure
  4112. if (cLeftNodes + 1 == iRow)
  4113. {
  4114. // We found the Chain
  4115. *pfaChain = faChain;
  4116. // Return the Node Index
  4117. *piNode = i;
  4118. // Done
  4119. goto exit;
  4120. }
  4121. // Increment cLeftNodes
  4122. cLeftNodes++;
  4123. // Goto the Right ?
  4124. if (iRow <= (cLeftNodes + pChain->rgNode[i].cRightNodes))
  4125. {
  4126. // Goto the Right
  4127. faChain = pChain->rgNode[i].faRightChain;
  4128. // Break
  4129. break;
  4130. }
  4131. // First Node ?
  4132. cLeftNodes += pChain->rgNode[i].cRightNodes;
  4133. }
  4134. // Nothing found...
  4135. if (i == pChain->cNodes)
  4136. break;
  4137. }
  4138. }
  4139. // Not Found
  4140. hr = E_FAIL;
  4141. exit:
  4142. // Done
  4143. return(hr);
  4144. }
  4145. //--------------------------------------------------------------------------
  4146. // CDatabase::CreateRowset
  4147. //--------------------------------------------------------------------------
  4148. STDMETHODIMP CDatabase::CreateRowset(INDEXORDINAL iIndex,
  4149. CREATEROWSETFLAGS dwFlags, LPHROWSET phRowset)
  4150. {
  4151. // Locals
  4152. HRESULT hr=S_OK;
  4153. ROWSETORDINAL iRowset;
  4154. LPROWSETTABLE pTable;
  4155. LPROWSETINFO pRowset;
  4156. HLOCK hLock=NULL;
  4157. // Trace
  4158. TraceCall("CDatabase::CreateRowset");
  4159. // Invalid Args
  4160. Assert(iIndex < CMAX_INDEXES && phRowset);
  4161. // Lock
  4162. IF_FAILEXIT(hr = Lock(&hLock));
  4163. // De-Ref the RowsetTable
  4164. pTable = &m_pShare->Rowsets;
  4165. // Init Rowset Table
  4166. if (FALSE == pTable->fInitialized)
  4167. {
  4168. // Init the rgiFree Array
  4169. for (iRowset=0; iRowset<CMAX_OPEN_ROWSETS; iRowset++)
  4170. {
  4171. // set rgiFree
  4172. pTable->rgiFree[iRowset] = iRowset;
  4173. // Set rgRowset
  4174. pTable->rgRowset[iRowset].iRowset = iRowset;
  4175. }
  4176. // Set cFree
  4177. pTable->cFree = CMAX_OPEN_ROWSETS;
  4178. // Initialized
  4179. pTable->fInitialized = TRUE;
  4180. }
  4181. // No free Rowsets ?
  4182. if (0 == pTable->cFree)
  4183. {
  4184. hr = TraceResult(DB_E_TOOMANYOPENROWSETS);
  4185. goto exit;
  4186. }
  4187. // Get a Free Rowset...
  4188. iRowset = pTable->rgiFree[pTable->cFree - 1];
  4189. // Set phRowset (Need to Add one so that I don't return a NULL
  4190. *phRowset = (HROWSET)IntToPtr(iRowset + 1);
  4191. // Set pRowset...
  4192. pRowset = &pTable->rgRowset[iRowset];
  4193. // Validate iRowset
  4194. Assert(pRowset->iRowset == iRowset);
  4195. // Zero the Roset
  4196. ZeroMemory(pRowset, sizeof(ROWSETINFO));
  4197. // Set iRowset
  4198. pRowset->iRowset = iRowset;
  4199. // set iRow
  4200. pRowset->iRow = 1;
  4201. // Set Index
  4202. pRowset->iIndex = iIndex;
  4203. // Remove iRowset from rgiFree
  4204. pTable->cFree--;
  4205. // Put iRowset into rgiUsed
  4206. pTable->rgiUsed[pTable->cUsed] = iRowset;
  4207. // Increment cUsed
  4208. pTable->cUsed++;
  4209. exit:
  4210. // Enter Lock
  4211. Unlock(&hLock);
  4212. // Done
  4213. return(hr);
  4214. }
  4215. //--------------------------------------------------------------------------
  4216. // CDatabase::CloseRowset
  4217. //--------------------------------------------------------------------------
  4218. STDMETHODIMP CDatabase::CloseRowset(LPHROWSET phRowset)
  4219. {
  4220. // Locals
  4221. HRESULT hr=S_OK;
  4222. BYTE i;
  4223. LPROWSETTABLE pTable;
  4224. HLOCK hLock=NULL;
  4225. ROWSETORDINAL iRowset=((ROWSETORDINAL)(*phRowset) - 1);
  4226. LPROWSETINFO pRowset;
  4227. // Nothing ?
  4228. if (NULL == *phRowset)
  4229. return(S_OK);
  4230. // Lock
  4231. IF_FAILEXIT(hr = Lock(&hLock));
  4232. // Get the Rowset
  4233. pRowset = &m_pShare->Rowsets.rgRowset[iRowset];
  4234. // Validate
  4235. Assert(iRowset == pRowset->iRowset);
  4236. // De-Ref the RowsetTable
  4237. pTable = &m_pShare->Rowsets;
  4238. // Search rgiUsed
  4239. for (i=0; i<pTable->cUsed; i++)
  4240. {
  4241. // Is this It ?
  4242. if (pTable->rgiUsed[i] == pRowset->iRowset)
  4243. {
  4244. // Remove this Rowset
  4245. MoveMemory(&pTable->rgiUsed[i], &pTable->rgiUsed[i + 1], sizeof(ROWSETORDINAL) * (pTable->cUsed - (i + 1)));
  4246. // Decrement cUsed
  4247. pTable->cUsed--;
  4248. // Put iRowset into the free list
  4249. pTable->rgiFree[pTable->cFree] = pRowset->iRowset;
  4250. // Increment cFree
  4251. pTable->cFree++;
  4252. // Done
  4253. break;
  4254. }
  4255. }
  4256. // Don't Free Again
  4257. *phRowset = NULL;
  4258. exit:
  4259. // Enter Lock
  4260. Unlock(&hLock);
  4261. // Done
  4262. return(hr);
  4263. }
  4264. //--------------------------------------------------------------------------
  4265. // CDatabase::GetRowOrdinal
  4266. //--------------------------------------------------------------------------
  4267. STDMETHODIMP CDatabase::GetRowOrdinal(INDEXORDINAL iIndex,
  4268. LPVOID pBinding, LPROWORDINAL piRow)
  4269. {
  4270. // Locals
  4271. HRESULT hr=S_OK;
  4272. FILEADDRESS faChain;
  4273. NODEINDEX iNode;
  4274. HLOCK hLock=NULL;
  4275. // Trace
  4276. TraceCall("CDatabase::GetRowOrdinal");
  4277. // Lock
  4278. IF_FAILEXIT(hr = Lock(&hLock));
  4279. // Invalid Arg
  4280. Assert(pBinding && piRow && iIndex < CMAX_INDEXES);
  4281. // Simply do a find record...
  4282. IF_FAILEXIT(hr = _FindRecord(iIndex, COLUMNS_ALL, pBinding, &faChain, &iNode, piRow));
  4283. // No Found
  4284. if (DB_S_NOTFOUND == hr)
  4285. {
  4286. hr = DB_E_NOTFOUND;
  4287. goto exit;
  4288. }
  4289. exit:
  4290. // Process / Thread Safety
  4291. Unlock(&hLock);
  4292. // Done
  4293. return(hr);
  4294. }
  4295. //--------------------------------------------------------------------------
  4296. // CDatabase::SeekRowset
  4297. //--------------------------------------------------------------------------
  4298. STDMETHODIMP CDatabase::SeekRowset(HROWSET hRowset, SEEKROWSETTYPE tySeek,
  4299. LONG cRows, LPROWORDINAL piRowNew)
  4300. {
  4301. // Locals
  4302. HRESULT hr=S_OK;
  4303. HLOCK hLock=NULL;
  4304. ROWORDINAL iRowNew;
  4305. ROWSETORDINAL iRowset=((ROWSETORDINAL)(hRowset) - 1);
  4306. LPROWSETINFO pRowset;
  4307. // Trace
  4308. TraceCall("CDatabase::SeekRowset");
  4309. // Lock
  4310. IF_FAILEXIT(hr = Lock(&hLock));
  4311. // Get the Rowset
  4312. pRowset = &m_pShare->Rowsets.rgRowset[iRowset];
  4313. // Validate
  4314. Assert(iRowset == pRowset->iRowset);
  4315. // If there are no records, then seek operation is meaningless
  4316. if (0 == m_pHeader->rgcRecords[pRowset->iIndex])
  4317. {
  4318. hr = DB_E_NORECORDS;
  4319. goto exit;
  4320. }
  4321. // Seek From Beginning
  4322. if (SEEK_ROWSET_BEGIN == tySeek)
  4323. {
  4324. // Set iRow (and remember 0th row from beginning is row #1)
  4325. iRowNew = (cRows + 1);
  4326. }
  4327. // Seek From Current Position
  4328. else if (SEEK_ROWSET_CURRENT == tySeek)
  4329. {
  4330. // Adjust iRow
  4331. iRowNew = (pRowset->iRow + cRows);
  4332. }
  4333. // SEEK_ROWSET_END
  4334. else if (SEEK_ROWSET_END == tySeek)
  4335. {
  4336. // Adjust iRow
  4337. iRowNew = m_pHeader->rgcRecords[pRowset->iIndex] + cRows;
  4338. }
  4339. // Error
  4340. if (iRowNew > m_pHeader->rgcRecords[pRowset->iIndex] || iRowNew <= 0)
  4341. {
  4342. hr = E_UNEXPECTED;
  4343. goto exit;
  4344. }
  4345. // Set New iRow
  4346. pRowset->iRow = iRowNew;
  4347. // Return piRowNew ?
  4348. if (piRowNew)
  4349. *piRowNew = pRowset->iRow;
  4350. exit:
  4351. // Process / Thread Safety
  4352. Unlock(&hLock);
  4353. // Done
  4354. return(hr);
  4355. }
  4356. //--------------------------------------------------------------------------
  4357. // CDatabase::QueryRowset
  4358. //--------------------------------------------------------------------------
  4359. STDMETHODIMP CDatabase::QueryRowset(HROWSET hRowset, LONG lWanted,
  4360. LPVOID *prgpRecord, LPDWORD pcObtained)
  4361. {
  4362. // Locals
  4363. HRESULT hr=S_OK;
  4364. DWORD cRead=0;
  4365. FILEADDRESS faChain;
  4366. NODEINDEX iNode;
  4367. LPCHAINBLOCK pChain;
  4368. DWORD cWanted=abs(lWanted);
  4369. HLOCK hLock=NULL;
  4370. LONG lDirection=(lWanted < 0 ? -1 : 1);
  4371. ROWSETORDINAL iRowset=((ROWSETORDINAL)(hRowset) - 1);
  4372. LPROWSETINFO pRowset;
  4373. // Trace
  4374. TraceCall("CDatabase::GetRows");
  4375. // Invalid Args
  4376. Assert(prgpRecord && hRowset);
  4377. // Initialize
  4378. if (pcObtained)
  4379. *pcObtained = 0;
  4380. // Lock
  4381. IF_FAILEXIT(hr = Lock(&hLock));
  4382. // Get the Rowset
  4383. pRowset = &m_pShare->Rowsets.rgRowset[iRowset];
  4384. // Validate
  4385. Assert(iRowset == pRowset->iRowset);
  4386. // Invalid ?
  4387. if (0 == pRowset->iRow || pRowset->iRow > m_pHeader->rgcRecords[pRowset->iIndex])
  4388. {
  4389. hr = S_FALSE;
  4390. goto exit;
  4391. }
  4392. // While we have a record address
  4393. while (cRead < cWanted)
  4394. {
  4395. // Get the chain from the index
  4396. if (FAILED(_GetChainByIndex(pRowset->iIndex, pRowset->iRow, &faChain, &iNode)))
  4397. {
  4398. // Done
  4399. pRowset->iRow = 0xffffffff;
  4400. // Done
  4401. break;
  4402. }
  4403. // De-reference the Chain
  4404. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faChain, (LPVOID *)&pChain));
  4405. // Open the Record into pBinding
  4406. IF_FAILEXIT(hr = _ReadRecord(pChain->rgNode[iNode].faRecord, ((LPBYTE)prgpRecord + (m_pSchema->cbBinding * cRead))));
  4407. // Increment cRead
  4408. cRead++;
  4409. // Validate
  4410. Assert(pRowset->iRow > 0 && pRowset->iRow <= m_pHeader->rgcRecords[pRowset->iIndex]);
  4411. // Increment Index
  4412. pRowset->iRow += lDirection;
  4413. // If this is a child chain, I can walk all the nodes
  4414. if (0 == pChain->faLeftChain)
  4415. {
  4416. // Better not have any left nodes
  4417. Assert(0 == pChain->cLeftNodes);
  4418. // Forward ?
  4419. if (lDirection > 0)
  4420. {
  4421. // Loop
  4422. for (NODEINDEX i=iNode + 1; i<pChain->cNodes && cRead < cWanted; i++)
  4423. {
  4424. // Better not have a right chain or any right nodes
  4425. Assert(0 == pChain->rgNode[i].faRightChain && 0 == pChain->rgNode[i].cRightNodes);
  4426. // Open the Record into pBinding
  4427. IF_FAILEXIT(hr = _ReadRecord(pChain->rgNode[i].faRecord, ((LPBYTE)prgpRecord + (m_pSchema->cbBinding * cRead))));
  4428. // Increment cRead
  4429. cRead++;
  4430. // Increment Index
  4431. pRowset->iRow += 1;
  4432. }
  4433. }
  4434. // Otherwise, backwards
  4435. else
  4436. {
  4437. // Loop
  4438. for (LONG i=iNode - 1; i>=0 && cRead < cWanted; i--)
  4439. {
  4440. // Better not have a right chain or any right nodes
  4441. Assert(0 == pChain->rgNode[i].faRightChain && 0 == pChain->rgNode[i].cRightNodes);
  4442. // Open the Record into pBinding
  4443. IF_FAILEXIT(hr = _ReadRecord(pChain->rgNode[i].faRecord, ((LPBYTE)prgpRecord + (m_pSchema->cbBinding * cRead))));
  4444. // Increment cRead
  4445. cRead++;
  4446. // Increment Index
  4447. pRowset->iRow -= 1;
  4448. }
  4449. }
  4450. }
  4451. }
  4452. // Set pcbObtained
  4453. if (pcObtained)
  4454. *pcObtained = cRead;
  4455. // Set hr
  4456. hr = (cRead > 0) ? S_OK : S_FALSE;
  4457. exit:
  4458. // Enter Lock
  4459. Unlock(&hLock);
  4460. // Done
  4461. return(hr);
  4462. }
  4463. //--------------------------------------------------------------------------
  4464. // CDatabase::FreeRecord
  4465. //--------------------------------------------------------------------------
  4466. STDMETHODIMP CDatabase::FreeRecord(LPVOID pBinding)
  4467. {
  4468. // Locals
  4469. LPVOID pFree;
  4470. // Trace
  4471. TraceCall("CDatabase::FreeRecord");
  4472. // Invalid Args
  4473. Assert(pBinding);
  4474. // Get the pointer to free
  4475. pFree = *((LPVOID *)((LPBYTE)pBinding + m_pSchema->ofMemory));
  4476. // Not NULL
  4477. if (pFree)
  4478. {
  4479. // Don't Free again
  4480. *((LPVOID *)((LPBYTE)pBinding + m_pSchema->ofMemory)) = NULL;
  4481. // Free This
  4482. HeapFree(pFree);
  4483. }
  4484. // Done
  4485. return(S_OK);
  4486. }
  4487. //--------------------------------------------------------------------------
  4488. // CDatabase::_GetRecordSize
  4489. //--------------------------------------------------------------------------
  4490. HRESULT CDatabase::_GetRecordSize(LPVOID pBinding, LPRECORDMAP pMap)
  4491. {
  4492. // Locals
  4493. DWORD i;
  4494. LPCTABLECOLUMN pColumn;
  4495. // Trace
  4496. TraceCall("CDatabase::_GetRecordSize");
  4497. // Invalid Args
  4498. Assert(pBinding && pMap);
  4499. // Initialize
  4500. ZeroMemory(pMap, sizeof(RECORDMAP));
  4501. // Walk through the members in the structure
  4502. for (i=0; i<m_pSchema->cColumns; i++)
  4503. {
  4504. // Readability
  4505. pColumn = &m_pSchema->prgColumn[i];
  4506. // Is Data Set ?
  4507. if (FALSE == DBTypeIsDefault(pColumn, pBinding))
  4508. {
  4509. // Compute Amount of Data to Store
  4510. pMap->cbData += DBTypeGetSize(pColumn, pBinding);
  4511. // Count Tags
  4512. pMap->cTags++;
  4513. // Compute Amount of Tags to Store
  4514. pMap->cbTags += sizeof(COLUMNTAG);
  4515. }
  4516. }
  4517. // Done
  4518. return(S_OK);
  4519. }
  4520. //--------------------------------------------------------------------------
  4521. // CDatabase::_SaveRecord
  4522. //--------------------------------------------------------------------------
  4523. HRESULT CDatabase::_SaveRecord(LPRECORDBLOCK pRecord, LPRECORDMAP pMap,
  4524. LPVOID pBinding)
  4525. {
  4526. // Locals
  4527. HRESULT hr=S_OK;
  4528. DWORD i;
  4529. DWORD cbOffset=0;
  4530. DWORD cTags=0;
  4531. LPCTABLECOLUMN pColumn;
  4532. LPCOLUMNTAG pTag;
  4533. // Trace
  4534. TraceCall("CDatabase::_SaveRecord");
  4535. // Invalid Args
  4536. Assert(pRecord && pRecord->faBlock > 0 && pBinding);
  4537. // Better have enough space
  4538. Assert(pMap->cbData + pMap->cbTags <= pRecord->cbSize && pMap->cbTags == (pMap->cTags * sizeof(COLUMNTAG)));
  4539. // Set prgTag
  4540. pMap->prgTag = (LPCOLUMNTAG)((LPBYTE)pRecord + sizeof(RECORDBLOCK));
  4541. // Set pbData
  4542. pMap->pbData = (LPBYTE)((LPBYTE)pRecord + sizeof(RECORDBLOCK) + pMap->cbTags);
  4543. // Walk through the members in the structure
  4544. for (i=0; i<m_pSchema->cColumns; i++)
  4545. {
  4546. // Readability
  4547. pColumn = &m_pSchema->prgColumn[i];
  4548. // Is Data Set ?
  4549. if (FALSE == DBTypeIsDefault(pColumn, pBinding))
  4550. {
  4551. // Compute Hash
  4552. pTag = &pMap->prgTag[cTags];
  4553. // Set Tag Id
  4554. pTag->iColumn = pColumn->iOrdinal;
  4555. // Assume pTag Doesn't Contain Data
  4556. pTag->fData = 0;
  4557. // Store the Offset
  4558. pTag->Offset = cbOffset;
  4559. // WriteBindTypeData
  4560. cbOffset += DBTypeWriteValue(pColumn, pBinding, pTag, pMap->pbData + cbOffset);
  4561. // Count Tags
  4562. cTags++;
  4563. // Validate
  4564. Assert(cbOffset <= pMap->cbData);
  4565. // Done ?
  4566. if (cTags == pMap->cTags)
  4567. {
  4568. // Should have Wrote Everything
  4569. Assert(cbOffset == pMap->cbData);
  4570. // Done
  4571. break;
  4572. }
  4573. }
  4574. }
  4575. // Increment Record Version
  4576. pRecord->bVersion++;
  4577. // Write the number of columns
  4578. pRecord->cTags = pMap->cTags;
  4579. // Validate
  4580. Assert(cTags == pMap->cTags && pRecord->cTags > 0);
  4581. // Done
  4582. return(S_OK);
  4583. }
  4584. //--------------------------------------------------------------------------
  4585. // CDatabase::_ReadRecord
  4586. //--------------------------------------------------------------------------
  4587. HRESULT CDatabase::_ReadRecord(FILEADDRESS faRecord, LPVOID pBinding,
  4588. BOOL fInternal /* = FALSE */)
  4589. {
  4590. // Locals
  4591. HRESULT hr=S_OK;
  4592. LPBYTE pbData=NULL;
  4593. LPCTABLECOLUMN pColumn;
  4594. LPCOLUMNTAG pTag;
  4595. WORD iTag;
  4596. RECORDMAP Map;
  4597. LPRECORDBLOCK pRecord;
  4598. // Trace
  4599. TraceCall("CDatabase::_ReadRecord");
  4600. // Invalid Args
  4601. Assert(faRecord > 0 && pBinding);
  4602. // Zero pBinding
  4603. ZeroMemory(pBinding, m_pSchema->cbBinding);
  4604. // Bad Chain Node
  4605. IF_FAILEXIT(hr = _GetBlock(BLOCK_RECORD, faRecord, (LPVOID *)&pRecord));
  4606. // Get Record Map
  4607. IF_FAILEXIT(hr = _GetRecordMap(TRUE, pRecord, &Map));
  4608. // Not Internal
  4609. if (FALSE == fInternal)
  4610. {
  4611. // Allocate a record
  4612. IF_NULLEXIT(pbData = (LPBYTE)PHeapAllocate(0, Map.cbData));
  4613. // Just read the Record
  4614. CopyMemory(pbData, Map.pbData, Map.cbData);
  4615. // Copy Data From pbData...
  4616. Map.pbData = pbData;
  4617. }
  4618. // Walk through the Tags of the Record
  4619. for (iTag=0; iTag<Map.cTags; iTag++)
  4620. {
  4621. // Readability
  4622. pTag = &Map.prgTag[iTag];
  4623. // Validate the Tag
  4624. if (pTag->iColumn < m_pSchema->cColumns)
  4625. {
  4626. // De-ref the Column
  4627. pColumn = &m_pSchema->prgColumn[pTag->iColumn];
  4628. // Read the Data
  4629. DBTypeReadValue(pColumn, pTag, &Map, pBinding);
  4630. }
  4631. }
  4632. // Store the version of the record into the binding
  4633. *((BYTE *)((LPBYTE)pBinding + m_pSchema->ofVersion)) = pRecord->bVersion;
  4634. // Store a point to the blob and free it later
  4635. *((LPVOID *)((LPBYTE)pBinding + m_pSchema->ofMemory)) = (LPVOID)pbData;
  4636. exit:
  4637. // Done
  4638. return(hr);
  4639. }
  4640. //--------------------------------------------------------------------------
  4641. // CDatabase::BindRecord
  4642. //--------------------------------------------------------------------------
  4643. HRESULT CDatabase::BindRecord(LPRECORDMAP pMap, LPVOID pBinding)
  4644. {
  4645. // Locals
  4646. WORD iTag;
  4647. LPCOLUMNTAG pTag;
  4648. LPCTABLECOLUMN pColumn;
  4649. // Trace
  4650. TraceCall("CDatabase::BindRecord");
  4651. // Zero Out the Binding
  4652. ZeroMemory(pBinding, m_pSchema->cbBinding);
  4653. // Walk through the Tags of the Record
  4654. for (iTag=0; iTag<pMap->cTags; iTag++)
  4655. {
  4656. // Readability
  4657. pTag = &pMap->prgTag[iTag];
  4658. // Validate the Tag
  4659. Assert(pTag->iColumn < m_pSchema->cColumns);
  4660. // De-ref the Column
  4661. pColumn = &m_pSchema->prgColumn[pTag->iColumn];
  4662. // Read the Data
  4663. DBTypeReadValue(pColumn, pTag, pMap, pBinding);
  4664. }
  4665. // Done
  4666. return(S_OK);
  4667. }
  4668. //--------------------------------------------------------------------------
  4669. // CDatabase::_IsVisible (S_OK = Show, S_FALSE = Hide)
  4670. //--------------------------------------------------------------------------
  4671. HRESULT CDatabase::_IsVisible(HQUERY hQuery, LPVOID pBinding)
  4672. {
  4673. // Trace
  4674. TraceCall("CDatabase::_IsVisible");
  4675. // No hQuery, the record must be visible
  4676. if (NULL == hQuery)
  4677. return(S_OK);
  4678. // Evaluate the Query
  4679. return(EvaluateQuery(hQuery, pBinding, m_pSchema, this, m_pExtension));
  4680. }
  4681. //--------------------------------------------------------------------------
  4682. // CDatabase::_CompareBinding
  4683. //--------------------------------------------------------------------------
  4684. HRESULT CDatabase::_CompareBinding(INDEXORDINAL iIndex, DWORD cColumns,
  4685. LPVOID pBinding, FILEADDRESS faRecord, INT *pnCompare)
  4686. {
  4687. // Locals
  4688. HRESULT hr=S_OK;
  4689. DWORD cKeys;
  4690. LPRECORDBLOCK pBlock;
  4691. RECORDMAP Map;
  4692. LPCOLUMNTAG pTag;
  4693. DWORD iKey;
  4694. LPTABLEINDEX pIndex;
  4695. LPCTABLECOLUMN pColumn;
  4696. // Trace
  4697. TraceCall("CDatabase::_CompareBinding");
  4698. // Initialize
  4699. *pnCompare = 1;
  4700. // Get the Right Record Block
  4701. IF_FAILEXIT(hr = _GetBlock(BLOCK_RECORD, faRecord, (LPVOID *)&pBlock));
  4702. // Get the Right Node Data
  4703. IF_FAILEXIT(hr = _GetRecordMap(TRUE, pBlock, &Map));
  4704. // De-Ref the Index
  4705. pIndex = &m_pHeader->rgIndexInfo[iIndex];
  4706. // Compute Number of keys to match (possible partial index search)
  4707. cKeys = min(cColumns, pIndex->cKeys);
  4708. // Loop through the key members
  4709. for (iKey=0; iKey<cKeys; iKey++)
  4710. {
  4711. // Readability
  4712. pColumn = &m_pSchema->prgColumn[pIndex->rgKey[iKey].iColumn];
  4713. // Get Tag From Column Ordinal
  4714. pTag = PTagFromOrdinal(&Map, pColumn->iOrdinal);
  4715. // Compare Types
  4716. *pnCompare = DBTypeCompareBinding(pColumn, &pIndex->rgKey[iKey], pBinding, pTag, &Map);
  4717. // Done
  4718. if (0 != *pnCompare)
  4719. break;
  4720. }
  4721. exit:
  4722. // Done
  4723. return(hr);
  4724. }
  4725. //--------------------------------------------------------------------------
  4726. // CDatabase::_PartialIndexCompare
  4727. //--------------------------------------------------------------------------
  4728. HRESULT CDatabase::_PartialIndexCompare(INDEXORDINAL iIndex, DWORD cColumns,
  4729. LPVOID pBinding, LPCHAINBLOCK *ppChain, LPNODEINDEX piNode, LPROWORDINAL piRow)
  4730. {
  4731. // Locals
  4732. HRESULT hr=S_OK;
  4733. ROWORDINAL iRow=(piRow ? *piRow : 0xffffffff);
  4734. LONG iNode=(*piNode);
  4735. LPCHAINBLOCK pChain=(*ppChain);
  4736. FILEADDRESS faChain;
  4737. INT nCompare=0;
  4738. BYTE fFirstLoop;
  4739. // Trace
  4740. TraceCall("CDatabase::_PartialIndexCompare");
  4741. // Invalid Args
  4742. Assert(pBinding && pChain && piNode && *piNode < pChain->cNodes);
  4743. // Loop
  4744. while (1)
  4745. {
  4746. // Assume no more chains
  4747. faChain = 0;
  4748. // First Loop
  4749. fFirstLoop = TRUE;
  4750. // Loop through Current
  4751. while (1)
  4752. {
  4753. // Validate iNode
  4754. Assert(iNode >= 0 && iNode < BTREE_ORDER);
  4755. // Only do this on every iteration except the first
  4756. if (FALSE == fFirstLoop)
  4757. {
  4758. // Decrement iRow
  4759. iRow -= pChain->rgNode[iNode].cRightNodes;
  4760. }
  4761. // Compare with first node in this chain
  4762. IF_FAILEXIT(hr = _CompareBinding(iIndex, cColumns, pBinding, pChain->rgNode[iNode].faRecord, &nCompare));
  4763. // Validate nCompare
  4764. Assert(0 == nCompare || nCompare > 0);
  4765. // pBinding == Node
  4766. if (0 == nCompare)
  4767. {
  4768. // Set new Found iNode
  4769. *piNode = (NODEINDEX)iNode;
  4770. // Set pFound
  4771. *ppChain = pChain;
  4772. // Update piRow
  4773. if (piRow)
  4774. {
  4775. // Update piRow
  4776. (*piRow) = iRow;
  4777. }
  4778. // Should we goto the left ?
  4779. if (0 == iNode)
  4780. {
  4781. // Set faNextChain
  4782. faChain = pChain->faLeftChain;
  4783. // Updating piRow
  4784. if (piRow)
  4785. {
  4786. // Decrement iRow
  4787. iRow -= pChain->cLeftNodes;
  4788. // Decrement One More ?
  4789. iRow--;
  4790. }
  4791. // Done
  4792. break;
  4793. }
  4794. }
  4795. // If pBinding > Node
  4796. else if (nCompare > 0)
  4797. {
  4798. // Set faNextChain
  4799. faChain = pChain->rgNode[iNode].faRightChain;
  4800. // Done
  4801. break;
  4802. }
  4803. // Decrement iNode
  4804. iNode--;
  4805. // Decrement iRow
  4806. iRow--;
  4807. // No Longer the First Loop
  4808. fFirstLoop = FALSE;
  4809. }
  4810. // Done
  4811. if (0 == faChain)
  4812. break;
  4813. // Get the Current Chain
  4814. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faChain, (LPVOID *)&pChain));
  4815. // Reset iNode
  4816. iNode = pChain->cNodes - 1;
  4817. // Update piRow
  4818. if (piRow)
  4819. {
  4820. // Increment piRow with cLeftNodes
  4821. iRow += pChain->cLeftNodes;
  4822. // Include this node
  4823. iRow++;
  4824. // Loop
  4825. for (NODEINDEX i = 1; i <= pChain->cNodes - 1; i++)
  4826. {
  4827. // Increment with cRightNodes
  4828. iRow += pChain->rgNode[i - 1].cRightNodes;
  4829. // Include this node
  4830. iRow++;
  4831. }
  4832. }
  4833. }
  4834. #ifdef DEBUG
  4835. #ifdef PARTIAL_COMPARE_VALIDATE
  4836. if (piRow)
  4837. {
  4838. ROWORDINAL iOrdinal;
  4839. LPVOID pTmpBind = PHeapAllocate(HEAP_ZERO_MEMORY, m_pSchema->cbBinding);
  4840. IxpAssert(pTmpBind);
  4841. IxpAssert(SUCCEEDED(_ReadRecord((*ppChain)->rgNode[(*piNode)].faRecord, pTmpBind)));
  4842. IxpAssert(SUCCEEDED(GetRowOrdinal(iIndex, pTmpBind, &iOrdinal)));
  4843. IxpAssert(*piRow == iOrdinal);
  4844. SafeFreeBinding(pTmpBind);
  4845. IxpAssert(SUCCEEDED(_CompareBinding(iIndex, cColumns, pBinding, &(*ppChain)->rgNode[(*piNode)], &nCompare)));
  4846. IxpAssert(0 == nCompare);
  4847. }
  4848. #endif
  4849. #endif
  4850. exit:
  4851. // Done
  4852. return(hr);
  4853. }
  4854. //--------------------------------------------------------------------------
  4855. // CDatabase::_FindRecord
  4856. //--------------------------------------------------------------------------
  4857. HRESULT CDatabase::_FindRecord(INDEXORDINAL iIndex, DWORD cColumns,
  4858. LPVOID pBinding, LPFILEADDRESS pfaChain, LPNODEINDEX piNode,
  4859. LPROWORDINAL piRow /*=NULL*/, INT *pnCompare /*=NULL*/)
  4860. {
  4861. // Locals
  4862. HRESULT hr=S_OK;
  4863. LONG lLower;
  4864. LONG lUpper;
  4865. LONG lMiddle=0;
  4866. LONG lLastMiddle;
  4867. INT nCompare=-1;
  4868. DWORD iKey;
  4869. BOOL fPartial;
  4870. LPCHAINBLOCK pChain;
  4871. FILEADDRESS faChain;
  4872. // Trace
  4873. TraceCall("CDatabase::_FindRecord");
  4874. // Invalid Args
  4875. Assert(pBinding && pfaChain && piNode && iIndex < CMAX_INDEXES);
  4876. // Partial Index Search ?
  4877. fPartial = (COLUMNS_ALL == cColumns || cColumns == m_pHeader->rgIndexInfo[iIndex].cKeys) ? FALSE : TRUE;
  4878. // Initialize
  4879. *pfaChain = 0;
  4880. *piNode = 0;
  4881. // Start chain address
  4882. faChain = m_pHeader->rgfaIndex[iIndex];
  4883. // Row Ordinal
  4884. if (piRow)
  4885. *piRow = 0;
  4886. // Loop
  4887. while (faChain)
  4888. {
  4889. // Get the Chain
  4890. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faChain, (LPVOID *)&pChain));
  4891. // Set *pfaChain
  4892. *pfaChain = pChain->faBlock;
  4893. // Compute initial upper and lower bounds
  4894. lLower = 0;
  4895. lUpper = pChain->cNodes - 1;
  4896. // Do binary search / insert
  4897. while (lLower <= lUpper)
  4898. {
  4899. // Compute middle record to compare against
  4900. lMiddle = (BYTE)((lLower + lUpper) / 2);
  4901. // Do the Comparison
  4902. IF_FAILEXIT(hr = _CompareBinding(iIndex, cColumns, pBinding, pChain->rgNode[lMiddle].faRecord, &nCompare));
  4903. // Partial Index Searching
  4904. if (0 == nCompare)
  4905. {
  4906. // Validate
  4907. Assert(lMiddle >= 0 && lMiddle <= BTREE_ORDER);
  4908. // Set the found node
  4909. *piNode = (BYTE)lMiddle;
  4910. // Return *pnCompare
  4911. if (pnCompare)
  4912. *pnCompare = 0;
  4913. // Compute piRow
  4914. if (piRow)
  4915. {
  4916. // Increment piRow with cLeftNodes
  4917. (*piRow) += pChain->cLeftNodes;
  4918. // Include this node
  4919. (*piRow)++;
  4920. // Loop
  4921. for (NODEINDEX iNode=1; iNode<=lMiddle; iNode++)
  4922. {
  4923. // Increment with cRightNodes
  4924. (*piRow) += pChain->rgNode[iNode - 1].cRightNodes;
  4925. // Include this node
  4926. (*piRow)++;
  4927. }
  4928. }
  4929. // Partial Search
  4930. if (fPartial)
  4931. {
  4932. // Handle Partial Search
  4933. IF_FAILEXIT(hr = _PartialIndexCompare(iIndex, cColumns, pBinding, &pChain, piNode, piRow));
  4934. // Set *pfaChain
  4935. *pfaChain = pChain->faBlock;
  4936. }
  4937. // We found it
  4938. hr = DB_S_FOUND;
  4939. // Done
  4940. goto exit;
  4941. }
  4942. // Save last middle position
  4943. lLastMiddle = lMiddle;
  4944. // Compute upper and lower
  4945. if (nCompare > 0)
  4946. lLower = lMiddle + 1;
  4947. else
  4948. lUpper = lMiddle - 1;
  4949. }
  4950. // No match was found, is lpSearchKey less than last middle ?
  4951. if (nCompare < 0 && 0 == lLastMiddle)
  4952. {
  4953. // Goto the left, there is no need to update piRow
  4954. faChain = pChain->faLeftChain;
  4955. }
  4956. // Otherwise
  4957. else
  4958. {
  4959. // If nCompare is less than zero, then...
  4960. if (nCompare < 0)
  4961. lLastMiddle--;
  4962. // Compute piRow
  4963. if (piRow && pChain->rgNode[lLastMiddle].faRightChain)
  4964. {
  4965. // Increment piRow with cLeftNodes
  4966. (*piRow) += pChain->cLeftNodes;
  4967. // Include this node
  4968. (*piRow)++;
  4969. // Loop
  4970. for (NODEINDEX iNode=1; iNode<=lLastMiddle; iNode++)
  4971. {
  4972. // Increment with cRightNodes
  4973. (*piRow) += pChain->rgNode[iNode - 1].cRightNodes;
  4974. // Include this node
  4975. (*piRow)++;
  4976. }
  4977. }
  4978. // Goto the Right
  4979. faChain = pChain->rgNode[lLastMiddle].faRightChain;
  4980. }
  4981. }
  4982. // Set piNode
  4983. *piNode = (NODEINDEX)lMiddle;
  4984. // Return *pnCompare
  4985. if (pnCompare)
  4986. *pnCompare = nCompare;
  4987. // We didn't find it
  4988. hr = DB_S_NOTFOUND;
  4989. exit:
  4990. // Done
  4991. return(hr);
  4992. }
  4993. //--------------------------------------------------------------------------
  4994. // CDatabase::_ExpandChain
  4995. //--------------------------------------------------------------------------
  4996. HRESULT CDatabase::_ExpandChain(LPCHAINBLOCK pChain, NODEINDEX iNodeBase)
  4997. {
  4998. // Locals
  4999. HRESULT hr=S_OK;
  5000. CHAR iNode;
  5001. LPCHAINBLOCK pRightChain;
  5002. // Trace
  5003. TraceCall("CDatabase::_ExpandChain");
  5004. // Invalid Args
  5005. Assert(pChain && pChain->cNodes > 0 && pChain->cNodes < BTREE_ORDER + 1);
  5006. Assert(iNodeBase <= pChain->cNodes);
  5007. // Loop from iNode to cNodes
  5008. for (iNode = pChain->cNodes - 1; iNode >= iNodeBase; iNode--)
  5009. {
  5010. // Propagate this node up one level
  5011. CopyMemory(&pChain->rgNode[iNode + 1], &pChain->rgNode[iNode], sizeof(CHAINNODE));
  5012. // If there is a right chain
  5013. if (pChain->rgNode[iNode].faRightChain)
  5014. {
  5015. // Get the Right Chain
  5016. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, pChain->rgNode[iNode].faRightChain, (LPVOID *)&pRightChain));
  5017. // Validate the current Parent
  5018. Assert(pRightChain->faParent == pChain->faBlock);
  5019. // Validate the current index
  5020. Assert(pRightChain->iParent == iNode);
  5021. // Reset the Parent
  5022. pRightChain->iParent = iNode + 1;
  5023. }
  5024. }
  5025. // Increment Node Count
  5026. pChain->cNodes++;
  5027. exit:
  5028. // Done
  5029. return(hr);
  5030. }
  5031. //--------------------------------------------------------------------------
  5032. // CDatabase::_ChainInsert
  5033. //--------------------------------------------------------------------------
  5034. HRESULT CDatabase::_ChainInsert(INDEXORDINAL iIndex, LPCHAINBLOCK pChain,
  5035. LPCHAINNODE pNodeLeft, LPNODEINDEX piNodeIndex)
  5036. {
  5037. // Locals
  5038. HRESULT hr=S_OK;
  5039. CHAR iNode;
  5040. DWORD iKey;
  5041. INT nCompare;
  5042. LPRECORDBLOCK pBlock;
  5043. LPCHAINNODE pNodeRight;
  5044. LPCOLUMNTAG pTagLeft;
  5045. LPCOLUMNTAG pTagRight;
  5046. RECORDMAP MapLeft;
  5047. RECORDMAP MapRight;
  5048. LPTABLEINDEX pIndex;
  5049. LPCTABLECOLUMN pColumn;
  5050. // Trace
  5051. TraceCall("CDatabase::_ChainInsert");
  5052. // Invalid Args
  5053. Assert(pChain && pNodeLeft && pChain->cNodes > 0);
  5054. // Get the Record Block
  5055. IF_FAILEXIT(hr = _GetBlock(BLOCK_RECORD, pNodeLeft->faRecord, (LPVOID *)&pBlock));
  5056. // Get the Record Map
  5057. IF_FAILEXIT(hr = _GetRecordMap(TRUE, pBlock, &MapLeft));
  5058. // De-Reference the Index
  5059. pIndex = &m_pHeader->rgIndexInfo[iIndex];
  5060. // Insert into chain
  5061. for (iNode = pChain->cNodes - 1; iNode >= 0; iNode--)
  5062. {
  5063. // Set pNodeRight
  5064. pNodeRight = &pChain->rgNode[iNode];
  5065. // Get the Record Block
  5066. IF_FAILEXIT(hr = _GetBlock(BLOCK_RECORD, pNodeRight->faRecord, (LPVOID *)&pBlock));
  5067. // Get the Left Node
  5068. IF_FAILEXIT(hr = _GetRecordMap(TRUE, pBlock, &MapRight));
  5069. // Loop through the key members
  5070. for (iKey=0; iKey<pIndex->cKeys; iKey++)
  5071. {
  5072. // Readability
  5073. pColumn = &m_pSchema->prgColumn[pIndex->rgKey[iKey].iColumn];
  5074. // Get Left Tag
  5075. pTagLeft = PTagFromOrdinal(&MapLeft, pColumn->iOrdinal);
  5076. // Get the Right Tag
  5077. pTagRight = PTagFromOrdinal(&MapRight, pColumn->iOrdinal);
  5078. // Compare Types
  5079. nCompare = DBTypeCompareRecords(pColumn, &pIndex->rgKey[iKey], pTagLeft, pTagRight, &MapLeft, &MapRight);
  5080. // Done
  5081. if (0 != nCompare)
  5082. break;
  5083. }
  5084. // Insert in this node ?
  5085. if (nCompare >= 0)
  5086. break;
  5087. }
  5088. // Expand the Chain
  5089. IF_FAILEXIT(hr = _ExpandChain(pChain, iNode + 1));
  5090. // Final Insert
  5091. CopyMemory(&pChain->rgNode[iNode + 1], pNodeLeft, sizeof(CHAINNODE));
  5092. // Node
  5093. *piNodeIndex = iNode + 1;
  5094. exit:
  5095. // Done
  5096. return(hr);
  5097. }
  5098. //--------------------------------------------------------------------------
  5099. // CDatabase::_SplitChainInsert
  5100. //--------------------------------------------------------------------------
  5101. HRESULT CDatabase::_SplitChainInsert(INDEXORDINAL iIndex, FILEADDRESS faSplit)
  5102. {
  5103. // Locals
  5104. HRESULT hr=S_OK;
  5105. NODEINDEX i;
  5106. NODEINDEX iNode;
  5107. CHAINBLOCK Split;
  5108. DWORD cLeftNodes;
  5109. DWORD cRightNodes;
  5110. FILEADDRESS faChain;
  5111. FILEADDRESS faLeftChain;
  5112. FILEADDRESS faRightChain;
  5113. LPCHAINBLOCK pLeft;
  5114. LPCHAINBLOCK pRight;
  5115. LPCHAINBLOCK pParent;
  5116. LPCHAINBLOCK pSplit;
  5117. // Trace
  5118. TraceCall("CDatabase::_SplitChainInsert");
  5119. // Allocate Space for this chain
  5120. IF_FAILEXIT(hr = _AllocateBlock(BLOCK_CHAIN, 0, (LPVOID *)&pLeft));
  5121. // Set faLeftChain
  5122. faLeftChain = pLeft->faBlock;
  5123. // Zero Allocate
  5124. ZeroBlock(pLeft, sizeof(CHAINBLOCK));
  5125. // Get Split Chain Block
  5126. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faSplit, (LPVOID *)&pSplit));
  5127. // Deref the Chain
  5128. CopyMemory(&Split, pSplit, sizeof(CHAINBLOCK));
  5129. // Set faRigthChain
  5130. faRightChain = faSplit;
  5131. // Get Right Chain
  5132. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faRightChain, (LPVOID *)&pRight));
  5133. // Zero pRight
  5134. ZeroBlock(pRight, sizeof(CHAINBLOCK));
  5135. // Both new child chains will be half filled
  5136. pLeft->cNodes = pRight->cNodes = BTREE_MIN_CAP;
  5137. // Set the Right Chains, left pointer to the middle's right pointer
  5138. pRight->faLeftChain = Split.rgNode[BTREE_MIN_CAP].faRightChain;
  5139. // Adjust faRightChains Parent
  5140. if (pRight->faLeftChain)
  5141. {
  5142. // Locals
  5143. LPCHAINBLOCK pLeftChain;
  5144. // Get Left Chain
  5145. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, pRight->faLeftChain, (LPVOID *)&pLeftChain));
  5146. // Validate the Current Parent
  5147. Assert(pLeftChain->faParent == pRight->faBlock);
  5148. // Validate the index
  5149. Assert(pLeftChain->iParent == BTREE_MIN_CAP);
  5150. // Reset the Parent Index
  5151. pLeftChain->iParent = 0;
  5152. }
  5153. // Set Left Node Count
  5154. pRight->cLeftNodes = Split.rgNode[BTREE_MIN_CAP].cRightNodes;
  5155. // Set the left chains left chain address to the left chain left chain address
  5156. pLeft->faLeftChain = Split.faLeftChain;
  5157. // Adjust Parents
  5158. if (pLeft->faLeftChain)
  5159. {
  5160. // Locals
  5161. LPCHAINBLOCK pLeftChain;
  5162. // Get Left Chain
  5163. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, pLeft->faLeftChain, (LPVOID *)&pLeftChain));
  5164. // Validate the index
  5165. Assert(pLeftChain->iParent == 0);
  5166. // Reset faParent
  5167. pLeftChain->faParent = pLeft->faBlock;
  5168. }
  5169. // Set Left Nodes
  5170. pLeft->cLeftNodes = Split.cLeftNodes;
  5171. // Initialize cRightNodes
  5172. cRightNodes = (BTREE_MIN_CAP + pRight->cLeftNodes);
  5173. // Initialize cLeftNodes
  5174. cLeftNodes = (BTREE_MIN_CAP + pLeft->cLeftNodes);
  5175. // This splits the chain
  5176. for (i=0; i<BTREE_MIN_CAP; i++)
  5177. {
  5178. // Copy Right Node
  5179. CopyMemory(&pRight->rgNode[i], &Split.rgNode[i + BTREE_MIN_CAP + 1], sizeof(CHAINNODE));
  5180. // Adjust the Child's iParent ?
  5181. if (pRight->rgNode[i].faRightChain)
  5182. {
  5183. // Locals
  5184. LPCHAINBLOCK pRightChain;
  5185. // Get Left Chain
  5186. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, pRight->rgNode[i].faRightChain, (LPVOID *)&pRightChain));
  5187. // Validate the Current Parent
  5188. Assert(pRightChain->faParent == pRight->faBlock);
  5189. // Validate the index
  5190. Assert(pRightChain->iParent == i + BTREE_MIN_CAP + 1);
  5191. // Reset the Parent
  5192. pRightChain->iParent = i;
  5193. }
  5194. // Count All Child Nodes on the Right
  5195. cRightNodes += pRight->rgNode[i].cRightNodes;
  5196. // Copy Left Node
  5197. CopyMemory(&pLeft->rgNode[i], &Split.rgNode[i], sizeof(CHAINNODE));
  5198. // If there is a right chain
  5199. if (pLeft->rgNode[i].faRightChain)
  5200. {
  5201. // Locals
  5202. LPCHAINBLOCK pRightChain;
  5203. // Get Left Chain
  5204. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, pLeft->rgNode[i].faRightChain, (LPVOID *)&pRightChain));
  5205. // Validate the Old Parent
  5206. Assert(pRightChain->faParent == Split.faBlock);
  5207. // Validate the index
  5208. Assert(pRightChain->iParent == i);
  5209. // Reset the Parent
  5210. pRightChain->faParent = pLeft->faBlock;
  5211. }
  5212. // Count All Child Nodes on the Left
  5213. cLeftNodes += pLeft->rgNode[i].cRightNodes;
  5214. }
  5215. // Set the middle nodes right chain address to the right chain's start address
  5216. Split.rgNode[BTREE_MIN_CAP].faRightChain = faRightChain;
  5217. // Set Right Nodes
  5218. Split.rgNode[BTREE_MIN_CAP].cRightNodes = cRightNodes;
  5219. // Done with pLeft and pRight
  5220. pLeft = pRight = NULL;
  5221. // Elevate the middle leaf node - Create new root chain, then were done
  5222. if (0 == Split.faParent)
  5223. {
  5224. // Allocate Space for this chain
  5225. IF_FAILEXIT(hr = _AllocateBlock(BLOCK_CHAIN, 0, (LPVOID *)&pParent));
  5226. // ZeroInit
  5227. ZeroBlock(pParent, sizeof(CHAINBLOCK));
  5228. // Set Node count
  5229. pParent->cNodes = 1;
  5230. // Set Left Chain
  5231. pParent->faLeftChain = faLeftChain;
  5232. // Set Left Node Count
  5233. pParent->cLeftNodes = cLeftNodes;
  5234. // Copy the Root Node
  5235. CopyMemory(&pParent->rgNode[0], &Split.rgNode[BTREE_MIN_CAP], sizeof(CHAINNODE));
  5236. // New Root Chain Address
  5237. m_pHeader->rgfaIndex[iIndex] = pParent->faBlock;
  5238. // Get pLeft
  5239. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faLeftChain, (LPVOID *)&pLeft));
  5240. // Get Right
  5241. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faRightChain, (LPVOID *)&pRight));
  5242. // Set faLeft's faParent
  5243. pRight->faParent = pLeft->faParent = pParent->faBlock;
  5244. // Set faLeft's iParent
  5245. pRight->iParent = pLeft->iParent = 0;
  5246. }
  5247. // Otherwise, locate chainNodeMiddle's parent chain list!
  5248. else
  5249. {
  5250. // De-Reference
  5251. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, Split.faParent, (LPVOID *)&pParent));
  5252. // Insert leaf's middle record into the parent
  5253. IF_FAILEXIT(hr = _ChainInsert(iIndex, pParent, &Split.rgNode[BTREE_MIN_CAP], &iNode));
  5254. // Get pLeft
  5255. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faLeftChain, (LPVOID *)&pLeft));
  5256. // Get Right
  5257. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faRightChain, (LPVOID *)&pRight));
  5258. // Set faLeft's faParent
  5259. pRight->faParent = pLeft->faParent = pParent->faBlock;
  5260. // Update Surrounding Nodes
  5261. if (iNode > 0)
  5262. {
  5263. // Set faRight Chain
  5264. pParent->rgNode[iNode - 1].faRightChain = faLeftChain;
  5265. // Set cRightNodes
  5266. pParent->rgNode[iNode - 1].cRightNodes = cLeftNodes;
  5267. // Set faLeft's iParent
  5268. pRight->iParent = (BYTE)iNode;
  5269. // Set Left Parent
  5270. pLeft->iParent = iNode - 1;
  5271. }
  5272. // iNode is first node
  5273. else if (iNode == 0)
  5274. {
  5275. // Set faLeftChain
  5276. pParent->faLeftChain = faLeftChain;
  5277. // Set cLeftNodes
  5278. pParent->cLeftNodes = cLeftNodes;
  5279. // Set faLeft's iParent
  5280. pRight->iParent = pLeft->iParent = 0;
  5281. }
  5282. // If Node is FULL, we must do a split insert
  5283. if (pParent->cNodes > BTREE_ORDER)
  5284. {
  5285. // Recursive Split
  5286. IF_FAILEXIT(hr = _SplitChainInsert(iIndex, Split.faParent));
  5287. }
  5288. // Other wise, simply write the updated chain list
  5289. else
  5290. {
  5291. // Increment Parent Record Count
  5292. IF_FAILEXIT(hr = _AdjustParentNodeCount(iIndex, Split.faParent, 1));
  5293. }
  5294. }
  5295. exit:
  5296. // Done
  5297. return(hr);
  5298. }
  5299. //--------------------------------------------------------------------------
  5300. // CDatabase::_FreeRecordStorage
  5301. //--------------------------------------------------------------------------
  5302. HRESULT CDatabase::_FreeRecordStorage(OPERATIONTYPE tyOperation,
  5303. FILEADDRESS faRecord)
  5304. {
  5305. // Locals
  5306. HRESULT hr=S_OK;
  5307. ULONG i;
  5308. LPVOID pBinding=NULL;
  5309. LPCTABLECOLUMN pColumn;
  5310. // Trace
  5311. TraceCall("CDatabase::_FreeRecordStorage");
  5312. // Invalid Args
  5313. Assert(faRecord > 0);
  5314. // Does the record have streams
  5315. if (OPERATION_DELETE == tyOperation && ISFLAGSET(m_pSchema->dwFlags, TSF_HASSTREAMS))
  5316. {
  5317. // Allocate a Record
  5318. IF_NULLEXIT(pBinding = PHeapAllocate(HEAP_ZERO_MEMORY, m_pSchema->cbBinding));
  5319. // Load the Record from the file
  5320. IF_FAILEXIT(hr = _ReadRecord(faRecord, pBinding));
  5321. // Walk through the members in the structure
  5322. for (i=0; i<m_pSchema->cColumns; i++)
  5323. {
  5324. // Readability
  5325. pColumn = &m_pSchema->prgColumn[i];
  5326. // Variable Length Member ?
  5327. if (CDT_STREAM == pColumn->type)
  5328. {
  5329. // Get the Starting address of the stream
  5330. FILEADDRESS faStart = *((FILEADDRESS *)((LPBYTE)pBinding + pColumn->ofBinding));
  5331. // Release Stream Storage...
  5332. if (faStart > 0)
  5333. {
  5334. // Delete the Stream
  5335. DeleteStream(faStart);
  5336. }
  5337. }
  5338. }
  5339. }
  5340. // Free the base record
  5341. IF_FAILEXIT(hr = _FreeBlock(BLOCK_RECORD, faRecord));
  5342. exit:
  5343. // Cleanup
  5344. SafeFreeBinding(pBinding);
  5345. // Done
  5346. return(hr);
  5347. }
  5348. //--------------------------------------------------------------------------
  5349. // CDatabase::DeleteStream
  5350. //--------------------------------------------------------------------------
  5351. HRESULT CDatabase::DeleteStream(FILEADDRESS faStart)
  5352. {
  5353. // Locals
  5354. HRESULT hr=S_OK;
  5355. DWORD i;
  5356. HLOCK hLock=NULL;
  5357. BOOL fFound=FALSE;
  5358. // Trace
  5359. TraceCall("CDatabase::DeleteStream");
  5360. // See if this stream is currently open any where...
  5361. if (0 == faStart)
  5362. return TraceResult(E_INVALIDARG);
  5363. // Lock
  5364. IF_FAILEXIT(hr = Lock(&hLock));
  5365. // Look for faStart in the stream table
  5366. for (i=0; i<CMAX_OPEN_STREAMS; i++)
  5367. {
  5368. // Is this it ?
  5369. if (faStart == m_pShare->rgStream[i].faStart)
  5370. {
  5371. // The Stream Must be Open
  5372. Assert(m_pShare->rgStream[i].cOpenCount > 0);
  5373. // Mark the stream for delete on close
  5374. m_pShare->rgStream[i].fDeleteOnClose = TRUE;
  5375. // Not that I found It
  5376. fFound = TRUE;
  5377. // Done
  5378. break;
  5379. }
  5380. }
  5381. // If we didn't find it, then lets free the storage
  5382. if (FALSE == fFound)
  5383. {
  5384. // Free the Stream Storage
  5385. IF_FAILEXIT(hr = _FreeStreamStorage(faStart));
  5386. }
  5387. // Update the Version
  5388. m_pShare->dwVersion++;
  5389. exit:
  5390. // Mutal Exclusion
  5391. Unlock(&hLock);
  5392. // Done
  5393. return(hr);
  5394. }
  5395. //--------------------------------------------------------------------------
  5396. // CDatabase::_StreamSychronize
  5397. //--------------------------------------------------------------------------
  5398. HRESULT CDatabase::_StreamSychronize(CDatabaseStream *pStream)
  5399. {
  5400. // Locals
  5401. HRESULT hr=S_OK;
  5402. LPOPENSTREAM pInfo;
  5403. LPSTREAMBLOCK pBlock;
  5404. DWORD iBlock=0;
  5405. BOOL fFound=FALSE;
  5406. IF_DEBUG(DWORD cbOffset=0;)
  5407. FILEADDRESS faCurrent;
  5408. // Trace
  5409. TraceCall("CDatabase::_StreamSychronize");
  5410. // Invalid Args
  5411. Assert(pStream);
  5412. // Get Stream Info
  5413. pInfo = &m_pShare->rgStream[pStream->m_iStream];
  5414. // Validate
  5415. if (pInfo->faStart == pStream->m_faStart)
  5416. goto exit;
  5417. // Set faCurrent
  5418. faCurrent = pInfo->faStart;
  5419. // Loop until I find pStream->m_iCurrent
  5420. while (faCurrent > 0)
  5421. {
  5422. // Valid stream Block
  5423. IF_FAILEXIT(hr = _GetBlock(BLOCK_STREAM, faCurrent, (LPVOID *)&pBlock));
  5424. // Validate
  5425. Assert(0 == pBlock->faNext ? TRUE : pBlock->cbData == pBlock->cbSize);
  5426. // Is this It ?
  5427. if (iBlock == pStream->m_iCurrent)
  5428. {
  5429. // We Found It
  5430. fFound = TRUE;
  5431. // Save m_faCurrent
  5432. pStream->m_faCurrent = faCurrent;
  5433. // Validate Size
  5434. Assert(pStream->m_cbCurrent <= pBlock->cbData && cbOffset + pStream->m_cbCurrent == pStream->m_cbOffset);
  5435. // Done
  5436. break;
  5437. }
  5438. // Goto Next
  5439. faCurrent = pBlock->faNext;
  5440. // Increment iBlock
  5441. iBlock++;
  5442. // Increment cbOffset
  5443. IF_DEBUG(cbOffset += pBlock->cbData;)
  5444. }
  5445. // If not found...
  5446. if (FALSE == fFound)
  5447. {
  5448. hr = TraceResult(E_FAIL);
  5449. goto exit;
  5450. }
  5451. // Reset Start Address
  5452. pStream->m_faStart = pInfo->faStart;
  5453. exit:
  5454. // Done
  5455. return(hr);
  5456. }
  5457. //--------------------------------------------------------------------------
  5458. // CDatabase::StreamCompareDatabase
  5459. //--------------------------------------------------------------------------
  5460. HRESULT CDatabase::StreamCompareDatabase(CDatabaseStream *pStream,
  5461. IDatabase *pDatabase)
  5462. {
  5463. // Locals
  5464. HRESULT hr=S_OK;
  5465. HLOCK hLockSrc=NULL;
  5466. HLOCK hLockDst=NULL;
  5467. CDatabase *pDB=NULL;
  5468. // Lock
  5469. IF_FAILEXIT(hr = Lock(&hLockSrc));
  5470. // Lock the Dst
  5471. IF_FAILEXIT(hr = pDatabase->Lock(&hLockDst));
  5472. // QI for CDatabase
  5473. IF_FAILEXIT(hr = pDatabase->QueryInterface(IID_CDatabase, (LPVOID *)&pDB));
  5474. // Compare m_pStorage->pszMap
  5475. hr = (0 == StrCmpIW(m_pStorage->pszMap, pDB->m_pStorage->pszMap)) ? S_OK : S_FALSE;
  5476. exit:
  5477. // Cleanup
  5478. SafeRelease(pDB);
  5479. // Mutal Exclusion
  5480. Unlock(&hLockSrc);
  5481. pDatabase->Unlock(&hLockDst);
  5482. // Done
  5483. return(hr);
  5484. }
  5485. //--------------------------------------------------------------------------
  5486. // CDatabase::GetStreamAddress
  5487. //--------------------------------------------------------------------------
  5488. HRESULT CDatabase::GetStreamAddress(CDatabaseStream *pStream,
  5489. LPFILEADDRESS pfaStream)
  5490. {
  5491. // Locals
  5492. HRESULT hr=S_OK;
  5493. HLOCK hLock=NULL;
  5494. // Lock
  5495. IF_FAILEXIT(hr = Lock(&hLock));
  5496. // StreamSychronize
  5497. IF_FAILEXIT(hr = _StreamSychronize(pStream));
  5498. // Get the address
  5499. *pfaStream = pStream->m_faStart;
  5500. exit:
  5501. // Mutal Exclusion
  5502. Unlock(&hLock);
  5503. // Done
  5504. return(hr);
  5505. }
  5506. //--------------------------------------------------------------------------
  5507. // CDatabase::StreamRead
  5508. //--------------------------------------------------------------------------
  5509. HRESULT CDatabase::StreamRead(CDatabaseStream *pStream, LPVOID pvData,
  5510. ULONG cbWanted, ULONG *pcbRead)
  5511. {
  5512. // Locals
  5513. HRESULT hr=S_OK;
  5514. LPBYTE pbMap;
  5515. DWORD cbRead=0;
  5516. DWORD cbGet;
  5517. LPSTREAMBLOCK pBlock;
  5518. HLOCK hLock=NULL;
  5519. // Trace
  5520. TraceCall("CDatabase::_StreamRead");
  5521. // Init pcbRead
  5522. if (pcbRead)
  5523. *pcbRead = 0;
  5524. // Lock
  5525. IF_FAILEXIT(hr = Lock(&hLock));
  5526. // Invalid Args
  5527. Assert(pStream && pvData);
  5528. // StreamSychronize
  5529. IF_FAILEXIT(hr = _StreamSychronize(pStream));
  5530. // Loop and Read
  5531. while (cbRead < cbWanted)
  5532. {
  5533. // Valid stream Block
  5534. IF_FAILEXIT(hr = _GetBlock(BLOCK_STREAM, pStream->m_faCurrent, (LPVOID *)&pBlock));
  5535. // Time to go to the next block ?
  5536. if (pStream->m_cbCurrent == pBlock->cbData && 0 != pBlock->faNext)
  5537. {
  5538. // Set m_faCurrent
  5539. pStream->m_faCurrent = pBlock->faNext;
  5540. // Increment m_iCurrent
  5541. pStream->m_iCurrent++;
  5542. // Reset offset into current block
  5543. pStream->m_cbCurrent = 0;
  5544. // Valid stream Block
  5545. IF_FAILEXIT(hr = _GetBlock(BLOCK_STREAM, pStream->m_faCurrent, (LPVOID *)&pBlock));
  5546. }
  5547. // Validate
  5548. Assert(0 == pBlock->faNext ? TRUE : pBlock->cbData == pBlock->cbSize);
  5549. // Validate the Offset
  5550. Assert(pStream->m_cbCurrent <= pBlock->cbData);
  5551. // Determine how much we can read from the current block ?
  5552. cbGet = min(cbWanted - cbRead, pBlock->cbData - pStream->m_cbCurrent);
  5553. // Nothing left to get
  5554. if (cbGet == 0)
  5555. break;
  5556. // Read some bytes
  5557. pbMap = ((LPBYTE)pBlock + sizeof(STREAMBLOCK));
  5558. // Copy the Data
  5559. CopyMemory((LPBYTE)pvData + cbRead, pbMap + pStream->m_cbCurrent, cbGet);
  5560. // Increment Amount of Data Read
  5561. cbRead += cbGet;
  5562. // Increment Offset within Current Block
  5563. pStream->m_cbCurrent += cbGet;
  5564. // Global Offset
  5565. pStream->m_cbOffset += cbGet;
  5566. }
  5567. // Init pcbRead
  5568. if (pcbRead)
  5569. *pcbRead = cbRead;
  5570. exit:
  5571. // Mutal Exclusion
  5572. Unlock(&hLock);
  5573. // Done
  5574. return(hr);
  5575. }
  5576. //--------------------------------------------------------------------------
  5577. // CDatabase::StreamWrite
  5578. //--------------------------------------------------------------------------
  5579. HRESULT CDatabase::StreamWrite(CDatabaseStream *pStream, const void *pvData,
  5580. ULONG cb, ULONG *pcbWrote)
  5581. {
  5582. // Locals
  5583. HRESULT hr=S_OK;
  5584. LPBYTE pbMap;
  5585. DWORD cbWrote=0;
  5586. DWORD cbPut;
  5587. LPSTREAMBLOCK pBlock;
  5588. HLOCK hLock=NULL;
  5589. // Trace
  5590. TraceCall("CDatabase::StreamWrite");
  5591. // Init pcbRead
  5592. if (pcbWrote)
  5593. *pcbWrote = 0;
  5594. // Lock
  5595. IF_FAILEXIT(hr = Lock(&hLock));
  5596. // Invalid Args
  5597. Assert(pStream && pStream->m_tyAccess == ACCESS_WRITE && pvData);
  5598. // StreamSychronize
  5599. IF_FAILEXIT(hr = _StreamSychronize(pStream));
  5600. // Loop and Read
  5601. while (cbWrote < cb)
  5602. {
  5603. // Valid stream Block
  5604. IF_FAILEXIT(hr = _GetBlock(BLOCK_STREAM, pStream->m_faCurrent, (LPVOID *)&pBlock));
  5605. // Validate
  5606. Assert(0 == pBlock->faNext ? TRUE : pBlock->cbData == pBlock->cbSize);
  5607. // Validation
  5608. Assert(pStream->m_cbCurrent <= pBlock->cbData);
  5609. // Have we written to the end of the current block and there are more blocks
  5610. if (pStream->m_cbCurrent == pBlock->cbSize)
  5611. {
  5612. // Are there more blocks
  5613. if (0 == pBlock->faNext)
  5614. {
  5615. // Locals
  5616. LPSTREAMBLOCK pBlockNew;
  5617. // Allocate a block in the tree
  5618. IF_FAILEXIT(hr = _AllocateBlock(BLOCK_STREAM, 0, (LPVOID *)&pBlockNew));
  5619. // Get the Current Stream Block
  5620. IF_FAILEXIT(hr = _GetBlock(BLOCK_STREAM, pStream->m_faCurrent, (LPVOID *)&pBlock));
  5621. // Set the next block address on the current block
  5622. pBlock->faNext = pBlockNew->faBlock;
  5623. // Access the block
  5624. pBlock = pBlockNew;
  5625. // Initial 0 data
  5626. pBlock->cbData = 0;
  5627. // No next block
  5628. pBlock->faNext = 0;
  5629. }
  5630. // Otherwise, move to the next block
  5631. else
  5632. {
  5633. // Save faBlcok
  5634. IF_FAILEXIT(hr = _GetBlock(BLOCK_STREAM, pBlock->faNext, (LPVOID *)&pBlock));
  5635. }
  5636. // Set m_faCurrent
  5637. pStream->m_faCurrent = pBlock->faBlock;
  5638. // Increment the block index
  5639. pStream->m_iCurrent++;
  5640. // Reset the Current Block Offset
  5641. pStream->m_cbCurrent = 0;
  5642. // Validate
  5643. Assert(0 == pBlock->faNext ? TRUE : pBlock->cbData == pBlock->cbSize);
  5644. }
  5645. // Compute how much of the cb we can write
  5646. cbPut = min(cb - cbWrote, pBlock->cbSize - pStream->m_cbCurrent);
  5647. // Read some bytes
  5648. pbMap = ((LPBYTE)pBlock + sizeof(STREAMBLOCK));
  5649. // Check memory
  5650. //Assert(FALSE == IsBadWritePtr(pbMap, cbPut));
  5651. // Check memory
  5652. //Assert(FALSE == IsBadReadPtr((LPBYTE)pvData + cbWrote, cbPut));
  5653. // Copy the Data
  5654. CopyMemory(pbMap + pStream->m_cbCurrent, (LPBYTE)pvData + cbWrote, cbPut);
  5655. // Increment the Offset within the current block
  5656. pStream->m_cbCurrent += cbPut;
  5657. // Increment the Offset within the current block
  5658. pStream->m_cbOffset += cbPut;
  5659. // Increment the Amount that has been wrote
  5660. cbWrote += cbPut;
  5661. // Increment the amount of data in the block only if we are expanding its size
  5662. if (0 == pBlock->faNext && pStream->m_cbCurrent > pBlock->cbData)
  5663. {
  5664. // Set the Amount of Data in this block
  5665. pBlock->cbData = pStream->m_cbCurrent;
  5666. }
  5667. // Otherwise, the block should be full
  5668. else
  5669. Assert(pBlock->cbData == pBlock->cbSize);
  5670. }
  5671. // Init pcbRead
  5672. if (pcbWrote)
  5673. *pcbWrote = cbWrote;
  5674. // Update Version
  5675. m_pShare->dwVersion++;
  5676. exit:
  5677. // Mutal Exclusion
  5678. Unlock(&hLock);
  5679. // Done
  5680. return(hr);
  5681. }
  5682. //--------------------------------------------------------------------------
  5683. // CDatabase::StreamSeek
  5684. //--------------------------------------------------------------------------
  5685. HRESULT CDatabase::StreamSeek(CDatabaseStream *pStream, LARGE_INTEGER liMove,
  5686. DWORD dwOrigin, ULARGE_INTEGER *pulNew)
  5687. {
  5688. // Locals
  5689. HRESULT hr=S_OK;
  5690. DWORD cbNewOffset;
  5691. LONG lOffset;
  5692. DWORD cbSize=0;
  5693. FILEADDRESS faBlock;
  5694. LPSTREAMBLOCK pBlock;
  5695. HLOCK hLock=NULL;
  5696. // Trace
  5697. TraceCall("CDatabase::StreamSeek");
  5698. // Lock
  5699. IF_FAILEXIT(hr = Lock(&hLock));
  5700. // Invalid Args
  5701. Assert(pStream && 0 == liMove.HighPart);
  5702. // StreamSychronize
  5703. IF_FAILEXIT(hr = _StreamSychronize(pStream));
  5704. // Cast lowpart
  5705. lOffset = (LONG)liMove.LowPart;
  5706. // STREAM_SEEK_CUR
  5707. if (STREAM_SEEK_CUR == dwOrigin)
  5708. {
  5709. // Validate
  5710. if (lOffset < 0 && (DWORD)(0 - lOffset) > pStream->m_cbOffset)
  5711. {
  5712. hr = TraceResult(E_FAIL);
  5713. goto exit;
  5714. }
  5715. // Set new Offset...
  5716. cbNewOffset = (pStream->m_cbOffset + lOffset);
  5717. }
  5718. // STREAM_SEEK_END
  5719. else if (STREAM_SEEK_END == dwOrigin)
  5720. {
  5721. // Compute Size...from current offset
  5722. faBlock = pStream->m_faCurrent;
  5723. // Valid stream Block
  5724. IF_FAILEXIT(hr = _GetBlock(BLOCK_STREAM, faBlock, (LPVOID *)&pBlock));
  5725. // Validate
  5726. Assert(0 == pBlock->faNext ? TRUE : pBlock->cbData == pBlock->cbSize);
  5727. // Validation
  5728. Assert(pStream->m_cbCurrent <= pBlock->cbData && pStream->m_cbCurrent <= pBlock->cbSize);
  5729. // Set cbSize
  5730. cbSize = pStream->m_cbOffset + (pBlock->cbData - pStream->m_cbCurrent);
  5731. // Goto the next block
  5732. faBlock = pBlock->faNext;
  5733. // While
  5734. while (faBlock > 0)
  5735. {
  5736. // Valid stream Block
  5737. IF_FAILEXIT(hr = _GetBlock(BLOCK_STREAM, faBlock, (LPVOID *)&pBlock));
  5738. // Validate
  5739. Assert(0 == pBlock->faNext ? TRUE : pBlock->cbData == pBlock->cbSize);
  5740. // Increment cbSize
  5741. cbSize += pBlock->cbData;
  5742. // Set faBlock
  5743. faBlock = pBlock->faNext;
  5744. }
  5745. // If lOffset is negative and absolutely larger than the size of the stream
  5746. if (lOffset > 0 || (lOffset < 0 && (DWORD)(0 - lOffset) > cbSize))
  5747. {
  5748. hr = TraceResult(E_FAIL);
  5749. goto exit;
  5750. }
  5751. // Save new offset
  5752. cbNewOffset = cbSize + lOffset;
  5753. }
  5754. // STREAM_SEEK_SET
  5755. else
  5756. {
  5757. // Can't be negative
  5758. if (lOffset < 0)
  5759. {
  5760. hr = TraceResult(E_FAIL);
  5761. goto exit;
  5762. }
  5763. // Save new offset
  5764. cbNewOffset = lOffset;
  5765. }
  5766. // Did the offset change
  5767. if (cbNewOffset != pStream->m_cbOffset)
  5768. {
  5769. // Reset Current Position
  5770. pStream->m_faCurrent = pStream->m_faStart;
  5771. pStream->m_cbCurrent = 0;
  5772. pStream->m_iCurrent = 0;
  5773. pStream->m_cbOffset = 0;
  5774. // Initialize the loop
  5775. faBlock = pStream->m_faStart;
  5776. // Lets seek from the start of the stream to the new offset
  5777. while (faBlock > 0)
  5778. {
  5779. // Valid stream Block
  5780. IF_FAILEXIT(hr = _GetBlock(BLOCK_STREAM, faBlock, (LPVOID *)&pBlock));
  5781. // Validate
  5782. Assert(0 == pBlock->faNext ? TRUE : pBlock->cbData == pBlock->cbSize);
  5783. // Save some stuff
  5784. pStream->m_faCurrent = pBlock->faBlock;
  5785. // Is this the block we want ?
  5786. if (pStream->m_cbOffset + pBlock->cbData >= cbNewOffset)
  5787. {
  5788. // Compute m_cbCurrent
  5789. pStream->m_cbCurrent = (cbNewOffset - pStream->m_cbOffset);
  5790. // Set m_cbOffset
  5791. pStream->m_cbOffset += pStream->m_cbCurrent;
  5792. // Done
  5793. break;
  5794. }
  5795. // Set m_cbCurrent
  5796. pStream->m_cbCurrent = pBlock->cbData;
  5797. // Increment global offset
  5798. pStream->m_cbOffset += pBlock->cbData;
  5799. // Increment Index
  5800. pStream->m_iCurrent++;
  5801. // Goto Next
  5802. faBlock = pBlock->faNext;
  5803. }
  5804. }
  5805. // Return Position
  5806. if (pulNew)
  5807. pulNew->LowPart = pStream->m_cbOffset;
  5808. exit:
  5809. // Mutal Exclusion
  5810. Unlock(&hLock);
  5811. // Done
  5812. return(hr);
  5813. }
  5814. //--------------------------------------------------------------------------
  5815. // CDatabase::StreamGetAddress
  5816. //--------------------------------------------------------------------------
  5817. HRESULT CDatabase::StreamGetAddress(CDatabaseStream *pStream, LPFILEADDRESS pfaStart)
  5818. {
  5819. // Locals
  5820. HRESULT hr=S_OK;
  5821. HLOCK hLock=NULL;
  5822. // Trace
  5823. TraceCall("CDatabase::StreamGetAddress");
  5824. // Lock
  5825. IF_FAILEXIT(hr = Lock(&hLock));
  5826. // Invalid Args
  5827. Assert(pStream);
  5828. // StreamSychronize
  5829. IF_FAILEXIT(hr = _StreamSychronize(pStream));
  5830. // Return the Address
  5831. *pfaStart = pStream->m_faStart;
  5832. exit:
  5833. // Mutal Exclusion
  5834. Unlock(&hLock);
  5835. // Done
  5836. return(hr);
  5837. }
  5838. //--------------------------------------------------------------------------
  5839. // CDatabase::StreamRelease
  5840. //--------------------------------------------------------------------------
  5841. HRESULT CDatabase::StreamRelease(CDatabaseStream *pStream)
  5842. {
  5843. // Locals
  5844. HRESULT hr=S_OK;
  5845. LPOPENSTREAM pInfo;
  5846. HLOCK hLock=NULL;
  5847. // Trace
  5848. TraceCall("CDatabase::StreamRelease");
  5849. // Invalid Args
  5850. Assert(pStream);
  5851. // Lock
  5852. IF_FAILEXIT(hr = Lock(&hLock));
  5853. // Validate
  5854. Assert(m_pShare->rgStream);
  5855. // Cast iStream
  5856. pInfo = &m_pShare->rgStream[pStream->m_iStream];
  5857. // Better have a reference count
  5858. Assert(pInfo->cOpenCount > 0);
  5859. // Decrement cOpenCount
  5860. pInfo->cOpenCount--;
  5861. // Reset the Lock Count based on the access type
  5862. if (ACCESS_WRITE == pStream->m_tyAccess)
  5863. {
  5864. // Validate the lLock
  5865. Assert(LOCK_VALUE_WRITER == pInfo->lLock && 0 == pInfo->cOpenCount);
  5866. // Set to none
  5867. pInfo->lLock = LOCK_VALUE_NONE;
  5868. }
  5869. // Otherwise, must have been locked for a read
  5870. else
  5871. {
  5872. // Validate
  5873. Assert(ACCESS_READ == pStream->m_tyAccess && pInfo->lLock > 0);
  5874. // Validate Lock count
  5875. pInfo->lLock--;
  5876. }
  5877. // If this was the last reference...
  5878. if (0 == pInfo->cOpenCount)
  5879. {
  5880. // If the stream is marked for deletion
  5881. if (TRUE == pInfo->fDeleteOnClose)
  5882. {
  5883. // Validate Start Address
  5884. Assert(pInfo->faStart > 0);
  5885. // Free the Storage
  5886. IF_FAILEXIT(hr = _FreeStreamStorage(pInfo->faStart));
  5887. }
  5888. // Zero Out This Entry
  5889. ZeroMemory(pInfo, sizeof(OPENSTREAM));
  5890. }
  5891. exit:
  5892. // Mutal Exclusion
  5893. Unlock(&hLock);
  5894. // Done
  5895. return(hr);
  5896. }
  5897. //--------------------------------------------------------------------------
  5898. // CDatabase::_FreeStreamStorage
  5899. //--------------------------------------------------------------------------
  5900. HRESULT CDatabase::_FreeStreamStorage(FILEADDRESS faStart)
  5901. {
  5902. // Locals
  5903. HRESULT hr=S_OK;
  5904. LPSTREAMBLOCK pBlock;
  5905. FILEADDRESS faCurrent;
  5906. // Trace
  5907. TraceCall("CDatabase::_FreeStreamStorage");
  5908. // Invalid Args
  5909. Assert(faStart > 0);
  5910. // Initialize Loop
  5911. faCurrent = faStart;
  5912. // Read through all of the blocks (i.e. verify headers and count the number of chains)
  5913. while (faCurrent)
  5914. {
  5915. // Valid stream Block
  5916. IF_FAILEXIT(hr = _GetBlock(BLOCK_STREAM, faCurrent, (LPVOID *)&pBlock));
  5917. // Set faCurrent
  5918. faCurrent = pBlock->faNext;
  5919. // Read the header
  5920. IF_FAILEXIT(hr = _FreeBlock(BLOCK_STREAM, pBlock->faBlock));
  5921. }
  5922. exit:
  5923. // Done
  5924. return(hr);
  5925. }
  5926. //--------------------------------------------------------------------------
  5927. // CDatabase::CreateStream
  5928. //--------------------------------------------------------------------------
  5929. HRESULT CDatabase::CreateStream(LPFILEADDRESS pfaStream)
  5930. {
  5931. // Locals
  5932. HRESULT hr=S_OK;
  5933. FILEADDRESS faStart=0;
  5934. HLOCK hLock=NULL;
  5935. LPSTREAMBLOCK pStream;
  5936. // Trace
  5937. TraceCall("CDatabase::CreateStream");
  5938. // Invalid Arg
  5939. Assert(pfaStream);
  5940. // Initialize
  5941. *pfaStream = NULL;
  5942. // Lock
  5943. IF_FAILEXIT(hr = Lock(&hLock));
  5944. // Allocate the first 512 block of the stream
  5945. IF_FAILEXIT(hr = _AllocateBlock(BLOCK_STREAM, 0, (LPVOID *)&pStream));
  5946. // Write the Initialize Header
  5947. pStream->cbData = 0;
  5948. pStream->faNext = 0;
  5949. // Return the block
  5950. *pfaStream = pStream->faBlock;
  5951. // Modify the Version
  5952. m_pShare->dwVersion++;
  5953. exit:
  5954. // Thread Safety
  5955. Unlock(&hLock);
  5956. // Done
  5957. return(hr);
  5958. }
  5959. //--------------------------------------------------------------------------
  5960. // CDatabase::CopyStream
  5961. //--------------------------------------------------------------------------
  5962. STDMETHODIMP CDatabase::CopyStream(IDatabase *pDatabase, FILEADDRESS faStream,
  5963. LPFILEADDRESS pfaNew)
  5964. {
  5965. // Locals
  5966. HRESULT hr=S_OK;
  5967. FILEADDRESS faNew=0;
  5968. DWORD cbRead;
  5969. HLOCK hLock=NULL;
  5970. BYTE rgbBuffer[4096];
  5971. IStream *pStmDst=NULL;
  5972. IStream *pStmSrc=NULL;
  5973. // Trace
  5974. TraceCall("CDatabase::CopyStream");
  5975. // Invalid Arg
  5976. if (NULL == pDatabase || 0 == faStream || NULL == pfaNew)
  5977. return(TraceResult(E_INVALIDARG));
  5978. // Initialize
  5979. *pfaNew = NULL;
  5980. // Lock
  5981. IF_FAILEXIT(hr = Lock(&hLock));
  5982. // Allocate a Stream in the Destination Database
  5983. IF_FAILEXIT(hr = pDatabase->CreateStream(&faNew));
  5984. // Open It Dst
  5985. IF_FAILEXIT(hr = pDatabase->OpenStream(ACCESS_WRITE, faNew, &pStmDst));
  5986. // Open It Src
  5987. IF_FAILEXIT(hr = OpenStream(ACCESS_READ, faStream, &pStmSrc));
  5988. // Read and Write...
  5989. while (1)
  5990. {
  5991. // Read a Block From the Source
  5992. IF_FAILEXIT(hr = pStmSrc->Read(rgbBuffer, sizeof(rgbBuffer), &cbRead));
  5993. // Done
  5994. if (0 == cbRead)
  5995. break;
  5996. // Write It
  5997. IF_FAILEXIT(hr = pStmDst->Write(rgbBuffer, cbRead, NULL));
  5998. // Yield Compacting
  5999. if (m_pShare->fCompacting && m_fCompactYield)
  6000. {
  6001. // Give up a timeslice
  6002. Sleep(0);
  6003. }
  6004. }
  6005. // Comit the Dest
  6006. IF_FAILEXIT(hr = pStmDst->Commit(STGC_DEFAULT));
  6007. // Modify the Version
  6008. *pfaNew = faNew;
  6009. // Don't Free It
  6010. faNew = 0;
  6011. exit:
  6012. // Cleanup
  6013. SafeRelease(pStmDst);
  6014. SafeRelease(pStmSrc);
  6015. // Failure
  6016. if (faNew)
  6017. {
  6018. // Delete the Stream
  6019. SideAssert(SUCCEEDED(pDatabase->DeleteStream(faNew)));
  6020. }
  6021. // Thread Safety
  6022. Unlock(&hLock);
  6023. // Done
  6024. return(hr);
  6025. }
  6026. //--------------------------------------------------------------------------
  6027. // CDatabase::ChangeStreamLock
  6028. //--------------------------------------------------------------------------
  6029. STDMETHODIMP CDatabase::ChangeStreamLock(IStream *pStream, ACCESSTYPE tyAccessNew)
  6030. {
  6031. // Locals
  6032. HRESULT hr=S_OK;
  6033. HLOCK hLock=NULL;
  6034. LPOPENSTREAM pInfo;
  6035. CDatabaseStream *pDBStream=NULL;
  6036. // Trace
  6037. TraceCall("CDatabase::ChangeStreamLock");
  6038. // Lock
  6039. IF_FAILEXIT(hr = Lock(&hLock));
  6040. // Get Private Stream
  6041. IF_FAILEXIT(hr = pStream->QueryInterface(IID_CDatabaseStream, (LPVOID *)&pDBStream));
  6042. // Get Stream Info
  6043. pInfo = &m_pShare->rgStream[pDBStream->m_iStream];
  6044. // Going to Writer
  6045. if (ACCESS_WRITE == tyAccessNew)
  6046. {
  6047. // Already Locked for Write
  6048. if (LOCK_VALUE_WRITER == pInfo->lLock)
  6049. {
  6050. Assert(ACCESS_WRITE == pDBStream->m_tyAccess);
  6051. goto exit;
  6052. }
  6053. // If more than one reader
  6054. if (pInfo->lLock > 1)
  6055. {
  6056. hr = TraceResult(DB_E_LOCKEDFORREAD);
  6057. goto exit;
  6058. }
  6059. // Change Lock Type
  6060. pInfo->lLock = LOCK_VALUE_WRITER;
  6061. // Write Access
  6062. pDBStream->m_tyAccess = ACCESS_WRITE;
  6063. }
  6064. // Otherwise, change to read...
  6065. else
  6066. {
  6067. // Validate
  6068. Assert(ACCESS_READ == tyAccessNew);
  6069. // If already locked for read
  6070. if (LOCK_VALUE_WRITER != pInfo->lLock)
  6071. {
  6072. Assert(ACCESS_READ == pDBStream->m_tyAccess);
  6073. goto exit;
  6074. }
  6075. // Change to 1 reader
  6076. pInfo->lLock = 1;
  6077. // Read Access
  6078. pDBStream->m_tyAccess = ACCESS_READ;
  6079. }
  6080. exit:
  6081. // Mutal Exclusion
  6082. Unlock(&hLock);
  6083. // Cleanup
  6084. SafeRelease(pDBStream);
  6085. // Done
  6086. return(hr);
  6087. }
  6088. //--------------------------------------------------------------------------
  6089. // CDatabase::OpenStream
  6090. //--------------------------------------------------------------------------
  6091. HRESULT CDatabase::OpenStream(ACCESSTYPE tyAccess, FILEADDRESS faStart,
  6092. IStream **ppStream)
  6093. {
  6094. // Locals
  6095. HRESULT hr=S_OK;
  6096. STREAMINDEX i;
  6097. STREAMINDEX iStream=INVALID_STREAMINDEX;
  6098. STREAMINDEX iFirstUnused=INVALID_STREAMINDEX;
  6099. LPOPENSTREAM pInfo;
  6100. HLOCK hLock=NULL;
  6101. CDatabaseStream *pStream=NULL;
  6102. // Trace
  6103. TraceCall("CDatabase::OpenStream");
  6104. // Invalid Arg
  6105. if (0 == faStart || NULL == ppStream)
  6106. return TraceResult(E_INVALIDARG);
  6107. // Lock
  6108. IF_FAILEXIT(hr = Lock(&hLock));
  6109. // Validate
  6110. Assert(m_pShare->rgStream);
  6111. // Does the faStart Stream Exist in my stream table
  6112. for (i=0; i<CMAX_OPEN_STREAMS; i++)
  6113. {
  6114. // Is this the stream
  6115. if (faStart == m_pShare->rgStream[i].faStart)
  6116. {
  6117. // This must already be locked for write or read
  6118. Assert(LOCK_VALUE_WRITER == m_pShare->rgStream[i].lLock || m_pShare->rgStream[i].lLock > 0);
  6119. // Get Access
  6120. if (ACCESS_WRITE == tyAccess)
  6121. {
  6122. hr = TraceResult(DB_E_LOCKEDFORREAD);
  6123. goto exit;
  6124. }
  6125. // Otheriwise, get read lock
  6126. else
  6127. {
  6128. // If Locked for a write
  6129. if (LOCK_VALUE_WRITER == m_pShare->rgStream[i].lLock)
  6130. {
  6131. hr = TraceResult(DB_E_LOCKEDFORWRITE);
  6132. goto exit;
  6133. }
  6134. }
  6135. // Set iStream
  6136. iStream = i;
  6137. // Increment Open Count for this stream
  6138. m_pShare->rgStream[i].cOpenCount++;
  6139. // I Must have got a read lock
  6140. Assert(ACCESS_READ == tyAccess && m_pShare->rgStream[i].lLock > 0);
  6141. // Increment Reader Count
  6142. m_pShare->rgStream[i].lLock++;
  6143. }
  6144. // If this entry is unused, lets remember it
  6145. if (FALSE == m_pShare->rgStream[i].fInUse && INVALID_STREAMINDEX == iFirstUnused)
  6146. iFirstUnused = i;
  6147. }
  6148. // If we didn't find faStart in the stream table, append an entry ?
  6149. if (INVALID_STREAMINDEX == iStream)
  6150. {
  6151. // Is there enought space
  6152. if (INVALID_STREAMINDEX == iFirstUnused)
  6153. {
  6154. hr = TraceResult(DB_E_STREAMTABLEFULL);
  6155. goto exit;
  6156. }
  6157. // Set iStream
  6158. iStream = iFirstUnused;
  6159. // Readability
  6160. pInfo = &m_pShare->rgStream[iStream];
  6161. // This entry is now in use
  6162. pInfo->fInUse = TRUE;
  6163. // Register the starting address of this stream
  6164. pInfo->faStart = faStart;
  6165. // Reader or Writer ?
  6166. pInfo->lLock = (ACCESS_WRITE == tyAccess) ? LOCK_VALUE_WRITER : (m_pShare->rgStream[i].lLock + 1);
  6167. // Set Open count
  6168. pInfo->cOpenCount++;
  6169. }
  6170. // Allocate an Object Database Stream
  6171. IF_NULLEXIT(pStream = new CDatabaseStream(this, iStream, tyAccess, faStart));
  6172. // Return
  6173. *ppStream = (IStream *)pStream;
  6174. pStream = NULL;
  6175. exit:
  6176. // Mutal Exclusion
  6177. Unlock(&hLock);
  6178. // Cleanup
  6179. SafeRelease(pStream);
  6180. // Done
  6181. return(hr);
  6182. }
  6183. //--------------------------------------------------------------------------
  6184. // CDatabase::_CollapseChain
  6185. //--------------------------------------------------------------------------
  6186. HRESULT CDatabase::_CollapseChain(LPCHAINBLOCK pChain, NODEINDEX iDelete)
  6187. {
  6188. // Locals
  6189. HRESULT hr=S_OK;
  6190. NODEINDEX i;
  6191. // Trace
  6192. TraceCall("CDatabase::_CollapseChain");
  6193. // Simply set node[i] = node[i+1]; cNodes -= 1; Write the Chain !
  6194. for (i=iDelete; i<pChain->cNodes - 1; i++)
  6195. {
  6196. // Copy i + 1 chain node down one
  6197. CopyMemory(&pChain->rgNode[i], &pChain->rgNode[i + 1], sizeof(CHAINNODE));
  6198. // If there is a right chain
  6199. if (pChain->rgNode[i].faRightChain)
  6200. {
  6201. // Locals
  6202. LPCHAINBLOCK pRightChain;
  6203. // Get Right Chain block
  6204. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, pChain->rgNode[i].faRightChain, (LPVOID *)&pRightChain));
  6205. // Validate the current Parent
  6206. Assert(pRightChain->faParent == pChain->faBlock);
  6207. // Validate the current index
  6208. Assert(pRightChain->iParent == i + 1);
  6209. // Reset the Parent
  6210. pRightChain->iParent = i;
  6211. }
  6212. }
  6213. // Decrement count
  6214. pChain->cNodes--;
  6215. exit:
  6216. // Done
  6217. return(hr);
  6218. }
  6219. //--------------------------------------------------------------------------
  6220. // CDatabase::_IndexDeleteRecord
  6221. //--------------------------------------------------------------------------
  6222. HRESULT CDatabase::_IndexDeleteRecord(INDEXORDINAL iIndex,
  6223. FILEADDRESS faDelete, NODEINDEX iDelete)
  6224. {
  6225. // Locals
  6226. HRESULT hr=S_OK;
  6227. HRESULT hrIsLeafChain;
  6228. NODEINDEX i;
  6229. LPCHAINBLOCK pDelete;
  6230. CHAINSHARETYPE tyShare;
  6231. CHAINDELETETYPE tyDelete;
  6232. FILEADDRESS faShare;
  6233. LPCHAINBLOCK pSuccessor;
  6234. FILEADDRESS faRecord;
  6235. // Trace
  6236. TraceCall("CDatabase::_IndexDeleteRecord");
  6237. // Invalid Args
  6238. Assert(iDelete < BTREE_ORDER);
  6239. // Get Block
  6240. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faDelete, (LPVOID *)&pDelete));
  6241. // Is this a leaf node
  6242. hrIsLeafChain = _IsLeafChain(pDelete);
  6243. // Case 1: Deleting a leaf node that does not violate the minimum capcity constraint
  6244. if (S_OK == hrIsLeafChain && (pDelete->cNodes - 1 >= BTREE_MIN_CAP || 0 == pDelete->faParent))
  6245. {
  6246. // Collapse the Chain
  6247. _CollapseChain(pDelete, iDelete);
  6248. // Update the Parent Node Count
  6249. IF_FAILEXIT(hr = _AdjustParentNodeCount(iIndex, faDelete, -1));
  6250. // Did we just delete the root chain
  6251. if (0 == pDelete->faParent && 0 == pDelete->cNodes)
  6252. {
  6253. // Add pShare to free list
  6254. IF_FAILEXIT(hr = _FreeBlock(BLOCK_CHAIN, faDelete));
  6255. // Update the header, we don't have any more nodes
  6256. m_pHeader->rgfaIndex[iIndex] = 0;
  6257. }
  6258. }
  6259. // Case 2: Deleting from a nonleaf node and replacing that record with a record from a leaf node that does not violate the minimum capacity contstraint.
  6260. else if (S_FALSE == hrIsLeafChain)
  6261. {
  6262. // Get Inorder Successor
  6263. IF_FAILEXIT(hr = _GetInOrderSuccessor(faDelete, iDelete, &pSuccessor));
  6264. // Free Tree Block
  6265. faRecord = pDelete->rgNode[iDelete].faRecord;
  6266. // Free Tree Block
  6267. pDelete->rgNode[iDelete].faRecord = pSuccessor->rgNode[0].faRecord;
  6268. // Delete pSuccessor->rgNode[0] - and the record which we just replaced
  6269. pSuccessor->rgNode[0].faRecord = faRecord;
  6270. // Delete this node
  6271. IF_FAILEXIT(hr = _IndexDeleteRecord(iIndex, pSuccessor->faBlock, 0));
  6272. }
  6273. // Case 3: Deleting from a leaf node that causes a minimum capacity constraint violation that can be corrected by redistributing the records with an adjacent sibling node.
  6274. else
  6275. {
  6276. // Decide if I need to do a shared or coalesce type delete
  6277. IF_FAILEXIT(hr = _DecideHowToDelete(&faShare, faDelete, &tyDelete, &tyShare));
  6278. // Collapse the Chain
  6279. _CollapseChain(pDelete, iDelete);
  6280. // If NULL, then do a coalesc
  6281. if (CHAIN_DELETE_SHARE == tyDelete)
  6282. {
  6283. // Adjust the Parent's Parents
  6284. IF_FAILEXIT(hr = _AdjustParentNodeCount(iIndex, faDelete, -1));
  6285. // Do a shared deleted
  6286. IF_FAILEXIT(hr = _ChainDeleteShare(iIndex, faDelete, faShare, tyShare));
  6287. }
  6288. // Coalesce Type Delete
  6289. else
  6290. {
  6291. // Validate the delete type
  6292. Assert(faShare && CHAIN_DELETE_COALESCE == tyDelete && pDelete->faParent != 0);
  6293. // Adjust the Parent's Parents
  6294. IF_FAILEXIT(hr = _AdjustParentNodeCount(iIndex, pDelete->faParent, -1));
  6295. // Do a coalescing delete
  6296. IF_FAILEXIT(hr = _ChainDeleteCoalesce(iIndex, faDelete, faShare, tyShare));
  6297. }
  6298. }
  6299. exit:
  6300. // Done
  6301. return(hr);
  6302. }
  6303. //--------------------------------------------------------------------------
  6304. // CDatabase::_IsLeafChain
  6305. //--------------------------------------------------------------------------
  6306. HRESULT CDatabase::_IsLeafChain(LPCHAINBLOCK pChain)
  6307. {
  6308. // If Left Chain is NULL, then all chains must be null
  6309. return (0 == pChain->faLeftChain) ? S_OK : S_FALSE;
  6310. }
  6311. //--------------------------------------------------------------------------
  6312. // CDatabase::_ChainDeleteShare
  6313. //--------------------------------------------------------------------------
  6314. HRESULT CDatabase::_ChainDeleteShare(INDEXORDINAL iIndex,
  6315. FILEADDRESS faDelete, FILEADDRESS faShare, CHAINSHARETYPE tyShare)
  6316. {
  6317. // Locals
  6318. HRESULT hr=S_OK;
  6319. NODEINDEX i;
  6320. NODEINDEX iInsert;
  6321. NODEINDEX iParent;
  6322. FILEADDRESS faParentRightChain;
  6323. DWORD cParentRightNodes;
  6324. DWORD cCopyNodes;
  6325. LPCHAINBLOCK pDelete;
  6326. LPCHAINBLOCK pShare;
  6327. LPCHAINBLOCK pParent;
  6328. // Trace
  6329. TraceCall("CDatabase::_ChainDeleteShare");
  6330. // Invalid ARgs
  6331. Assert(faDelete && faShare);
  6332. // Get pShare
  6333. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faShare, (LPVOID *)&pShare));
  6334. // Get the Parent
  6335. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, pShare->faParent, (LPVOID *)&pParent));
  6336. // Get pDelete
  6337. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faDelete, (LPVOID *)&pDelete));
  6338. // Validation
  6339. Assert(pShare->faParent == pDelete->faParent);
  6340. // Setup iParent
  6341. iParent = (CHAIN_SHARE_LEFT == tyShare) ? pDelete->iParent : pShare->iParent;
  6342. // Save Paren't Right Chain, we are going to replace iParent with the last left or first right node
  6343. faParentRightChain = pParent->rgNode[iParent].faRightChain;
  6344. // Save the cParentRightNodes
  6345. cParentRightNodes = pParent->rgNode[iParent].cRightNodes;
  6346. // Insert Parent Node into lpChainFound - Parent Pointers stay the same
  6347. pParent->rgNode[iParent].faRightChain = 0;
  6348. // Reset cRightNodes
  6349. pParent->rgNode[iParent].cRightNodes = 0;
  6350. // Insert the parent node into the chain that we are deleting from
  6351. IF_FAILEXIT(hr = _ChainInsert(iIndex, pDelete, &pParent->rgNode[iParent], &iInsert));
  6352. // If promoting from the Left, promote Node: cNodes-1 to parent
  6353. if (CHAIN_SHARE_LEFT == tyShare)
  6354. {
  6355. // Should have inserted at position zero
  6356. Assert(0 == iInsert);
  6357. // Promote Node: 0 to from the deletion node into the parent node
  6358. pDelete->rgNode[0].faRightChain = pDelete->faLeftChain;
  6359. // Propagate cLeftNodes to cRightNodes
  6360. pDelete->rgNode[0].cRightNodes = pDelete->cLeftNodes;
  6361. // Update Left Chain Address
  6362. pDelete->faLeftChain = pShare->rgNode[pShare->cNodes - 1].faRightChain;
  6363. // Update the left chain's parent
  6364. if (pDelete->faLeftChain)
  6365. {
  6366. // Locals
  6367. LPCHAINBLOCK pLeftChain;
  6368. // Get Left
  6369. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, pDelete->faLeftChain, (LPVOID *)&pLeftChain));
  6370. // Set faParent
  6371. pLeftChain->faParent = pDelete->faBlock;
  6372. // Set iParent
  6373. pLeftChain->iParent = 0;
  6374. }
  6375. // Update Left Chain Node count
  6376. pDelete->cLeftNodes = pShare->rgNode[pShare->cNodes - 1].cRightNodes;
  6377. // Save cCopyNodes
  6378. cCopyNodes = pDelete->cLeftNodes + 1;
  6379. // Copy the node from the left share chain into the parent's spot
  6380. CopyMemory(&pParent->rgNode[iParent], &pShare->rgNode[pShare->cNodes - 1], sizeof(CHAINNODE));
  6381. // Reset the right chain on the parent
  6382. pParent->rgNode[iParent].faRightChain = faParentRightChain;
  6383. // Reset the right node count on the parent
  6384. pParent->rgNode[iParent].cRightNodes = cParentRightNodes;
  6385. // Decrement number of nodes in the share chain
  6386. pShare->cNodes--;
  6387. // Special case, pShare is to the left of the first node of the parent chain
  6388. if (0 == iParent)
  6389. {
  6390. // Can not be the left chain, otherwise, we wouldn't be sharing from the right
  6391. Assert(pShare->faBlock == pParent->faLeftChain && pParent->cLeftNodes > cCopyNodes);
  6392. // Decrement Right Node Count
  6393. pParent->cLeftNodes -= cCopyNodes;
  6394. // Increment
  6395. pParent->rgNode[0].cRightNodes += cCopyNodes;
  6396. }
  6397. // Otherwise, Decrement cRightNodes
  6398. else
  6399. {
  6400. // Validate share left chain
  6401. Assert(pShare->faBlock == pParent->rgNode[iParent - 1].faRightChain && pParent->rgNode[iParent - 1].cRightNodes > cCopyNodes);
  6402. // Decrement Right Nodes Count
  6403. pParent->rgNode[iParent - 1].cRightNodes -= cCopyNodes;
  6404. // Validate Right Chain
  6405. Assert(pParent->rgNode[iParent].faRightChain == pDelete->faBlock && iParent == pDelete->iParent && pDelete->iParent < pParent->cNodes);
  6406. // Increment Right Nodes Count
  6407. pParent->rgNode[iParent].cRightNodes += cCopyNodes;
  6408. }
  6409. }
  6410. // Otherwise, share from the right
  6411. else
  6412. {
  6413. // Verify the share type
  6414. Assert(CHAIN_SHARE_RIGHT == tyShare && pDelete->cNodes - 1 == iInsert);
  6415. // Promote Node: 0 to parent
  6416. pDelete->rgNode[pDelete->cNodes - 1].faRightChain = pShare->faLeftChain;
  6417. // Update the Right Chain's Parent
  6418. if (pDelete->rgNode[pDelete->cNodes - 1].faRightChain)
  6419. {
  6420. // Locals
  6421. LPCHAINBLOCK pRightChain;
  6422. // Get Right Chain
  6423. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, pDelete->rgNode[pDelete->cNodes - 1].faRightChain, (LPVOID *)&pRightChain));
  6424. // Set faParent
  6425. pRightChain->faParent = pDelete->faBlock;
  6426. // Set iParent
  6427. pRightChain->iParent = (pDelete->cNodes - 1);
  6428. }
  6429. // Set cRightNodes Count
  6430. pDelete->rgNode[pDelete->cNodes - 1].cRightNodes = pShare->cLeftNodes;
  6431. // Save cCopyNodes
  6432. cCopyNodes = pDelete->rgNode[pDelete->cNodes - 1].cRightNodes + 1;
  6433. // Tree Shift
  6434. pShare->faLeftChain = pShare->rgNode[0].faRightChain;
  6435. // Tree Shift
  6436. pShare->cLeftNodes = pShare->rgNode[0].cRightNodes;
  6437. // Copy the node from the share chain to the parent
  6438. CopyMemory(&pParent->rgNode[iParent], &pShare->rgNode[0], sizeof(CHAINNODE));
  6439. // Collapse this Chain
  6440. _CollapseChain(pShare, 0);
  6441. // Reset the right chain on the parent
  6442. pParent->rgNode[iParent].faRightChain = faParentRightChain;
  6443. // Reset the right node count on the parent
  6444. pParent->rgNode[iParent].cRightNodes = cParentRightNodes;
  6445. // Special case, pShare is to the left of the first node of the parent chain
  6446. if (0 == iParent)
  6447. {
  6448. // Can not be the left chain, otherwise, we wouldn't be sharing from the right
  6449. Assert(pParent->rgNode[0].faRightChain == pShare->faBlock && pParent->rgNode[0].cRightNodes > cCopyNodes);
  6450. // Decrement Right Node Count
  6451. pParent->rgNode[0].cRightNodes -= cCopyNodes;
  6452. // Validate
  6453. Assert(pParent->faLeftChain == pDelete->faBlock);
  6454. // Increment
  6455. pParent->cLeftNodes += cCopyNodes;
  6456. }
  6457. // Otherwise, Decrement cRightNodes
  6458. else
  6459. {
  6460. // Validate share left chain
  6461. Assert(pShare->faBlock == pParent->rgNode[iParent].faRightChain && pParent->rgNode[iParent].cRightNodes > 0);
  6462. // Decrement Right Node Count
  6463. pParent->rgNode[iParent].cRightNodes -= cCopyNodes;
  6464. // Validate
  6465. Assert(pParent->rgNode[iParent - 1].faRightChain == pDelete->faBlock);
  6466. // Increment Left Sibling
  6467. pParent->rgNode[iParent - 1].cRightNodes += cCopyNodes;
  6468. }
  6469. }
  6470. exit:
  6471. // Done
  6472. return(hr);
  6473. }
  6474. //--------------------------------------------------------------------------
  6475. // CDatabase::_ChainDeleteCoalesce
  6476. //--------------------------------------------------------------------------
  6477. HRESULT CDatabase::_ChainDeleteCoalesce(INDEXORDINAL iIndex,
  6478. FILEADDRESS faDelete, FILEADDRESS faShare, CHAINSHARETYPE tyShare)
  6479. {
  6480. // Locals
  6481. HRESULT hr=S_OK;
  6482. NODEINDEX i;
  6483. NODEINDEX iInsert;
  6484. NODEINDEX iParent;
  6485. LPCHAINBLOCK pParent;
  6486. LPCHAINBLOCK pDelete;
  6487. LPCHAINBLOCK pShare;
  6488. FILEADDRESS faShareAgain;
  6489. CHAINDELETETYPE tyDelete;
  6490. DWORD cRightNodes;
  6491. // Trace
  6492. TraceCall("CDatabase::_ChainDeleteCoalesce");
  6493. // Invalid ARgs
  6494. Assert(faDelete && faShare);
  6495. // Get pShare
  6496. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faShare, (LPVOID *)&pShare));
  6497. // Get the Parent
  6498. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, pShare->faParent, (LPVOID *)&pParent));
  6499. // Get pDelete
  6500. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faDelete, (LPVOID *)&pDelete));
  6501. // Validation
  6502. Assert(pShare->faParent == pDelete->faParent);
  6503. // Setup iParent
  6504. iParent = (CHAIN_SHARE_LEFT == tyShare) ? pDelete->iParent : pShare->iParent;
  6505. // Insert the Parent
  6506. IF_FAILEXIT(hr = _ChainInsert(iIndex, pDelete, &pParent->rgNode[iParent], &iInsert));
  6507. // Set newly inserted nodes pointers
  6508. if (CHAIN_SHARE_LEFT == tyShare)
  6509. {
  6510. // Validate
  6511. Assert(0 == iInsert);
  6512. // Adjust the right Chain
  6513. pDelete->rgNode[0].faRightChain = pDelete->faLeftChain;
  6514. // Adjust the right node count
  6515. pDelete->rgNode[0].cRightNodes = pDelete->cLeftNodes;
  6516. // Adjust the left chain
  6517. pDelete->faLeftChain = pShare->faLeftChain;
  6518. // Update faLeftChain
  6519. if (pDelete->faLeftChain)
  6520. {
  6521. // Locals
  6522. LPCHAINBLOCK pLeftChain;
  6523. // Get Block
  6524. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, pDelete->faLeftChain, (LPVOID *)&pLeftChain));
  6525. // Set faParent
  6526. pLeftChain->faParent = pDelete->faBlock;
  6527. // Set iParent
  6528. pLeftChain->iParent = 0;
  6529. }
  6530. // Adjust the left chain node count
  6531. pDelete->cLeftNodes = pShare->cLeftNodes;
  6532. }
  6533. // Share from the right
  6534. else
  6535. {
  6536. // Verify Share Type
  6537. Assert(CHAIN_SHARE_RIGHT == tyShare && pDelete->cNodes - 1 == iInsert);
  6538. // Adjust the right chain
  6539. pDelete->rgNode[pDelete->cNodes - 1].faRightChain = pShare->faLeftChain;
  6540. // Update the Right Chain's Parent
  6541. if (pDelete->rgNode[pDelete->cNodes - 1].faRightChain)
  6542. {
  6543. // Locals
  6544. LPCHAINBLOCK pRightChain;
  6545. // Get the right chain
  6546. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, pDelete->rgNode[pDelete->cNodes - 1].faRightChain, (LPVOID *)&pRightChain));
  6547. // Set faParent
  6548. pRightChain->faParent = pDelete->faBlock;
  6549. // Set iParent
  6550. pRightChain->iParent = (pDelete->cNodes - 1);
  6551. }
  6552. // Adjust the right Node Count
  6553. pDelete->rgNode[pDelete->cNodes - 1].cRightNodes = pShare->cLeftNodes;
  6554. }
  6555. // Combine pShare Nodes into pDelete
  6556. for (i=0; i<pShare->cNodes; i++)
  6557. {
  6558. // Insert the Share
  6559. IF_FAILEXIT(hr = _ChainInsert(iIndex, pDelete, &pShare->rgNode[i], &iInsert));
  6560. // Need to update...
  6561. if (pDelete->rgNode[iInsert].faRightChain)
  6562. {
  6563. // Locals
  6564. LPCHAINBLOCK pRightChain;
  6565. // Get Right Chain
  6566. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, pDelete->rgNode[iInsert].faRightChain, (LPVOID *)&pRightChain));
  6567. // Set faParent
  6568. pRightChain->faParent = pDelete->faBlock;
  6569. // Set iParent
  6570. pRightChain->iParent = iInsert;
  6571. }
  6572. }
  6573. // Don't use pShare any more
  6574. pShare = NULL;
  6575. // We can't possible need pShare anymore since we just copied all of its nodes into pDelete
  6576. IF_FAILEXIT(hr = _FreeBlock(BLOCK_CHAIN, faShare));
  6577. // Collapse the Parent chain
  6578. _CollapseChain(pParent, iParent);
  6579. // If Parent is less than zero, then lets hope that it was the root node!
  6580. if (pParent->cNodes == 0)
  6581. {
  6582. // This is a bug
  6583. Assert(0 == pParent->faParent && m_pHeader->rgfaIndex[iIndex] == pParent->faBlock);
  6584. // Add pParent to free list
  6585. IF_FAILEXIT(hr = _FreeBlock(BLOCK_CHAIN, pParent->faBlock));
  6586. // Kill faParent Link
  6587. pDelete->faParent = pDelete->iParent = 0;
  6588. // We have a new root chain
  6589. m_pHeader->rgfaIndex[iIndex] = pDelete->faBlock;
  6590. // No more parent
  6591. goto exit;
  6592. }
  6593. // Compute cRightNodes
  6594. cRightNodes = pDelete->cNodes + pDelete->cLeftNodes;
  6595. // Loop and count all children
  6596. for (i=0; i<pDelete->cNodes; i++)
  6597. {
  6598. // Increment Node Count
  6599. cRightNodes += pDelete->rgNode[i].cRightNodes;
  6600. }
  6601. // Reset new parent to found node
  6602. if (CHAIN_SHARE_LEFT == tyShare)
  6603. {
  6604. // Readjust new parent node of coalesced chain
  6605. if (iParent > pParent->cNodes - 1)
  6606. {
  6607. // What is happening here
  6608. iParent = pParent->cNodes - 1;
  6609. }
  6610. // We should be replace pShare
  6611. Assert(pParent->rgNode[iParent].faRightChain == faShare);
  6612. // Update Parent for pDelete
  6613. pParent->rgNode[iParent].faRightChain = pDelete->faBlock;
  6614. // Adjust Right Chain's Parent
  6615. if (pParent->rgNode[iParent].faRightChain)
  6616. {
  6617. // Locals
  6618. LPCHAINBLOCK pRightChain;
  6619. // Get Right Chain
  6620. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, pParent->rgNode[iParent].faRightChain, (LPVOID *)&pRightChain));
  6621. // Set faParent
  6622. Assert(pRightChain->faParent == pParent->faBlock);
  6623. // Set the Parent Index
  6624. pRightChain->iParent = iParent;
  6625. }
  6626. // Compute cRightNodes
  6627. pParent->rgNode[iParent].cRightNodes = cRightNodes;
  6628. }
  6629. // Otherwise, adjust for CHAIN_SHARE_RIGHT
  6630. else
  6631. {
  6632. // Validation
  6633. Assert(pDelete->faParent == pParent->faBlock);
  6634. // First Node
  6635. if (0 == iParent)
  6636. {
  6637. // Must be left chain
  6638. Assert(pParent->faLeftChain == pDelete->faBlock);
  6639. // Validate my Parent
  6640. Assert(pDelete->iParent == 0);
  6641. // Set Left Node Count
  6642. pParent->cLeftNodes = cRightNodes;
  6643. }
  6644. // Otherwise
  6645. else
  6646. {
  6647. // Validate iParent
  6648. Assert(pParent->rgNode[iParent - 1].faRightChain == pDelete->faBlock);
  6649. // Validation
  6650. Assert(pDelete->iParent == iParent - 1);
  6651. // Set cRightNodes
  6652. pParent->rgNode[iParent - 1].cRightNodes = cRightNodes;
  6653. }
  6654. }
  6655. // Move up the chain, until lpChainPrev == NULL, lpChainPrev->cNodes can not be less than /2
  6656. if (0 == pParent->faParent)
  6657. goto exit;
  6658. // Min capacity
  6659. if (pParent->cNodes < BTREE_MIN_CAP)
  6660. {
  6661. // Decide if I need to do a shared or coalesce type delete
  6662. IF_FAILEXIT(hr = _DecideHowToDelete(&faShareAgain, pParent->faBlock, &tyDelete, &tyShare));
  6663. // Can't Share, we must coalesc again
  6664. if (CHAIN_DELETE_SHARE == tyDelete)
  6665. {
  6666. // Do a shared delete
  6667. IF_FAILEXIT(hr = _ChainDeleteShare(iIndex, pParent->faBlock, faShareAgain, tyShare));
  6668. }
  6669. // Coalesce type delete
  6670. else
  6671. {
  6672. // Validate
  6673. Assert(faShareAgain && CHAIN_DELETE_COALESCE == tyDelete);
  6674. // Recursive Coalescing
  6675. IF_FAILEXIT(hr = _ChainDeleteCoalesce(iIndex, pParent->faBlock, faShareAgain, tyShare));
  6676. }
  6677. }
  6678. exit:
  6679. // Done
  6680. return(hr);
  6681. }
  6682. //--------------------------------------------------------------------------
  6683. // CDatabase::_DecideHowToDelete
  6684. //--------------------------------------------------------------------------
  6685. HRESULT CDatabase::_DecideHowToDelete(LPFILEADDRESS pfaShare,
  6686. FILEADDRESS faDelete, CHAINDELETETYPE *ptyDelete,
  6687. CHAINSHARETYPE *ptyShare)
  6688. {
  6689. // Locals
  6690. HRESULT hr=S_OK;
  6691. HRESULT hrRight;
  6692. HRESULT hrLeft;
  6693. LPCHAINBLOCK pRight=NULL;
  6694. LPCHAINBLOCK pLeft=NULL;
  6695. // Trace
  6696. TraceCall("CDatabase::_DecideHowToDelete");
  6697. // Initialize
  6698. *pfaShare = NULL;
  6699. // Get the right sibling
  6700. IF_FAILEXIT(hr = _GetRightSibling(faDelete, &pRight));
  6701. // Set hrRight
  6702. hrRight = hr;
  6703. // Did we get a right parent that has nodes that I can steal from ?
  6704. if (DB_S_FOUND == hrRight && pRight->cNodes - 1 >= BTREE_MIN_CAP)
  6705. {
  6706. // Set Delete Type
  6707. *ptyDelete = CHAIN_DELETE_SHARE;
  6708. // Set Share Type
  6709. *ptyShare = CHAIN_SHARE_RIGHT;
  6710. // Set Share Link
  6711. *pfaShare = pRight->faBlock;
  6712. }
  6713. else
  6714. {
  6715. // Try to get the left sibling
  6716. IF_FAILEXIT(hr = _GetLeftSibling(faDelete, &pLeft));
  6717. // Set hrRight
  6718. hrLeft = hr;
  6719. // Did I get a left sibling that has nodes that I can steal from ?
  6720. if (DB_S_FOUND == hrLeft && pLeft->cNodes - 1 >= BTREE_MIN_CAP)
  6721. {
  6722. // Set Delete Type
  6723. *ptyDelete = CHAIN_DELETE_SHARE;
  6724. // Set Share Type
  6725. *ptyShare = CHAIN_SHARE_LEFT;
  6726. // Set Share Link
  6727. *pfaShare = pLeft->faBlock;
  6728. }
  6729. }
  6730. // Did we find a Share ?
  6731. if (0 == *pfaShare)
  6732. {
  6733. // Were are going to coalesce
  6734. *ptyDelete = CHAIN_DELETE_COALESCE;
  6735. // Coalesce and share from the right?
  6736. if (DB_S_FOUND == hrRight)
  6737. {
  6738. // Set Share Type
  6739. *ptyShare = CHAIN_SHARE_RIGHT;
  6740. // Set Share Link
  6741. *pfaShare = pRight->faBlock;
  6742. }
  6743. // Coalesce and share from the left?
  6744. else
  6745. {
  6746. // Validation
  6747. Assert(DB_S_FOUND == hrLeft);
  6748. // Set Share Type
  6749. *ptyShare = CHAIN_SHARE_LEFT;
  6750. // Set Share Link
  6751. *pfaShare = pLeft->faBlock;
  6752. }
  6753. }
  6754. exit:
  6755. // Done
  6756. return(hr);
  6757. }
  6758. //--------------------------------------------------------------------------
  6759. // CDatabase::_GetInOrderSuccessor
  6760. //--------------------------------------------------------------------------
  6761. HRESULT CDatabase::_GetInOrderSuccessor(FILEADDRESS faStart,
  6762. NODEINDEX iDelete, LPCHAINBLOCK *ppSuccessor)
  6763. {
  6764. // Locals
  6765. HRESULT hr=S_OK;
  6766. FILEADDRESS faCurrent;
  6767. LPCHAINBLOCK pCurrent;
  6768. LPCHAINBLOCK pStart;
  6769. // Trace
  6770. TraceCall("CDatabase::_GetInOrderSuccessor");
  6771. // Invalid Args
  6772. Assert(ppSuccessor);
  6773. // Initialize
  6774. *ppSuccessor = NULL;
  6775. // Get Start
  6776. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faStart, (LPVOID *)&pStart));
  6777. // Next Chain Address
  6778. faCurrent = pStart->rgNode[iDelete].faRightChain;
  6779. // Can't be zero
  6780. Assert(faCurrent != 0);
  6781. // Go until left chain is -1
  6782. while (faCurrent)
  6783. {
  6784. // Get Current
  6785. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faCurrent, (LPVOID *)&pCurrent));
  6786. // If leaf node, then return
  6787. if (S_OK == _IsLeafChain(pCurrent))
  6788. {
  6789. // Set Successor
  6790. *ppSuccessor = pCurrent;
  6791. // Done
  6792. goto exit;
  6793. }
  6794. // Otherwise, goto the left
  6795. faCurrent = pCurrent->faLeftChain;
  6796. }
  6797. // Not Found
  6798. hr = TraceResult(E_FAIL);
  6799. exit:
  6800. // Done
  6801. return(hr);
  6802. }
  6803. //--------------------------------------------------------------------------
  6804. // CDatabase::_GetLeftSibling
  6805. //--------------------------------------------------------------------------
  6806. HRESULT CDatabase::_GetLeftSibling(FILEADDRESS faCurrent,
  6807. LPCHAINBLOCK *ppSibling)
  6808. {
  6809. // Locals
  6810. HRESULT hr=S_OK;
  6811. LPCHAINBLOCK pCurrent;
  6812. LPCHAINBLOCK pParent;
  6813. // Trace
  6814. TraceCall("CDatabase::_GetLeftSibling");
  6815. // Invalid Args
  6816. Assert(faCurrent && ppSibling);
  6817. // Get Current
  6818. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faCurrent, (LPVOID *)&pCurrent));
  6819. // Get Parent
  6820. Assert(pCurrent->faParent);
  6821. // Get the Parent
  6822. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, pCurrent->faParent, (LPVOID *)&pParent));
  6823. // Validate iparent
  6824. Assert(pCurrent->iParent < pParent->cNodes);
  6825. // iParent is zero
  6826. if (0 == pCurrent->iParent)
  6827. {
  6828. // If pCurrent is the faRightChain ?
  6829. if (pCurrent->faBlock != pParent->rgNode[0].faRightChain)
  6830. return(DB_S_NOTFOUND);
  6831. // Get the Sibling
  6832. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, pParent->faLeftChain, (LPVOID *)ppSibling));
  6833. // Validate
  6834. Assert((*ppSibling)->iParent == 0);
  6835. }
  6836. // iParent is greater than zero ?
  6837. else
  6838. {
  6839. // Validate
  6840. Assert(pParent->rgNode[pCurrent->iParent].faRightChain == pCurrent->faBlock);
  6841. // Get the Sibling
  6842. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, pParent->rgNode[pCurrent->iParent - 1].faRightChain, (LPVOID *)ppSibling));
  6843. // Validate
  6844. Assert((*ppSibling)->iParent == pCurrent->iParent - 1);
  6845. }
  6846. // Better have a left sibling
  6847. Assert(0 != *ppSibling);
  6848. // Found
  6849. hr = DB_S_FOUND;
  6850. exit:
  6851. // Set hr
  6852. return(hr);
  6853. }
  6854. //--------------------------------------------------------------------------
  6855. // CDatabase::_GetRightSibling
  6856. //--------------------------------------------------------------------------
  6857. HRESULT CDatabase::_GetRightSibling(FILEADDRESS faCurrent,
  6858. LPCHAINBLOCK *ppSibling)
  6859. {
  6860. // Locals
  6861. HRESULT hr=S_OK;
  6862. LPCHAINBLOCK pParent;
  6863. LPCHAINBLOCK pCurrent;
  6864. // Trace
  6865. TraceCall("CDatabase::_GetRightSibling");
  6866. // Invalid Args
  6867. Assert(faCurrent && ppSibling);
  6868. // Get Current
  6869. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faCurrent, (LPVOID *)&pCurrent));
  6870. // Get Parent
  6871. Assert(pCurrent->faParent);
  6872. // Get the Parent
  6873. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, pCurrent->faParent, (LPVOID *)&pParent));
  6874. // Validate iparent
  6875. Assert(pCurrent->iParent < pParent->cNodes);
  6876. // iParent is zero
  6877. if (0 == pCurrent->iParent && pCurrent->faBlock == pParent->faLeftChain)
  6878. {
  6879. // Get the Sibling
  6880. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, pParent->rgNode[0].faRightChain, (LPVOID *)ppSibling));
  6881. // Validate
  6882. Assert((*ppSibling)->iParent == 0);
  6883. }
  6884. // iParent is greater than zero ?
  6885. else
  6886. {
  6887. // No more Right chains
  6888. if (pCurrent->iParent + 1 == pParent->cNodes)
  6889. return DB_S_NOTFOUND;
  6890. // Get the Sibling
  6891. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, pParent->rgNode[pCurrent->iParent + 1].faRightChain, (LPVOID *)ppSibling));
  6892. // Validate
  6893. Assert((*ppSibling)->iParent == pCurrent->iParent + 1);
  6894. }
  6895. // Better have a left sibling
  6896. Assert(0 != *ppSibling);
  6897. // Found
  6898. hr = DB_S_FOUND;
  6899. exit:
  6900. // Done
  6901. return(hr);
  6902. }
  6903. //--------------------------------------------------------------------------
  6904. // CDatabase::GetUserData
  6905. //--------------------------------------------------------------------------
  6906. HRESULT CDatabase::GetUserData(LPVOID pvUserData,
  6907. ULONG cbUserData)
  6908. {
  6909. // Locals
  6910. HRESULT hr=S_OK;
  6911. HLOCK hLock=NULL;
  6912. // Trace
  6913. TraceCall("CDatabase::GetUserData");
  6914. // Invalid Args
  6915. Assert(pvUserData);
  6916. // Lock
  6917. IF_FAILEXIT(hr = Lock(&hLock));
  6918. // Copy the data
  6919. CopyMemory(pvUserData, PUSERDATA(m_pHeader), cbUserData);
  6920. exit:
  6921. // Mutal Exclusion
  6922. Unlock(&hLock);
  6923. // Done
  6924. return(hr);
  6925. }
  6926. //--------------------------------------------------------------------------
  6927. // CDatabase::SetUserData
  6928. //--------------------------------------------------------------------------
  6929. HRESULT CDatabase::SetUserData(LPVOID pvUserData,
  6930. ULONG cbUserData)
  6931. {
  6932. // Locals
  6933. HRESULT hr=S_OK;
  6934. HLOCK hLock=NULL;
  6935. // Trace
  6936. TraceCall("CDatabase::SetUserData");
  6937. // Invalid Args
  6938. Assert(pvUserData);
  6939. // Lock
  6940. IF_FAILEXIT(hr = Lock(&hLock));
  6941. // Copy the data
  6942. CopyMemory(PUSERDATA(m_pHeader), pvUserData, cbUserData);
  6943. exit:
  6944. // Mutal Exclusion
  6945. Unlock(&hLock);
  6946. // Done
  6947. return(hr);
  6948. }
  6949. //--------------------------------------------------------------------------
  6950. // CDatabase::_CompactMoveRecordStreams
  6951. //--------------------------------------------------------------------------
  6952. HRESULT CDatabase::_CompactMoveRecordStreams(CDatabase *pDstDB,
  6953. LPVOID pBinding)
  6954. {
  6955. // Locals
  6956. HRESULT hr=S_OK;
  6957. COLUMNORDINAL iColumn;
  6958. FILEADDRESS faSrcStart;
  6959. FILEADDRESS faDstStart;
  6960. LPOPENSTREAM pInfo;
  6961. DWORD i;
  6962. // Trace
  6963. TraceCall("CDatabase::_CompactMoveRecordStreams");
  6964. // Walk through the format
  6965. for (iColumn=0; iColumn<m_pSchema->cColumns; iColumn++)
  6966. {
  6967. // Is this a stream
  6968. if (CDT_STREAM != m_pSchema->prgColumn[iColumn].type)
  6969. continue;
  6970. // Get the source stream starting address
  6971. faSrcStart = *((FILEADDRESS *)((LPBYTE)pBinding + m_pSchema->prgColumn[iColumn].ofBinding));
  6972. // Is there a stream
  6973. if (0 == faSrcStart)
  6974. continue;
  6975. // Move the Stream
  6976. IF_FAILEXIT(hr = CopyStream((IDatabase *)pDstDB, faSrcStart, &faDstStart));
  6977. // Store the stream address in the record
  6978. *((FILEADDRESS *)((LPBYTE)pBinding + m_pSchema->prgColumn[iColumn].ofBinding)) = faDstStart;
  6979. // Loop through the stream table and adjust the start address of all open streams
  6980. for (i=0; i<CMAX_OPEN_STREAMS; i++)
  6981. {
  6982. // Readability
  6983. pInfo = &m_pShare->rgStream[i];
  6984. // Is In use...
  6985. if (TRUE == pInfo->fInUse && faSrcStart == pInfo->faStart)
  6986. {
  6987. // Change the Address
  6988. pInfo->faMoved = faDstStart;
  6989. // Break;
  6990. break;
  6991. }
  6992. }
  6993. }
  6994. exit:
  6995. // Done
  6996. return(hr);
  6997. }
  6998. //--------------------------------------------------------------------------
  6999. // CDatabase::_CompactMoveOpenDeletedStreams
  7000. //--------------------------------------------------------------------------
  7001. HRESULT CDatabase::_CompactMoveOpenDeletedStreams(CDatabase *pDstDB)
  7002. {
  7003. // Locals
  7004. HRESULT hr=S_OK;
  7005. DWORD i;
  7006. LPOPENSTREAM pInfo;
  7007. // Trace
  7008. TraceCall("CDatabase::_CompactMoveOpenDeletedStreams");
  7009. // Loop through the stream table and adjust the start address of all open streams
  7010. for (i=0; i<CMAX_OPEN_STREAMS; i++)
  7011. {
  7012. // Readability
  7013. pInfo = &m_pShare->rgStream[i];
  7014. // Is In use...
  7015. if (FALSE == pInfo->fInUse || FALSE == pInfo->fDeleteOnClose)
  7016. continue;
  7017. // Move the Stream
  7018. IF_FAILEXIT(hr = CopyStream((IDatabase *)pDstDB, pInfo->faStart, &pInfo->faMoved));
  7019. }
  7020. exit:
  7021. // Done
  7022. return(hr);
  7023. }
  7024. //--------------------------------------------------------------------------
  7025. // CDatabase::_CompactTransferFilters
  7026. //--------------------------------------------------------------------------
  7027. HRESULT CDatabase::_CompactTransferFilters(CDatabase *pDstDB)
  7028. {
  7029. // Locals
  7030. HRESULT hr=S_OK;
  7031. DWORD i;
  7032. LPBLOCKHEADER pStringSrc;
  7033. LPBLOCKHEADER pStringDst;
  7034. // Trace
  7035. TraceCall("CDatabase::_CompactTransferFilters");
  7036. // Must have a Catalog
  7037. Assert(pDstDB->m_pHeader);
  7038. // Zero Out the Query String Addresses
  7039. for (i=0; i<CMAX_INDEXES; i++)
  7040. {
  7041. // Zero Filter1
  7042. pDstDB->m_pHeader->rgfaFilter[i] = 0;
  7043. // Copy Filter1
  7044. if (m_pHeader->rgfaFilter[i] && SUCCEEDED(_GetBlock(BLOCK_STRING, m_pHeader->rgfaFilter[i], (LPVOID *)&pStringSrc)))
  7045. {
  7046. // Try to Store the Query String
  7047. IF_FAILEXIT(hr = pDstDB->_AllocateBlock(BLOCK_STRING, pStringSrc->cbSize, (LPVOID *)&pStringDst));
  7048. // Write the String
  7049. CopyMemory(PSTRING(pStringDst), PSTRING(pStringSrc), pStringSrc->cbSize);
  7050. // String the String Address
  7051. pDstDB->m_pHeader->rgfaFilter[i] = pStringDst->faBlock;
  7052. }
  7053. }
  7054. // Change the Version so that it doesn't assert
  7055. pDstDB->m_dwQueryVersion = 0xffffffff;
  7056. // Rebuild the Query Table
  7057. IF_FAILEXIT(hr = pDstDB->_BuildQueryTable());
  7058. exit:
  7059. // Done
  7060. return(hr);
  7061. }
  7062. //--------------------------------------------------------------------------
  7063. // CDatabase::_CompactInsertRecord
  7064. //--------------------------------------------------------------------------
  7065. HRESULT CDatabase::_CompactInsertRecord(LPVOID pBinding)
  7066. {
  7067. // Locals
  7068. HRESULT hr=S_OK;
  7069. FINDRESULT rgResult[CMAX_INDEXES];
  7070. INDEXORDINAL iIndex;
  7071. DWORD i;
  7072. RECORDMAP RecordMap;
  7073. FILEADDRESS faRecord;
  7074. // Trace
  7075. TraceCall("CDatabase::InsertRecord");
  7076. // Invalid Args
  7077. Assert(pBinding);
  7078. // Loop through all the indexes
  7079. for (i = 0; i < m_pHeader->cIndexes; i++)
  7080. {
  7081. // Get Index Ordinal
  7082. iIndex = m_pHeader->rgiIndex[i];
  7083. // Otherwise: Decide Where to insert
  7084. IF_FAILEXIT(hr = _FindRecord(iIndex, COLUMNS_ALL, pBinding, &rgResult[i].faChain, &rgResult[i].iNode, NULL, &rgResult[i].nCompare));
  7085. // If key already exist, cache list and return
  7086. if (DB_S_FOUND == hr)
  7087. {
  7088. hr = TraceResult(DB_E_DUPLICATE);
  7089. goto exit;
  7090. }
  7091. }
  7092. // Get the Record Size
  7093. IF_FAILEXIT(hr = _GetRecordSize(pBinding, &RecordMap));
  7094. // Link Record Into the Table
  7095. IF_FAILEXIT(hr = _LinkRecordIntoTable(&RecordMap, pBinding, 0, &faRecord));
  7096. // Version Change
  7097. m_pShare->dwVersion++;
  7098. // Insert into the indexes
  7099. for (i = 0; i < m_pHeader->cIndexes; i++)
  7100. {
  7101. // Get Index Ordinal
  7102. iIndex = m_pHeader->rgiIndex[i];
  7103. // Visible in live index
  7104. if (S_OK == _IsVisible(m_rghFilter[iIndex], pBinding))
  7105. {
  7106. // Do the Insertion
  7107. IF_FAILEXIT(hr = _IndexInsertRecord(iIndex, rgResult[i].faChain, faRecord, &rgResult[i].iNode, rgResult[i].nCompare));
  7108. // Update Record Count
  7109. m_pHeader->rgcRecords[iIndex]++;
  7110. }
  7111. }
  7112. exit:
  7113. // Done
  7114. return(hr);
  7115. }
  7116. //--------------------------------------------------------------------------
  7117. // CDatabase::MoveFile
  7118. //--------------------------------------------------------------------------
  7119. STDMETHODIMP CDatabase::MoveFile(LPCWSTR pszFile)
  7120. {
  7121. // Locals
  7122. HRESULT hr=S_OK;
  7123. HLOCK hLock=NULL;
  7124. LPWSTR pszFilePath=NULL;
  7125. LPWSTR pszShare=NULL;
  7126. DWORD cchFilePath;
  7127. HANDLE hMutex=NULL;
  7128. BOOL fNeedOpenFile=FALSE;
  7129. BOOL fNewShare;
  7130. SHAREDDATABASE Share;
  7131. // Trace
  7132. TraceCall("CDatabase::MoveFile");
  7133. // Lock
  7134. IF_FAILEXIT(hr = Lock(&hLock));
  7135. // In move File
  7136. m_fInMoveFile = TRUE;
  7137. // Get the Full Path
  7138. IF_FAILEXIT(hr = DBGetFullPath(pszFile, &pszFilePath, &cchFilePath));
  7139. // Don't use pszFile again
  7140. pszFile = NULL;
  7141. // Failure
  7142. if (cchFilePath >= CCHMAX_DB_FILEPATH)
  7143. {
  7144. hr = TraceResult(E_INVALIDARG);
  7145. goto exit;
  7146. }
  7147. // Do It
  7148. IF_FAILEXIT(hr = _DispatchInvoke(INVOKE_CLOSEFILE));
  7149. // Need a remap..
  7150. fNeedOpenFile = TRUE;
  7151. // Move the file from the temp location to my current location
  7152. if (0 == MoveFileWrapW(m_pShare->szFile, pszFilePath))
  7153. {
  7154. hr = TraceResult(DB_E_MOVEFILE);
  7155. goto exit;
  7156. }
  7157. // Save the new file path...(other clients will remap to this file...)
  7158. StrCpyNW(m_pShare->szFile, pszFilePath, ARRAYSIZE(m_pShare->szFile));
  7159. // Save the Current Share
  7160. CopyMemory(&Share, m_pShare, sizeof(SHAREDDATABASE));
  7161. // Save Current Mutex
  7162. hMutex = m_hMutex;
  7163. // Clear m_hMutex so that we don't free it
  7164. m_hMutex = NULL;
  7165. // Create the Mutex Object
  7166. IF_FAILEXIT(hr = CreateSystemHandleName(pszFilePath, L"_DirectDBShare", &pszShare));
  7167. // Unmap the view of the memory mapped file
  7168. SafeUnmapViewOfFile(m_pShare);
  7169. // Unmap the view of the memory mapped file
  7170. SafeCloseHandle(m_pStorage->hShare);
  7171. // Open the file mapping
  7172. IF_FAILEXIT(hr = DBOpenFileMapping(INVALID_HANDLE_VALUE, pszShare, sizeof(SHAREDDATABASE), &fNewShare, &m_pStorage->hShare, (LPVOID *)&m_pShare));
  7173. // Should be new
  7174. Assert(fNewShare);
  7175. // Save the Current Share
  7176. CopyMemory(m_pShare, &Share, sizeof(SHAREDDATABASE));
  7177. // Get all clients to re-open the new file
  7178. IF_FAILEXIT(hr = _DispatchInvoke(INVOKE_OPENMOVEDFILE));
  7179. // Validate Mutex Changed
  7180. Assert(m_hMutex && hMutex != m_hMutex);
  7181. // Enter New Mutex
  7182. WaitForSingleObject(m_hMutex, INFINITE);
  7183. // Fix hLock
  7184. hLock = (HLOCK)m_hMutex;
  7185. // Release hMutex
  7186. ReleaseMutex(hMutex);
  7187. // Free hMutex
  7188. SafeCloseHandle(hMutex);
  7189. // Sucess
  7190. fNeedOpenFile = FALSE;
  7191. exit:
  7192. // Not In Move File
  7193. m_fInMoveFile = FALSE;
  7194. // If Need Open File
  7195. if (fNeedOpenFile)
  7196. {
  7197. // Try to re-open the file..
  7198. _DispatchInvoke(INVOKE_OPENFILE);
  7199. }
  7200. // Unlock
  7201. Unlock(&hLock);
  7202. // Cleanup
  7203. SafeMemFree(pszFilePath);
  7204. SafeMemFree(pszShare);
  7205. // done
  7206. return(hr);
  7207. }
  7208. //--------------------------------------------------------------------------
  7209. // CDatabase::Compact
  7210. //--------------------------------------------------------------------------
  7211. STDMETHODIMP CDatabase::Compact(IDatabaseProgress *pProgress, COMPACTFLAGS dwFlags)
  7212. {
  7213. // Locals
  7214. HRESULT hr=S_OK;
  7215. DWORD i;
  7216. DWORD dwVersion;
  7217. DWORDLONG dwlFree;
  7218. DWORD cDuplicates=0;
  7219. DWORD cRecords=0;
  7220. DWORD cbDecrease;
  7221. LPVOID pBinding=NULL;
  7222. DWORD dwNextId;
  7223. DWORD cbWasted;
  7224. HLOCK hLock=NULL;
  7225. HLOCK hDstLock=NULL;
  7226. DWORD cActiveThreads;
  7227. LPWSTR pszDstFile=NULL;
  7228. HROWSET hRowset=NULL;
  7229. DWORD cch;
  7230. CDatabase *pDstDB=NULL;
  7231. // Trace
  7232. TraceCall("CDatabase::Compact");
  7233. // Lock
  7234. IF_FAILEXIT(hr = Lock(&hLock));
  7235. // If Compacting...
  7236. if (TRUE == m_pShare->fCompacting)
  7237. {
  7238. // Leave Spin Lock
  7239. Unlock(&hLock);
  7240. // Trace
  7241. return(TraceResult(DB_E_COMPACTING));
  7242. }
  7243. // I am compacting
  7244. m_pShare->fCompacting = TRUE;
  7245. // Yield
  7246. if (ISFLAGSET(dwFlags, COMPACT_YIELD))
  7247. {
  7248. // Yield ?
  7249. m_fCompactYield = TRUE;
  7250. }
  7251. // Get Length
  7252. cch = lstrlenW(m_pShare->szFile);
  7253. //Bug #101511: (erici) Debug shlwapi validates it to MAX_PATH characters
  7254. if( (cch+15) < MAX_PATH)
  7255. {
  7256. cch = MAX_PATH-15;
  7257. }
  7258. // Create .dbt file
  7259. IF_NULLEXIT(pszDstFile = AllocateStringW(cch + 15));
  7260. // Copy File Name
  7261. StrCpyNW(pszDstFile, m_pShare->szFile, (cch+15));
  7262. // Change the Extension
  7263. PathRenameExtensionW(pszDstFile, L".dbt");
  7264. // Delete that file
  7265. DeleteFileWrapW(pszDstFile);
  7266. // Delete my Current File
  7267. if (PathFileExistsW(pszDstFile))
  7268. {
  7269. hr = TraceResult(E_FAIL);
  7270. goto exit;
  7271. }
  7272. // Loop through the stream table and see if there are any streams open for a write
  7273. for (i=0; i<CMAX_OPEN_STREAMS; i++)
  7274. {
  7275. // Is In use...
  7276. if (TRUE == m_pShare->rgStream[i].fInUse && LOCK_VALUE_WRITER == m_pShare->rgStream[i].lLock)
  7277. {
  7278. hr = TraceResult(DB_E_COMPACT_PREEMPTED);
  7279. goto exit;
  7280. }
  7281. }
  7282. // If there are pending notifications...
  7283. if (m_pHeader->cTransacts > 0)
  7284. {
  7285. hr = TraceResult(DB_E_DATABASE_CHANGED);
  7286. goto exit;
  7287. }
  7288. // Is there enought disk space where pDstDB is located
  7289. IF_FAILEXIT(hr = GetAvailableDiskSpace(m_pShare->szFile, &dwlFree));
  7290. // Compute cbWasted
  7291. cbWasted = (m_pHeader->cbFreed + (m_pStorage->cbFile - m_pHeader->faNextAllocate));
  7292. // Is there enough disk space ?
  7293. if (dwlFree <= ((DWORDLONG) (m_pStorage->cbFile - cbWasted)))
  7294. {
  7295. hr = TraceResult(DB_E_DISKFULL);
  7296. goto exit;
  7297. }
  7298. // Create the Object Database Object
  7299. IF_NULLEXIT(pDstDB = new CDatabase);
  7300. // Open the Table
  7301. IF_FAILEXIT(hr = pDstDB->Open(pszDstFile, OPEN_DATABASE_NOEXTENSION | OPEN_DATABASE_NOMONITOR, m_pSchema, NULL));
  7302. // Lock the Destination Database
  7303. IF_FAILEXIT(hr = pDstDB->Lock(&hDstLock));
  7304. // Get user info from current tree
  7305. if (m_pSchema->cbUserData)
  7306. {
  7307. // Set the user data
  7308. IF_FAILEXIT(hr = pDstDB->SetUserData(PUSERDATA(m_pHeader), m_pSchema->cbUserData));
  7309. }
  7310. // I'm going to grow the destination to be as big as the current file (I will truncate when finished)
  7311. IF_FAILEXIT(hr = pDstDB->SetSize(m_pStorage->cbFile - cbWasted));
  7312. // Set number of indexes
  7313. pDstDB->m_pHeader->cIndexes = m_pHeader->cIndexes;
  7314. // Copy Index Information...
  7315. CopyMemory((LPBYTE)pDstDB->m_pHeader->rgIndexInfo, (LPBYTE)m_pHeader->rgIndexInfo, sizeof(TABLEINDEX) * CMAX_INDEXES);
  7316. // Copy Index Information...
  7317. CopyMemory((LPBYTE)pDstDB->m_pHeader->rgiIndex, (LPBYTE)m_pHeader->rgiIndex, sizeof(INDEXORDINAL) * CMAX_INDEXES);
  7318. // Transfer Query Table...
  7319. IF_FAILEXIT(hr = _CompactTransferFilters(pDstDB));
  7320. // Allocate a Record
  7321. IF_NULLEXIT(pBinding = PHeapAllocate(HEAP_ZERO_MEMORY, m_pSchema->cbBinding));
  7322. // Create a Rowset
  7323. IF_FAILEXIT(hr = CreateRowset(IINDEX_PRIMARY, NOFLAGS, &hRowset));
  7324. // Save new Version
  7325. dwVersion = m_pShare->dwVersion;
  7326. // While we have a node address
  7327. while (S_OK == QueryRowset(hRowset, 1, (LPVOID *)pBinding, NULL))
  7328. {
  7329. // Can Preempt
  7330. if (ISFLAGSET(dwFlags, COMPACT_PREEMPTABLE) && m_pShare->cWaitingForLock > 0)
  7331. {
  7332. hr = TraceResult(DB_E_COMPACT_PREEMPTED);
  7333. goto exit;
  7334. }
  7335. // If the record has streams
  7336. if (ISFLAGSET(m_pSchema->dwFlags, TSF_HASSTREAMS))
  7337. {
  7338. // Compact Move Record Streams
  7339. IF_FAILEXIT(hr = _CompactMoveRecordStreams(pDstDB, pBinding));
  7340. }
  7341. // Insert Record Into Destination
  7342. hr = pDstDB->_CompactInsertRecord(pBinding);
  7343. // Duplicate
  7344. if (DB_E_DUPLICATE == hr)
  7345. {
  7346. // Trace
  7347. TraceResult(DB_E_DUPLICATE);
  7348. // Reset Hr
  7349. hr = S_OK;
  7350. // Count
  7351. cDuplicates++;
  7352. }
  7353. // Failed ?
  7354. else if (FAILED (hr))
  7355. {
  7356. TraceResult(hr);
  7357. goto exit;
  7358. }
  7359. // Count
  7360. cRecords++;
  7361. // Free this Record
  7362. FreeRecord(pBinding);
  7363. // Update the Progress...
  7364. if (pProgress)
  7365. {
  7366. // Call into the progress object
  7367. IF_FAILEXIT(hr = pProgress->Update(1));
  7368. // Version Change ?
  7369. if (dwVersion != m_pShare->dwVersion || m_pHeader->cTransacts > 0)
  7370. {
  7371. hr = TraceResult(DB_E_DATABASE_CHANGED);
  7372. goto exit;
  7373. }
  7374. }
  7375. // Yield
  7376. if (ISFLAGSET(dwFlags, COMPACT_YIELD))
  7377. {
  7378. // this will force this thread to give up a time-slice
  7379. Sleep(0);
  7380. }
  7381. }
  7382. // Duplicates ?
  7383. AssertSz(cDuplicates == 0, "Duplicates were found in the tree. They have been eliminated.");
  7384. // Copy over deleted streams that are currently open...
  7385. IF_FAILEXIT(hr = _CompactMoveOpenDeletedStreams(pDstDB));
  7386. // Number of records better be equal
  7387. AssertSz(cRecords == m_pHeader->rgcRecords[0], "Un-expected number of records compacted");
  7388. // Save dwNextId
  7389. dwNextId = m_pHeader->dwNextId;
  7390. // Save Active Threads
  7391. cActiveThreads = m_pHeader->cActiveThreads;
  7392. // Compute amount to decrease the file by
  7393. cbDecrease = (pDstDB->m_pStorage->cbFile - pDstDB->m_pHeader->faNextAllocate);
  7394. // Reduce the Size of myself...
  7395. IF_FAILEXIT(hr = pDstDB->_SetStorageSize(pDstDB->m_pStorage->cbFile - cbDecrease));
  7396. // Unlock the file
  7397. pDstDB->Unlock(&hDstLock);
  7398. // Release pDstDB
  7399. SafeRelease(pDstDB);
  7400. // Do It
  7401. IF_FAILEXIT(hr = _DispatchInvoke(INVOKE_CLOSEFILE));
  7402. // Delete my Current File
  7403. if (0 == DeleteFileWrapW(m_pShare->szFile))
  7404. {
  7405. // Failure
  7406. hr = TraceResult(E_FAIL);
  7407. // Try to re-open the file..
  7408. _DispatchInvoke(INVOKE_OPENFILE);
  7409. // Done
  7410. goto exit;
  7411. }
  7412. // Move the file from the temp location to my current location
  7413. if (0 == MoveFileWrapW(pszDstFile, m_pShare->szFile))
  7414. {
  7415. // Trace
  7416. hr = TraceResult(DB_E_MOVEFILE);
  7417. // Try to re-open the file..
  7418. _DispatchInvoke(INVOKE_OPENFILE);
  7419. // Done
  7420. goto exit;
  7421. }
  7422. // Do It
  7423. IF_FAILEXIT(hr = _DispatchInvoke(INVOKE_OPENFILE));
  7424. // Reset Active Thread Count
  7425. m_pHeader->cActiveThreads = cActiveThreads;
  7426. // Reset dwNextId
  7427. m_pHeader->dwNextId = dwNextId;
  7428. // Reset Notification Queue
  7429. Assert(0 == m_pHeader->faTransactHead && 0 == m_pHeader->faTransactTail);
  7430. // Reset Transaction List
  7431. m_pHeader->faTransactHead = m_pHeader->faTransactTail = m_pHeader->cTransacts = 0;
  7432. // Reset Share Transacts
  7433. m_pShare->faTransactLockHead = m_pShare->faTransactLockTail = 0;
  7434. // Loop through the stream table and adjust the start address of all open streams
  7435. for (i=0; i<CMAX_OPEN_STREAMS; i++)
  7436. {
  7437. // Is In use...
  7438. if (TRUE == m_pShare->rgStream[i].fInUse)
  7439. {
  7440. // Change the Address
  7441. m_pShare->rgStream[i].faStart = m_pShare->rgStream[i].faMoved;
  7442. }
  7443. }
  7444. // Build the Update Notification Package
  7445. _LogTransaction(TRANSACTION_COMPACTED, INVALID_INDEX_ORDINAL, NULL, 0, 0);
  7446. exit:
  7447. // Close my rowset
  7448. CloseRowset(&hRowset);
  7449. // Release Dst Lock
  7450. if (pDstDB && hDstLock)
  7451. pDstDB->Unlock(&hDstLock);
  7452. // Release the memory mapped pointers
  7453. SafeRelease(pDstDB);
  7454. // Free the Record
  7455. SafeFreeBinding(pBinding);
  7456. // No Longer compacting
  7457. m_pShare->fCompacting = FALSE;
  7458. // Delete pszDstFile
  7459. if (pszDstFile)
  7460. {
  7461. // Delete the file
  7462. DeleteFileWrapW(pszDstFile);
  7463. // Remaining Cleanup
  7464. SafeMemFree(pszDstFile);
  7465. }
  7466. // Reset Yield
  7467. m_fCompactYield = FALSE;
  7468. // Release Locks
  7469. Unlock(&hLock);
  7470. // Done
  7471. return(hr);
  7472. }
  7473. //--------------------------------------------------------------------------
  7474. // CDatabase::_CheckForCorruption
  7475. //--------------------------------------------------------------------------
  7476. HRESULT CDatabase::_CheckForCorruption(void)
  7477. {
  7478. // Locals
  7479. HRESULT hr=S_OK;
  7480. ULONG cRecords;
  7481. DWORD i;
  7482. HRESULT rghrCorrupt[CMAX_INDEXES]={0};
  7483. DWORD cCorrupt=0;
  7484. INDEXORDINAL iIndex;
  7485. // Trace
  7486. TraceCall("CDatabase::_CheckForCorruption");
  7487. // We should not be currently repairing
  7488. Assert(FALSE == m_pShare->fRepairing);
  7489. // We are now repairing
  7490. IF_DEBUG(m_pShare->fRepairing = TRUE);
  7491. // Walk Through the Indexes
  7492. for (i = 0; i < m_pHeader->cIndexes; i++)
  7493. {
  7494. // Get Index Ordinal
  7495. iIndex = m_pHeader->rgiIndex[i];
  7496. // Zero Out cRecords
  7497. cRecords = 0;
  7498. // Assume all is good
  7499. rghrCorrupt[iIndex] = S_OK;
  7500. // Start at the root
  7501. if (m_pHeader->rgfaIndex[iIndex])
  7502. {
  7503. // Validate the Index
  7504. rghrCorrupt[iIndex] = _ValidateIndex(iIndex, m_pHeader->rgfaIndex[iIndex], 0, &cRecords);
  7505. }
  7506. // If Not Corrupt, validate the record counts
  7507. if (DB_E_CORRUPT != rghrCorrupt[iIndex] && m_pHeader->rgcRecords[iIndex] != cRecords)
  7508. {
  7509. // Its Corrupt
  7510. rghrCorrupt[iIndex] = TraceResult(DB_E_CORRUPT);
  7511. }
  7512. // If Corrupt
  7513. if (DB_E_CORRUPT == rghrCorrupt[iIndex])
  7514. {
  7515. // Count Number of Corrupted records
  7516. cCorrupt += m_pHeader->rgcRecords[iIndex];
  7517. }
  7518. }
  7519. // Are the Corrupt Records
  7520. if (cCorrupt > 0 || m_pHeader->fCorrupt)
  7521. {
  7522. // I'm going to nuke the free block tables since they may also be corrupted...
  7523. ZeroMemory(m_pHeader->rgfaFreeBlock, sizeof(FILEADDRESS) * CC_FREE_BUCKETS);
  7524. // Reset Fixed blocks
  7525. m_pHeader->faFreeStreamBlock = m_pHeader->faFreeChainBlock = m_pHeader->faFreeLargeBlock = 0;
  7526. // Nuke the Transaction List
  7527. m_pHeader->cTransacts = m_pHeader->faTransactHead = m_pHeader->faTransactTail = 0;
  7528. // Nuke The Fixed Block Allocation Pages
  7529. ZeroMemory(&m_pHeader->AllocateRecord, sizeof(ALLOCATEPAGE));
  7530. ZeroMemory(&m_pHeader->AllocateChain, sizeof(ALLOCATEPAGE));
  7531. ZeroMemory(&m_pHeader->AllocateStream, sizeof(ALLOCATEPAGE));
  7532. // Reset rgcbAllocated
  7533. ZeroMemory(m_pHeader->rgcbAllocated, sizeof(DWORD) * CC_MAX_BLOCK_TYPES);
  7534. // Reset faNextAllocate
  7535. m_pHeader->faNextAllocate = m_pStorage->cbFile;
  7536. // Walk Through the Indexes
  7537. for (i = 0; i < m_pHeader->cIndexes; i++)
  7538. {
  7539. // Get Index Ordinal
  7540. iIndex = m_pHeader->rgiIndex[i];
  7541. // If Corrupt
  7542. if (DB_E_CORRUPT == rghrCorrupt[iIndex])
  7543. {
  7544. // Not Corrupt
  7545. m_pHeader->fCorrupt = TRUE;
  7546. // Rebuild the Index
  7547. IF_FAILEXIT(hr = _RebuildIndex(iIndex));
  7548. }
  7549. }
  7550. }
  7551. // Not Corrupt
  7552. m_pHeader->fCorrupt = FALSE;
  7553. // This causes all current file views to be flushed and released. Insures we are in a good state.
  7554. #ifdef BACKGROUND_MONITOR
  7555. DoBackgroundMonitor();
  7556. #else
  7557. CloseFileViews(TRUE);
  7558. #endif
  7559. exit:
  7560. // We are now repairing
  7561. IF_DEBUG(m_pShare->fRepairing = FALSE);
  7562. // Done
  7563. return(hr);
  7564. }
  7565. //--------------------------------------------------------------------------
  7566. // CDatabase::_FreeIndex
  7567. //--------------------------------------------------------------------------
  7568. HRESULT CDatabase::_FreeIndex(FILEADDRESS faChain)
  7569. {
  7570. // Locals
  7571. HRESULT hr=S_OK;
  7572. NODEINDEX i;
  7573. LPCHAINBLOCK pChain;
  7574. // Trace
  7575. TraceCall("CDatabase::_FreeIndex");
  7576. // Nothing to validate
  7577. if (0 == faChain)
  7578. return(S_OK);
  7579. // Validate faChain
  7580. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faChain, (LPVOID *)&pChain));
  7581. // Go to the left
  7582. IF_FAILEXIT(hr = _FreeIndex(pChain->faLeftChain));
  7583. // Loop throug right chains
  7584. for (i=0; i<pChain->cNodes; i++)
  7585. {
  7586. // Validate the Right Chain
  7587. IF_FAILEXIT(hr = _FreeIndex(pChain->rgNode[i].faRightChain));
  7588. }
  7589. // Free this Chain
  7590. IF_FAILEXIT(hr = _FreeBlock(BLOCK_CHAIN, faChain));
  7591. exit:
  7592. // Done
  7593. return(hr);
  7594. }
  7595. //--------------------------------------------------------------------------
  7596. // CDatabase::_ValidateIndex
  7597. //--------------------------------------------------------------------------
  7598. HRESULT CDatabase::_ValidateIndex(INDEXORDINAL iIndex,
  7599. FILEADDRESS faChain, ULONG cLeftNodes, ULONG *pcRecords)
  7600. {
  7601. // Locals
  7602. HRESULT hr=S_OK;
  7603. NODEINDEX i;
  7604. LPCHAINBLOCK pChain;
  7605. LPRECORDBLOCK pRecord;
  7606. DWORD cLeafs=0;
  7607. DWORD cNodes;
  7608. RECORDMAP Map;
  7609. // Trace
  7610. TraceCall("CDatabase::_ValidateIndex");
  7611. // Nothing to validate
  7612. Assert(0 != faChain);
  7613. // Get Chain
  7614. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faChain, (LPVOID *)&pChain));
  7615. // Validate Minimum Filled Constraint
  7616. if (pChain->cNodes < BTREE_MIN_CAP && pChain->faBlock != m_pHeader->rgfaIndex[iIndex])
  7617. return TraceResult(DB_E_CORRUPT);
  7618. // Validate faParent
  7619. if (pChain->faParent)
  7620. {
  7621. // Locals
  7622. LPCHAINBLOCK pParent;
  7623. // Get Parent
  7624. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, pChain->faParent, (LPVOID *)&pParent));
  7625. // Validate iParent
  7626. if (pChain->iParent >= pParent->cNodes)
  7627. return TraceResult(DB_E_CORRUPT);
  7628. // Validate the Parent Pointer
  7629. if (0 == pChain->iParent)
  7630. {
  7631. // Validation
  7632. if (pParent->rgNode[pChain->iParent].faRightChain != pChain->faBlock && pParent->faLeftChain != pChain->faBlock)
  7633. return TraceResult(DB_E_CORRUPT);
  7634. }
  7635. // Otherwise
  7636. else if (pParent->rgNode[pChain->iParent].faRightChain != pChain->faBlock)
  7637. return TraceResult(DB_E_CORRUPT);
  7638. }
  7639. // Otherwise, iParent should be zero...
  7640. else
  7641. {
  7642. // This is the root chain
  7643. if (m_pHeader->rgfaIndex[iIndex] != pChain->faBlock)
  7644. return TraceResult(DB_E_CORRUPT);
  7645. // iParent should be 0
  7646. Assert(pChain->iParent == 0);
  7647. }
  7648. // Do the Left
  7649. if (pChain->faLeftChain)
  7650. {
  7651. // Go to the left
  7652. IF_FAILEXIT(hr = _ValidateIndex(iIndex, pChain->faLeftChain, cLeftNodes, pcRecords));
  7653. }
  7654. // cNodes
  7655. cNodes = pChain->cLeftNodes;
  7656. // Validate the Records in this chain
  7657. for (i=0; i<pChain->cNodes; i++)
  7658. {
  7659. // Count the number of leaf nodes
  7660. cLeafs += (0 == pChain->rgNode[i].faRightChain) ? 1 : 0;
  7661. // cNodes
  7662. cNodes += pChain->rgNode[i].cRightNodes;
  7663. }
  7664. // Validate the Number of Leafs
  7665. if (cLeafs > 0 && cLeafs != (DWORD)pChain->cNodes)
  7666. return TraceResult(DB_E_CORRUPT);
  7667. // No leafs, but their are child nodes, or vice vera...
  7668. if ((0 != cLeafs && 0 != cNodes) || (0 == cLeafs && 0 == cNodes))
  7669. return TraceResult(DB_E_CORRUPT);
  7670. // Loop throug right chains
  7671. for (i=0; i<pChain->cNodes; i++)
  7672. {
  7673. // Try to get the record, if the block is invalid, we will throw away the record
  7674. if (SUCCEEDED(_GetBlock(BLOCK_RECORD, pChain->rgNode[i].faRecord, (LPVOID *)&pRecord, FALSE)))
  7675. {
  7676. // Validate Block...
  7677. if (SUCCEEDED(_GetRecordMap(FALSE, pRecord, &Map)))
  7678. {
  7679. // Validate and Repair the Record
  7680. if (S_OK == _ValidateAndRepairRecord(&Map))
  7681. {
  7682. // Count Records
  7683. (*pcRecords)++;
  7684. }
  7685. }
  7686. }
  7687. // First Node ?
  7688. if (0 == i)
  7689. {
  7690. // Increment cLeft Nodes
  7691. cLeftNodes += pChain->cLeftNodes;
  7692. }
  7693. // Otherwise
  7694. else
  7695. {
  7696. // Increment cLeftNodes
  7697. cLeftNodes += pChain->rgNode[i - 1].cRightNodes;
  7698. }
  7699. // Failure
  7700. if ((*pcRecords) != cLeftNodes + 1)
  7701. return TraceResult(DB_E_CORRUPT);
  7702. // Increment cLeftNodes
  7703. cLeftNodes++;
  7704. // Do the Right
  7705. if (pChain->rgNode[i].faRightChain)
  7706. {
  7707. // Validate the Right Chain
  7708. IF_FAILEXIT(hr = _ValidateIndex(iIndex, pChain->rgNode[i].faRightChain, cLeftNodes, pcRecords));
  7709. }
  7710. }
  7711. exit:
  7712. // Done
  7713. return(hr);
  7714. }
  7715. //--------------------------------------------------------------------------
  7716. // CDatabase::_RebuildIndex
  7717. //--------------------------------------------------------------------------
  7718. HRESULT CDatabase::_RebuildIndex(INDEXORDINAL iIndex)
  7719. {
  7720. // Locals
  7721. HRESULT hr=S_OK;
  7722. LPVOID pBinding=NULL;
  7723. DWORD cRecords=0;
  7724. FILEADDRESS faPrimary;
  7725. // Trace
  7726. TraceCall("CDatabase::_RebuildIndex");
  7727. // Allocate a record
  7728. IF_NULLEXIT(pBinding = PHeapAllocate(HEAP_ZERO_MEMORY, m_pSchema->cbBinding));
  7729. // Save Primary Index Starting Address
  7730. faPrimary = m_pHeader->rgfaIndex[IINDEX_PRIMARY];
  7731. // Reset rgfaIndex[iIndex]
  7732. m_pHeader->rgfaIndex[iIndex] = 0;
  7733. // Is there a root chain ?
  7734. if (faPrimary)
  7735. {
  7736. // Recursively Rebuild this index
  7737. IF_FAILEXIT(hr = _RecursiveRebuildIndex(iIndex, faPrimary, pBinding, &cRecords));
  7738. }
  7739. // Fixup Record Count
  7740. m_pHeader->rgcRecords[iIndex] = cRecords;
  7741. // Send Notifications ?
  7742. if (m_pShare->rgcIndexNotify[iIndex] > 0)
  7743. {
  7744. // Build the Update Notification Package
  7745. _LogTransaction(TRANSACTION_INDEX_CHANGED, iIndex, NULL, 0, 0);
  7746. }
  7747. exit:
  7748. // Cleanup
  7749. SafeFreeBinding(pBinding);
  7750. // Done
  7751. return(hr);
  7752. }
  7753. //--------------------------------------------------------------------------
  7754. // CDatabase::_RecursiveRebuildIndex
  7755. //--------------------------------------------------------------------------
  7756. HRESULT CDatabase::_RecursiveRebuildIndex(INDEXORDINAL iIndex,
  7757. FILEADDRESS faCurrent, LPVOID pBinding, LPDWORD pcRecords)
  7758. {
  7759. // Locals
  7760. NODEINDEX i;
  7761. FILEADDRESS faRecord;
  7762. FILEADDRESS faChain;
  7763. NODEINDEX iNode;
  7764. CHAINBLOCK Chain;
  7765. LPCHAINBLOCK pChain;
  7766. INT nCompare;
  7767. BOOL fGoodRecord=TRUE;
  7768. RECORDMAP Map;
  7769. LPRECORDBLOCK pRecord;
  7770. // Trace
  7771. TraceCall("CDatabase::_RecursiveRebuildIndex");
  7772. // Nothing to validate
  7773. Assert(0 != faCurrent);
  7774. // Validate faChain
  7775. if (FAILED(_GetBlock(BLOCK_CHAIN, faCurrent, (LPVOID *)&pChain)))
  7776. return(S_OK);
  7777. // Copy This
  7778. CopyMemory(&Chain, pChain, sizeof(CHAINBLOCK));
  7779. // Do the left
  7780. if (Chain.faLeftChain)
  7781. {
  7782. // Go to the left
  7783. _RecursiveRebuildIndex(iIndex, Chain.faLeftChain, pBinding, pcRecords);
  7784. }
  7785. // Loop throug right chains
  7786. for (i=0; i<Chain.cNodes; i++)
  7787. {
  7788. // Set faRecord
  7789. faRecord = Chain.rgNode[i].faRecord;
  7790. // Get the Block
  7791. if (SUCCEEDED(_GetBlock(BLOCK_RECORD, faRecord, (LPVOID *)&pRecord, NULL, FALSE)))
  7792. {
  7793. // Rebuilding Primary Index ?
  7794. if (IINDEX_PRIMARY == iIndex)
  7795. {
  7796. // Assume this is a bad record
  7797. fGoodRecord = FALSE;
  7798. // Try to get the record map
  7799. if (SUCCEEDED(_GetRecordMap(FALSE, pRecord, &Map)))
  7800. {
  7801. // Validate Map ?
  7802. if (S_OK == _ValidateAndRepairRecord(&Map))
  7803. {
  7804. // Good Record
  7805. fGoodRecord = TRUE;
  7806. }
  7807. }
  7808. }
  7809. // Good Record ?
  7810. if (fGoodRecord)
  7811. {
  7812. // Load the Record
  7813. if (SUCCEEDED(_ReadRecord(faRecord, pBinding, TRUE)))
  7814. {
  7815. // Reset hrVisible
  7816. if (S_OK == _IsVisible(m_rghFilter[iIndex], pBinding))
  7817. {
  7818. // Otherwise: Decide Where to insert
  7819. if (DB_S_NOTFOUND == _FindRecord(iIndex, COLUMNS_ALL, pBinding, &faChain, &iNode, NULL, &nCompare))
  7820. {
  7821. // Insert the Record
  7822. if (SUCCEEDED(_IndexInsertRecord(iIndex, faChain, faRecord, &iNode, nCompare)))
  7823. {
  7824. // Increment Record Count
  7825. (*pcRecords)++;
  7826. }
  7827. }
  7828. }
  7829. }
  7830. }
  7831. }
  7832. // Do the Right
  7833. if (Chain.rgNode[i].faRightChain)
  7834. {
  7835. // Index the Right Chain
  7836. _RecursiveRebuildIndex(iIndex, Chain.rgNode[i].faRightChain, pBinding, pcRecords);
  7837. }
  7838. }
  7839. // Done
  7840. return(S_OK);
  7841. }
  7842. //--------------------------------------------------------------------------
  7843. // CDatabase::_ValidateStream
  7844. //--------------------------------------------------------------------------
  7845. HRESULT CDatabase::_ValidateStream(FILEADDRESS faStart)
  7846. {
  7847. // Locals
  7848. LPSTREAMBLOCK pStream;
  7849. FILEADDRESS faCurrent;
  7850. // Trace
  7851. TraceCall("CDatabase::_ValidateStream");
  7852. // No Stream
  7853. if (0 == faStart)
  7854. return(S_OK);
  7855. // Initialize Loop
  7856. faCurrent = faStart;
  7857. // Read through all of the blocks (i.e. verify headers and count the number of chains)
  7858. while (faCurrent)
  7859. {
  7860. // Valid stream Block
  7861. if (FAILED(_GetBlock(BLOCK_STREAM, faCurrent, (LPVOID *)&pStream)))
  7862. return(S_FALSE);
  7863. // Validate cbData
  7864. if (pStream->cbData > pStream->cbSize)
  7865. return(S_FALSE);
  7866. // Set faCurrent
  7867. faCurrent = pStream->faNext;
  7868. }
  7869. // Done
  7870. return(S_OK);
  7871. }
  7872. //--------------------------------------------------------------------------
  7873. // CDatabase::_ValidateAndRepairRecord
  7874. //--------------------------------------------------------------------------
  7875. HRESULT CDatabase::_ValidateAndRepairRecord(LPRECORDMAP pMap)
  7876. {
  7877. // Locals
  7878. LPCTABLECOLUMN pColumn;
  7879. LPCOLUMNTAG pTag;
  7880. WORD iTag;
  7881. // Trace
  7882. TraceCall("CDatabase::_ValidateAndRepairRecord");
  7883. // Walk through the Tags of the Record
  7884. for (iTag=0; iTag<pMap->cTags; iTag++)
  7885. {
  7886. // Readability
  7887. pTag = &pMap->prgTag[iTag];
  7888. // Validate the Tag
  7889. if (pTag->iColumn >= m_pSchema->cColumns)
  7890. return(S_FALSE);
  7891. // De-ref the Column
  7892. pColumn = &m_pSchema->prgColumn[pTag->iColumn];
  7893. // Read the Data
  7894. if (S_FALSE == DBTypeValidate(pColumn, pTag, pMap))
  7895. return(S_FALSE);
  7896. // Is this a stream ?
  7897. if (CDT_STREAM == pColumn->type)
  7898. {
  7899. // Locals
  7900. FILEADDRESS faStream;
  7901. // Get the faStream
  7902. if (1 == pTag->fData)
  7903. faStream = pTag->Offset;
  7904. else
  7905. faStream = *((DWORD *)(pMap->pbData + pTag->Offset));
  7906. // Validate this stream...
  7907. if (S_FALSE == _ValidateStream(faStream))
  7908. {
  7909. // Kill the stream address...
  7910. if (1 == pTag->fData)
  7911. pTag->Offset = 0;
  7912. else
  7913. *((DWORD *)(pMap->pbData + pTag->Offset)) = 0;
  7914. }
  7915. }
  7916. // Unique Key ?
  7917. if (CDT_UNIQUE == pColumn->type)
  7918. {
  7919. // Locals
  7920. DWORD dwUniqueID;
  7921. // Get the dwUniqueID
  7922. if (1 == pTag->fData)
  7923. dwUniqueID = pTag->Offset;
  7924. else
  7925. dwUniqueID = *((DWORD *)(pMap->pbData + pTag->Offset));
  7926. // Adjust the id in the header ?
  7927. if (dwUniqueID >= m_pHeader->dwNextId)
  7928. m_pHeader->dwNextId = dwUniqueID + 1;
  7929. }
  7930. }
  7931. // Done
  7932. return(S_OK);
  7933. }
  7934. //--------------------------------------------------------------------------
  7935. // CDatabase::_GetRecordMap
  7936. //--------------------------------------------------------------------------
  7937. HRESULT CDatabase::_GetRecordMap(BOOL fGoCorrupt, LPRECORDBLOCK pBlock,
  7938. LPRECORDMAP pMap)
  7939. {
  7940. // Trace
  7941. TraceCall("CDatabase::_GetRecordMap");
  7942. // Invalid Args
  7943. Assert(pBlock && pMap);
  7944. // Set pSchema
  7945. pMap->pSchema = m_pSchema;
  7946. // Store Number of Tags
  7947. pMap->cTags = min(pBlock->cTags, m_pSchema->cColumns);
  7948. // Set prgTag
  7949. pMap->prgTag = (LPCOLUMNTAG)((LPBYTE)pBlock + sizeof(RECORDBLOCK));
  7950. // Compute Size of Tags
  7951. pMap->cbTags = (pBlock->cTags * sizeof(COLUMNTAG));
  7952. // Compute Size of Data
  7953. pMap->cbData = (pBlock->cbSize - pMap->cbTags);
  7954. // Set pbData
  7955. pMap->pbData = (LPBYTE)((LPBYTE)pBlock + sizeof(RECORDBLOCK) + pMap->cbTags);
  7956. // No Tags - this is usually the sign of a freeblock that was reused but not allocated
  7957. if (0 == pMap->cTags)
  7958. return _SetCorrupt(fGoCorrupt, __LINE__, REASON_INVALIDRECORDMAP, BLOCK_RECORD, pBlock->faBlock, pBlock->faBlock, pBlock->cbSize);
  7959. // Too many tags
  7960. if (pMap->cTags > m_pSchema->cColumns)
  7961. return _SetCorrupt(fGoCorrupt, __LINE__, REASON_INVALIDRECORDMAP, BLOCK_RECORD, pBlock->faBlock, pBlock->faBlock, pBlock->cbSize);
  7962. // cbTags is too large ?
  7963. if (pMap->cbTags > pBlock->cbSize)
  7964. return _SetCorrupt(fGoCorrupt, __LINE__, REASON_INVALIDRECORDMAP, BLOCK_RECORD, pBlock->faBlock, pBlock->faBlock, pBlock->cbSize);
  7965. // Done
  7966. return(S_OK);
  7967. }
  7968. #ifdef DEBUG
  7969. //--------------------------------------------------------------------------
  7970. // DBDebugValidateRecordFormat
  7971. //--------------------------------------------------------------------------
  7972. HRESULT CDatabase::_DebugValidateRecordFormat(void)
  7973. {
  7974. // Locals
  7975. ULONG i;
  7976. DWORD dwOrdinalPrev=0;
  7977. DWORD dwOrdinalMin=0xffffffff;
  7978. DWORD dwOrdinalMax=0;
  7979. // Validate memory buffer binding offset
  7980. Assert(0xFFFFFFFF != m_pSchema->ofMemory && m_pSchema->ofMemory < m_pSchema->cbBinding);
  7981. // Validate version binding offset
  7982. Assert(0xFFFFFFFF != m_pSchema->ofVersion && m_pSchema->ofVersion < m_pSchema->cbBinding);
  7983. // Validate Extension
  7984. Assert(*m_pSchema->pclsidExtension != CLSID_NULL);
  7985. // Validate Version
  7986. Assert(m_pSchema->dwMinorVersion != 0);
  7987. // Check Number of Indexes
  7988. Assert(m_pSchema->pPrimaryIndex);
  7989. // Loop through they Keys
  7990. for (i=0; i<m_pSchema->cColumns; i++)
  7991. {
  7992. // This Ordinal better be larger than the previous
  7993. if (i > 0)
  7994. Assert(m_pSchema->prgColumn[i].iOrdinal > dwOrdinalPrev);
  7995. // Save Min Ordinal
  7996. if (m_pSchema->prgColumn[i].iOrdinal < dwOrdinalMin)
  7997. dwOrdinalMin = m_pSchema->prgColumn[i].iOrdinal;
  7998. // Save Max Ordinal
  7999. if (m_pSchema->prgColumn[i].iOrdinal > dwOrdinalMax)
  8000. dwOrdinalMax = m_pSchema->prgColumn[i].iOrdinal;
  8001. // Save the Previous Ordinal
  8002. dwOrdinalPrev = m_pSchema->prgColumn[i].iOrdinal;
  8003. }
  8004. // Min ordinal must be one
  8005. Assert(dwOrdinalMin == 0);
  8006. // Done
  8007. return(S_OK);
  8008. }
  8009. //--------------------------------------------------------------------------
  8010. // _DebugValidateUnrefedRecord
  8011. //--------------------------------------------------------------------------
  8012. HRESULT CDatabase::_DebugValidateUnrefedRecord(FILEADDRESS faRecord)
  8013. {
  8014. // Locals
  8015. HRESULT hr=S_OK;
  8016. DWORD i;
  8017. INDEXORDINAL iIndex;
  8018. // Trace
  8019. TraceCall("CDatabase::_DebugValidateUnrefedRecord");
  8020. // Walk Through the Indexes
  8021. for (i=0; i<m_pHeader->cIndexes; i++)
  8022. {
  8023. // Get Index Ordinal
  8024. iIndex = m_pHeader->rgiIndex[i];
  8025. // Start at the root
  8026. if (m_pHeader->rgfaIndex[iIndex])
  8027. {
  8028. // Validate the Index
  8029. IF_FAILEXIT(hr = _DebugValidateIndexUnrefedRecord(m_pHeader->rgfaIndex[iIndex], faRecord));
  8030. }
  8031. }
  8032. exit:
  8033. // Done
  8034. return(hr);
  8035. }
  8036. //--------------------------------------------------------------------------
  8037. // CDatabase::_DebugValidateIndexUnrefedRecord
  8038. //--------------------------------------------------------------------------
  8039. HRESULT CDatabase::_DebugValidateIndexUnrefedRecord(FILEADDRESS faChain,
  8040. FILEADDRESS faRecord)
  8041. {
  8042. // Locals
  8043. HRESULT hr=S_OK;
  8044. NODEINDEX i;
  8045. LPCHAINBLOCK pChain;
  8046. // Trace
  8047. TraceCall("CDatabase::_DebugValidateIndexUnrefedRecord");
  8048. // Nothing to validate
  8049. Assert(0 != faChain);
  8050. // Validate faChain
  8051. IF_FAILEXIT(hr = _GetBlock(BLOCK_CHAIN, faChain, (LPVOID *)&pChain));
  8052. // Do the left
  8053. if (pChain->faLeftChain)
  8054. {
  8055. // Go to the left
  8056. IF_FAILEXIT(hr = _DebugValidateIndexUnrefedRecord(pChain->faLeftChain, faRecord));
  8057. }
  8058. // Loop throug right chains
  8059. for (i=0; i<pChain->cNodes; i++)
  8060. {
  8061. // Set faRecord
  8062. if (faRecord == pChain->rgNode[i].faRecord)
  8063. {
  8064. IxpAssert(FALSE);
  8065. hr = TraceResult(E_FAIL);
  8066. goto exit;
  8067. }
  8068. // Do the Right
  8069. if (pChain->rgNode[i].faRightChain)
  8070. {
  8071. // Index the Right Chain
  8072. IF_FAILEXIT(hr = _DebugValidateIndexUnrefedRecord(pChain->rgNode[i].faRightChain, faRecord));
  8073. }
  8074. }
  8075. exit:
  8076. // Done
  8077. return(hr);
  8078. }
  8079. #endif // DEBUG