git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@674 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2760 lines
67 KiB
C++
2760 lines
67 KiB
C++
//------------------------------------------------------------------------------
|
|
// Desc: Various classes used to manage cache.
|
|
//
|
|
// Tabs: 3
|
|
//
|
|
// Copyright (c) 2004-2006 Novell, Inc. All Rights Reserved.
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of version 2 of the GNU General Public
|
|
// License as published by the Free Software Foundation.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, contact Novell, Inc.
|
|
//
|
|
// To contact Novell about this file by physical or electronic mail,
|
|
// you may find current contact information at www.novell.com
|
|
//
|
|
// $Id$
|
|
//------------------------------------------------------------------------------
|
|
|
|
#ifndef FCACHE_H
|
|
#define FCACHE_H
|
|
|
|
class F_Rfl;
|
|
class F_Db;
|
|
class F_DbSystem;
|
|
class F_Database;
|
|
class F_MultiAlloc;
|
|
class F_Row;
|
|
class F_CachedBlock;
|
|
class F_BlockCacheMgr;
|
|
class F_RowCacheMgr;
|
|
class F_GlobalCacheMgr;
|
|
class F_CacheList;
|
|
class F_CachedItem;
|
|
class F_Btree;
|
|
class F_BTreeIStream;
|
|
class F_BTreeInfo;
|
|
class F_RowRelocator;
|
|
class F_ColumnDataRelocator;
|
|
class F_ColumnListRelocator;
|
|
class F_BlockRelocator;
|
|
|
|
#define MIN_HASH_BUCKETS 0x10000 // 65536 buckets - multiple of 2.
|
|
#define MAX_HASH_BUCKETS 0x20000000 // roughly 500,000,000 buckets.
|
|
|
|
FLMUINT caGetBestHashTblSize( // scache.cpp
|
|
FLMUINT uiCurrItemCount);
|
|
|
|
FINLINE FLMUINT minItemCount(
|
|
FLMUINT uiNumHashBuckets)
|
|
{
|
|
return( uiNumHashBuckets / 4);
|
|
}
|
|
|
|
FINLINE FLMUINT maxItemCount(
|
|
FLMUINT uiNumHashBuckets)
|
|
{
|
|
return( uiNumHashBuckets * 4);
|
|
}
|
|
|
|
FINLINE FLMBOOL shouldRehash(
|
|
FLMUINT uiItemCount,
|
|
FLMUINT uiNumBuckets)
|
|
{
|
|
return( ((uiItemCount > maxItemCount( uiNumBuckets) &&
|
|
uiNumBuckets < MAX_HASH_BUCKETS) ||
|
|
(uiItemCount < minItemCount( uiNumBuckets) &&
|
|
uiNumBuckets > MIN_HASH_BUCKETS))
|
|
? TRUE
|
|
: FALSE);
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: See if enough time has passed since we last tried to rehash. We
|
|
don't want to be continually trying to rehash.
|
|
***************************************************************************/
|
|
FINLINE FLMBOOL checkHashFailTime(
|
|
FLMUINT * puiHashFailTime)
|
|
{
|
|
if (*puiHashFailTime)
|
|
{
|
|
FLMUINT uiCurrTime = FLM_GET_TIMER();
|
|
|
|
if (FLM_ELAPSED_TIME( uiCurrTime, (*puiHashFailTime)) >=
|
|
gv_SFlmSysData.uiRehashAfterFailureBackoffTime)
|
|
{
|
|
*puiHashFailTime = 0;
|
|
return( TRUE);
|
|
}
|
|
else
|
|
{
|
|
return( FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return( TRUE);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
Desc: Cached item
|
|
******************************************************************************/
|
|
class F_CachedItem
|
|
{
|
|
public:
|
|
|
|
F_CachedItem()
|
|
{
|
|
m_pNextInGlobal = NULL;
|
|
m_pPrevInGlobal = NULL;
|
|
}
|
|
|
|
virtual ~F_CachedItem()
|
|
{
|
|
}
|
|
|
|
private:
|
|
|
|
F_CachedItem * m_pPrevInGlobal;
|
|
F_CachedItem * m_pNextInGlobal;
|
|
|
|
friend class F_CacheList;
|
|
friend class F_CachedBlock;
|
|
friend class F_Row;
|
|
friend class F_GlobalCacheMgr;
|
|
friend class F_BlockCacheMgr;
|
|
friend class F_RowCacheMgr;
|
|
friend class F_Database;
|
|
friend class F_Db;
|
|
friend class F_RowRelocator;
|
|
friend class F_ColumnDataRelocator;
|
|
friend class F_ColumnListRelocator;
|
|
friend class F_BlockRelocator;
|
|
};
|
|
|
|
/***************************************************************************
|
|
Desc: Object for keeping track of an MRU/LRU list of cached items (rows
|
|
or blocks)
|
|
***************************************************************************/
|
|
class F_CacheList
|
|
{
|
|
public:
|
|
|
|
F_CacheList()
|
|
{
|
|
m_pMRUItem = NULL;
|
|
m_pLRUItem = NULL;
|
|
m_pLastMRUItem = NULL;
|
|
}
|
|
|
|
~F_CacheList()
|
|
{
|
|
flmAssert( !m_pMRUItem);
|
|
flmAssert( !m_pLRUItem);
|
|
flmAssert( !m_pLastMRUItem);
|
|
}
|
|
|
|
// Link a cached item into the global list as the MRU item. This routine
|
|
// assumes that the cache mutex for managing this list
|
|
// has already been locked.
|
|
|
|
FINLINE void linkGlobalAsMRU(
|
|
F_CachedItem * pItem)
|
|
{
|
|
if ((pItem->m_pNextInGlobal = m_pMRUItem) != NULL)
|
|
{
|
|
pItem->m_pNextInGlobal->m_pPrevInGlobal = pItem;
|
|
}
|
|
else
|
|
{
|
|
m_pLRUItem = pItem;
|
|
m_pLastMRUItem = pItem;
|
|
}
|
|
|
|
pItem->m_pPrevInGlobal = NULL;
|
|
m_pMRUItem = pItem;
|
|
flmAssert( pItem != pItem->m_pPrevInGlobal);
|
|
flmAssert( pItem != pItem->m_pNextInGlobal);
|
|
}
|
|
|
|
// Link a cached item into the global list as the last MRU item.
|
|
// This routine assumes that the cache mutex for managing this list
|
|
// has already been locked.
|
|
|
|
FINLINE void linkGlobalAsLastMRU(
|
|
F_CachedItem * pItem)
|
|
{
|
|
if( !m_pLastMRUItem)
|
|
{
|
|
flmAssert( !m_pMRUItem);
|
|
linkGlobalAsMRU( pItem);
|
|
return;
|
|
}
|
|
|
|
flmAssert( m_pLastMRUItem);
|
|
|
|
if( m_pLastMRUItem->m_pNextInGlobal)
|
|
{
|
|
m_pLastMRUItem->m_pNextInGlobal->m_pPrevInGlobal = pItem;
|
|
pItem->m_pNextInGlobal = m_pLastMRUItem->m_pNextInGlobal;
|
|
}
|
|
else
|
|
{
|
|
flmAssert( m_pLRUItem == m_pLastMRUItem);
|
|
m_pLRUItem = pItem;
|
|
}
|
|
|
|
m_pLastMRUItem->m_pNextInGlobal = pItem;
|
|
|
|
pItem->m_pPrevInGlobal = m_pLastMRUItem;
|
|
m_pLastMRUItem = pItem;
|
|
|
|
flmAssert( pItem != pItem->m_pPrevInGlobal);
|
|
flmAssert( pItem != pItem->m_pNextInGlobal);
|
|
}
|
|
|
|
// Link a cached item into the global list as the LRU item. This routine
|
|
// assumes that the cache mutex for managing this list
|
|
// has already been locked.
|
|
|
|
FINLINE void linkGlobalAsLRU(
|
|
F_CachedItem * pItem)
|
|
{
|
|
if ((pItem->m_pPrevInGlobal = m_pLRUItem) != NULL)
|
|
{
|
|
pItem->m_pPrevInGlobal->m_pNextInGlobal = pItem;
|
|
}
|
|
else
|
|
{
|
|
flmAssert( !m_pMRUItem);
|
|
flmAssert( !m_pLastMRUItem);
|
|
|
|
m_pMRUItem = pItem;
|
|
m_pLastMRUItem = pItem;
|
|
}
|
|
|
|
pItem->m_pNextInGlobal = NULL;
|
|
m_pLRUItem = pItem;
|
|
|
|
flmAssert( pItem != pItem->m_pPrevInGlobal);
|
|
flmAssert( pItem != pItem->m_pNextInGlobal);
|
|
}
|
|
|
|
// Unlink a cached item from the global list. This routine
|
|
// assumes that the cache mutex for managing this list
|
|
// has already been locked.
|
|
|
|
FINLINE void unlinkGlobal(
|
|
F_CachedItem * pItem)
|
|
{
|
|
if( pItem == m_pLastMRUItem)
|
|
{
|
|
if( m_pLastMRUItem->m_pPrevInGlobal)
|
|
{
|
|
m_pLastMRUItem = m_pLastMRUItem->m_pPrevInGlobal;
|
|
}
|
|
else
|
|
{
|
|
m_pLastMRUItem = m_pLastMRUItem->m_pNextInGlobal;
|
|
}
|
|
}
|
|
|
|
if (pItem->m_pNextInGlobal)
|
|
{
|
|
flmAssert( pItem != m_pLRUItem);
|
|
|
|
pItem->m_pNextInGlobal->m_pPrevInGlobal = pItem->m_pPrevInGlobal;
|
|
}
|
|
else
|
|
{
|
|
m_pLRUItem = pItem->m_pPrevInGlobal;
|
|
}
|
|
|
|
if (pItem->m_pPrevInGlobal)
|
|
{
|
|
flmAssert( pItem != m_pMRUItem);
|
|
|
|
pItem->m_pPrevInGlobal->m_pNextInGlobal = pItem->m_pNextInGlobal;
|
|
}
|
|
else
|
|
{
|
|
m_pMRUItem = pItem->m_pNextInGlobal;
|
|
}
|
|
|
|
pItem->m_pNextInGlobal = NULL;
|
|
pItem->m_pPrevInGlobal = NULL;
|
|
}
|
|
|
|
// Moves a cached item one step closer to the MRU slot in the global list.
|
|
// This routine assumes that the cache mutex for managing this list
|
|
// has already been locked.
|
|
|
|
FINLINE void stepUpInGlobal(
|
|
F_CachedItem * pItem)
|
|
{
|
|
F_CachedItem * pPrevItem;
|
|
|
|
if ((pPrevItem = pItem->m_pPrevInGlobal) != NULL)
|
|
{
|
|
if( pItem == m_pLastMRUItem)
|
|
{
|
|
m_pLastMRUItem = m_pLastMRUItem->m_pPrevInGlobal;
|
|
}
|
|
|
|
if (pPrevItem->m_pPrevInGlobal)
|
|
{
|
|
pPrevItem->m_pPrevInGlobal->m_pNextInGlobal = pItem;
|
|
}
|
|
else
|
|
{
|
|
m_pMRUItem = pItem;
|
|
}
|
|
|
|
pItem->m_pPrevInGlobal = pPrevItem->m_pPrevInGlobal;
|
|
pPrevItem->m_pPrevInGlobal = pItem;
|
|
pPrevItem->m_pNextInGlobal = pItem->m_pNextInGlobal;
|
|
|
|
if (pItem->m_pNextInGlobal)
|
|
{
|
|
pItem->m_pNextInGlobal->m_pPrevInGlobal = pPrevItem;
|
|
}
|
|
else
|
|
{
|
|
m_pLRUItem = pPrevItem;
|
|
}
|
|
|
|
pItem->m_pNextInGlobal = pPrevItem;
|
|
}
|
|
}
|
|
|
|
private:
|
|
|
|
F_CachedItem * m_pMRUItem;
|
|
F_CachedItem * m_pLRUItem;
|
|
F_CachedItem * m_pLastMRUItem;
|
|
|
|
friend class F_CachedItem;
|
|
friend class F_RowCacheMgr;
|
|
friend class F_BlockCacheMgr;
|
|
friend class F_CachedBlock;
|
|
friend class F_RowRelocator;
|
|
friend class F_ColumnDataRelocator;
|
|
friend class F_ColumnListRelocator;
|
|
friend class F_BlockRelocator;
|
|
};
|
|
|
|
/***************************************************************************
|
|
Desc: Global cache manager for FLAIM-SQL.
|
|
***************************************************************************/
|
|
class F_GlobalCacheMgr : public F_Object
|
|
{
|
|
public:
|
|
F_GlobalCacheMgr();
|
|
|
|
~F_GlobalCacheMgr();
|
|
|
|
RCODE setup( void);
|
|
|
|
FINLINE void incrTotalBytes(
|
|
FLMUINT uiIncrAmount)
|
|
{
|
|
m_pSlabManager->incrementTotalBytesAllocated( uiIncrAmount);
|
|
}
|
|
|
|
FINLINE void decrTotalBytes(
|
|
FLMUINT uiDecrAmount)
|
|
{
|
|
m_pSlabManager->decrementTotalBytesAllocated( uiDecrAmount);
|
|
}
|
|
|
|
FINLINE FLMUINT totalBytes( void)
|
|
{
|
|
return( m_pSlabManager->totalBytesAllocated());
|
|
}
|
|
|
|
FINLINE FLMUINT availSlabs( void)
|
|
{
|
|
return( m_pSlabManager->availSlabs());
|
|
}
|
|
|
|
FINLINE FLMUINT allocatedSlabs( void)
|
|
{
|
|
return( m_pSlabManager->getTotalSlabs());
|
|
}
|
|
|
|
FINLINE FLMBOOL cacheOverLimit( void)
|
|
{
|
|
if( allocatedSlabs() > m_uiMaxSlabs)
|
|
{
|
|
return( TRUE);
|
|
}
|
|
|
|
return( FALSE);
|
|
}
|
|
|
|
RCODE setCacheLimit(
|
|
FLMUINT uiMaxCache,
|
|
FLMBOOL bPreallocateCache);
|
|
|
|
RCODE setDynamicMemoryLimit(
|
|
FLMUINT uiCacheAdjustPercent,
|
|
FLMUINT uiCacheAdjustMin,
|
|
FLMUINT uiCacheAdjustMax,
|
|
FLMUINT uiCacheAdjustMinToLeave);
|
|
|
|
RCODE setHardMemoryLimit(
|
|
FLMUINT uiPercent,
|
|
FLMBOOL bPercentOfAvail,
|
|
FLMUINT uiMin,
|
|
FLMUINT uiMax,
|
|
FLMUINT uiMinToLeave,
|
|
FLMBOOL bPreallocate);
|
|
|
|
void getCacheInfo(
|
|
SFLM_CACHE_INFO * pMemInfo);
|
|
|
|
RCODE adjustCache(
|
|
FLMUINT * puiCurrTime,
|
|
FLMUINT * puiLastCacheAdjustTime);
|
|
|
|
RCODE clearCache(
|
|
F_Db * pDb);
|
|
|
|
FINLINE void lockMutex( void)
|
|
{
|
|
f_mutexLock( m_hMutex);
|
|
}
|
|
|
|
FINLINE void unlockMutex( void)
|
|
{
|
|
f_mutexUnlock( m_hMutex);
|
|
}
|
|
|
|
private:
|
|
|
|
IF_SlabManager * m_pSlabManager;
|
|
FLMUINT m_uiMaxBytes;
|
|
FLMUINT m_uiMaxSlabs;
|
|
FLMBOOL m_bCachePreallocated;
|
|
FLMBOOL m_bDynamicCacheAdjust;
|
|
// Is cache to be dynamically adjusted?
|
|
FLMUINT m_uiCacheAdjustPercent;
|
|
// Percent of available memory to adjust to.
|
|
FLMUINT m_uiCacheAdjustMin;
|
|
// Minimum limit to adjust cache to.
|
|
FLMUINT m_uiCacheAdjustMax;
|
|
// Maximum limit to adjust cache to.
|
|
FLMUINT m_uiCacheAdjustMinToLeave;
|
|
// Minimum bytes to leave when adjusting cache.
|
|
FLMUINT m_uiCacheAdjustInterval;
|
|
// Interval for adjusting cache limit.
|
|
FLMUINT m_uiCacheCleanupInterval;
|
|
// Interval for cleaning up old things out of
|
|
// cache.
|
|
FLMUINT m_uiUnusedCleanupInterval;
|
|
// Interval for cleaning up unused structures
|
|
F_MUTEX m_hMutex; // Mutex to control access to global cache
|
|
// manager object.
|
|
|
|
friend class F_CachedItem;
|
|
friend class F_Row;
|
|
friend class F_CachedBlock;
|
|
friend class F_BlockCacheMgr;
|
|
friend class F_RowCacheMgr;
|
|
friend class F_Database;
|
|
friend class F_DbSystem;
|
|
};
|
|
|
|
/****************************************************************************
|
|
Desc: Class for moving cache blocks in cache.
|
|
****************************************************************************/
|
|
class F_BlockRelocator : public IF_Relocator
|
|
{
|
|
public:
|
|
|
|
F_BlockRelocator()
|
|
{
|
|
}
|
|
|
|
virtual ~F_BlockRelocator()
|
|
{
|
|
}
|
|
|
|
void FLMAPI relocate(
|
|
void * pvOldAlloc,
|
|
void * pvNewAlloc);
|
|
|
|
FLMBOOL FLMAPI canRelocate(
|
|
void * pvOldAlloc);
|
|
};
|
|
|
|
/****************************************************************************
|
|
Desc: This class manages block cache.
|
|
****************************************************************************/
|
|
class F_BlockCacheMgr : public F_Object
|
|
{
|
|
public:
|
|
F_BlockCacheMgr();
|
|
|
|
~F_BlockCacheMgr();
|
|
|
|
RCODE initCache( void);
|
|
|
|
void cleanupLRUCache( void);
|
|
|
|
void cleanupReplaceList( void);
|
|
|
|
void cleanupFreeCache( void);
|
|
|
|
void reduceReuseList( void);
|
|
|
|
RCODE reduceCache(
|
|
F_Db * pDb);
|
|
|
|
RCODE rehash( void);
|
|
|
|
RCODE allocBlock(
|
|
F_Db * pDb,
|
|
F_CachedBlock ** ppSCache);
|
|
|
|
// Returns a pointer to the correct entry in the block cache hash table for
|
|
// the given block address
|
|
|
|
FINLINE F_CachedBlock ** blockHash(
|
|
FLMUINT uiSigBitsInBlkSize,
|
|
FLMUINT uiBlkAddress
|
|
)
|
|
{
|
|
return( &m_ppHashBuckets[ (uiBlkAddress >>
|
|
uiSigBitsInBlkSize) & m_uiHashMask]);
|
|
}
|
|
|
|
FINLINE void defragmentMemory(
|
|
FLMBOOL bMutexLocked = FALSE)
|
|
{
|
|
if( !bMutexLocked)
|
|
{
|
|
f_mutexLock( gv_SFlmSysData.hBlockCacheMutex);
|
|
}
|
|
|
|
m_pBlockAllocator->defragmentMemory();
|
|
|
|
if( !bMutexLocked)
|
|
{
|
|
f_mutexUnlock( gv_SFlmSysData.hBlockCacheMutex);
|
|
}
|
|
}
|
|
|
|
private:
|
|
|
|
RCODE initHashTbl( void);
|
|
|
|
F_CacheList m_MRUList; // List of all block objects in MRU order.
|
|
F_CachedBlock * m_pMRUReplace; // Pointer to the MRU end of the list
|
|
// of cache items with no flags set.
|
|
F_CachedBlock * m_pLRUReplace; // Pointer to the LRU end of the list
|
|
// of cache items with no flags set.
|
|
F_CachedBlock * m_pFirstFree;
|
|
// Pointer to a linked list of cache
|
|
// blocks that need to be freed.
|
|
// Cache blocks in this list are no
|
|
// longer associated with a file and can
|
|
// be freed or re-used as needed. They
|
|
// are linked using the pNextInFile and
|
|
// pPrevInFile pointers.
|
|
F_CachedBlock * m_pLastFree;
|
|
// Pointer to a linked list of cache
|
|
// blocks that need to be freed.
|
|
// Cache blocks in this list are no
|
|
// longer associated with a file and can
|
|
// be freed or re-used as needed. They
|
|
// are linked using the pNextInFile and
|
|
// pPrevInFile pointers.
|
|
SFLM_CACHE_USAGE m_Usage; // Contains usage information.
|
|
FLMUINT m_uiFreeBytes; // Number of free bytes
|
|
FLMUINT m_uiFreeCount; // Number of free blocks
|
|
FLMUINT m_uiReplaceableCount;
|
|
// Number of blocks whose flags are 0
|
|
FLMUINT m_uiReplaceableBytes;
|
|
// Number of bytes belonging to blocks whose
|
|
// flags are 0
|
|
FLMBOOL m_bAutoCalcMaxDirty;
|
|
// Flag indicating we should automatically
|
|
// calculate maximum dirty cache.
|
|
FLMUINT m_uiMaxDirtyCache;
|
|
// Maximum cache that can be dirty.
|
|
FLMUINT m_uiLowDirtyCache;
|
|
// When maximum dirty cache is exceeded,
|
|
// threshhold it should be brought back
|
|
// under
|
|
FLMUINT m_uiTotalUses; // Total number of uses currently held
|
|
// on blocks in cache.
|
|
FLMUINT m_uiBlocksUsed;// Total number of blocks in cache that
|
|
// are being used.
|
|
FLMUINT m_uiPendingReads;
|
|
// Total reads currently pending.
|
|
FLMUINT m_uiIoWaits; // Number of times multiple threads
|
|
// were reading the same block from
|
|
// disk at the same time.
|
|
F_CachedBlock ** m_ppHashBuckets;// This is a pointer to a hash table that
|
|
// is used to find cache blocks. Each
|
|
// element in the table points to a
|
|
// linked list of F_CachedBlock objects that
|
|
// all hash to the same hash bucket.
|
|
FLMUINT m_uiNumBuckets;// This contains the number of buckets
|
|
// in the hash table.
|
|
FLMUINT m_uiHashFailTime;
|
|
// Last time we tried to rehash and
|
|
// failed. Want to wait before we
|
|
// retry again.
|
|
FLMUINT m_uiHashMask; // Bits that are significant
|
|
// for the number of hash buckets.
|
|
IF_MultiAlloc * m_pBlockAllocator;
|
|
// Fixed size allocators for cache blocks
|
|
F_BlockRelocator m_blockRelocator;
|
|
// Relocator for cache blocks
|
|
FLMBOOL m_bReduceInProgress;
|
|
#ifdef FLM_DEBUG
|
|
FLMBOOL m_bDebug; // Enables checksumming and cache use
|
|
// monitoring. Only available when
|
|
// debug is compiled in.
|
|
#endif
|
|
|
|
friend class F_CachedBlock;
|
|
friend class F_GlobalCacheMgr;
|
|
friend class F_Database;
|
|
friend class F_Db;
|
|
friend class F_DbSystem;
|
|
friend class F_BlockRelocator;
|
|
};
|
|
|
|
#ifdef FLM_DEBUG
|
|
/****************************************************************************
|
|
Struct: SCACHE_USE (Cache Block Use)
|
|
Desc: This is a debug only structure that is used to keep track of the
|
|
threads that are currently using a block.
|
|
****************************************************************************/
|
|
typedef struct SCache_Use
|
|
{
|
|
SCache_Use * pNext; // Pointer to next SCACHE_USE structure in
|
|
// the list.
|
|
FLMUINT uiThreadId; // Thread ID of thread using the block.
|
|
FLMUINT uiUseCount; // Use count for this particular thread.
|
|
} SCACHE_USE;
|
|
#endif
|
|
|
|
// Flags for m_ui16Flags field in F_CachedBlock
|
|
|
|
#define CA_DIRTY 0x0001
|
|
// This bit indicates that the block is
|
|
// dirty and needs to be flushed to disk.
|
|
// NOTE: For 3.x files, this bit may remain
|
|
// set on prior versions of blocks until the
|
|
// current transaction commits.
|
|
#define CA_WRITE_INHIBIT 0x0002
|
|
// Must not write block until use count
|
|
// goes to zero. NOTE: Can ignore when
|
|
// in the checkpoint thread.
|
|
#define CA_READ_PENDING 0x0004
|
|
// This bit indicates that the block is
|
|
// currently being read in from disk.
|
|
#define CA_WRITE_TO_LOG 0x0008
|
|
// This bit indicates that this version of
|
|
// the block should be written to the
|
|
// rollback log before being replaced.
|
|
// During an update transaction, the first
|
|
// time a block is updated, FLAIM will
|
|
// create a new version of the block and
|
|
// insert it into cache. The prior version
|
|
// of the block is marked with this flag
|
|
// to indicate that it needs to be written
|
|
// to the log before it can be replaced.
|
|
#define CA_LOG_FOR_CP 0x0010
|
|
// This bit indicates that this version of
|
|
// the block needs to be logged to the
|
|
// physical rollback in order to restore
|
|
// the last checkpoint. This is only
|
|
// applicable to 3.x files.
|
|
#define CA_WAS_DIRTY 0x0020
|
|
// This bit indicates that this version of
|
|
// the block was dirty before the newer
|
|
// version of the block was created.
|
|
// Its dirty state should be restored if
|
|
// the current transaction aborts. This
|
|
// flag is only used for 3.x files.
|
|
#define CA_WRITE_PENDING 0x0040
|
|
// This bit indicates that a block is in
|
|
// the process of being written out to
|
|
// disk.
|
|
#define CA_IN_WRITE_PENDING_LIST 0x0080
|
|
// This bit indicates that a block is in
|
|
// the write pending list.
|
|
#define CA_FREE 0x0100
|
|
// The block has been linked to the free
|
|
// list (and unlinked from all other lists)
|
|
#define CA_IN_FILE_LOG_LIST 0x0200
|
|
// Block is in the list of blocks that may
|
|
// have one or more versions that need to
|
|
// be logged
|
|
#define CA_IN_NEW_LIST 0x0400
|
|
// Dirty block that is beyond the last CP EOF
|
|
#define CA_DUMMY_FLAG 0x0800
|
|
// Used to prevent blocks from being linked
|
|
// into the replace list in cases where
|
|
// they will be removed immediately (because
|
|
// a bit is going to being set)
|
|
|
|
/****************************************************************************
|
|
Desc: This is the header structure for a cached data block.
|
|
****************************************************************************/
|
|
class F_CachedBlock : public F_CachedItem
|
|
{
|
|
public:
|
|
F_CachedBlock(
|
|
FLMUINT uiBlockSize);
|
|
|
|
~F_CachedBlock();
|
|
|
|
FINLINE FLMUINT memSize( void)
|
|
{
|
|
return( gv_SFlmSysData.pBlockCacheMgr->m_pBlockAllocator->getTrueSize(
|
|
(FLMBYTE *)this));
|
|
}
|
|
|
|
FINLINE FLMUINT blkAddress( void)
|
|
{
|
|
return( m_uiBlkAddress);
|
|
}
|
|
|
|
FINLINE F_BLK_HDR * getBlockPtr( void)
|
|
{
|
|
return( m_pBlkHdr);
|
|
}
|
|
|
|
FINLINE F_Database * getDatabase( void)
|
|
{
|
|
return( m_pDatabase);
|
|
}
|
|
|
|
FINLINE FLMUINT16 getModeFlags( void)
|
|
{
|
|
return( m_ui16Flags);
|
|
}
|
|
|
|
FINLINE FLMUINT getUseCount( void)
|
|
{
|
|
return( m_uiUseCount);
|
|
}
|
|
|
|
// Gets the prior image block address from the block header.
|
|
// NOTE: This function assumes that the block cache mutex is locked.
|
|
|
|
FINLINE FLMUINT getPriorImageAddress( void)
|
|
{
|
|
return( (FLMUINT)m_pBlkHdr->ui32PriorBlkImgAddr);
|
|
}
|
|
|
|
// Gets the transaction ID from the block header. NOTE: This function
|
|
// assumes that the block cache mutex is locked.
|
|
|
|
FINLINE FLMUINT64 getLowTransID( void)
|
|
{
|
|
return( m_pBlkHdr->ui64TransID);
|
|
}
|
|
|
|
// Set the high transaction ID for a cache block.
|
|
// NOTE: This function assumes that the block cache mutex is locked.
|
|
|
|
FINLINE void setTransID(
|
|
FLMUINT64 ui64NewTransID)
|
|
{
|
|
if (m_ui64HighTransID == FLM_MAX_UINT64 && ui64NewTransID != FLM_MAX_UINT64)
|
|
{
|
|
gv_SFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerBytes += memSize();
|
|
gv_SFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerCount++;
|
|
}
|
|
else if (m_ui64HighTransID != FLM_MAX_UINT64 && ui64NewTransID == FLM_MAX_UINT64)
|
|
{
|
|
FLMUINT uiSize = memSize();
|
|
|
|
flmAssert( gv_SFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerBytes >= uiSize);
|
|
gv_SFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerBytes -= uiSize;
|
|
flmAssert( gv_SFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerCount);
|
|
gv_SFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerCount--;
|
|
}
|
|
|
|
m_ui64HighTransID = ui64NewTransID;
|
|
}
|
|
|
|
// Determines if a cache block is needed by a read transaction.
|
|
FINLINE FLMBOOL neededByReadTrans( void)
|
|
{
|
|
return( m_pDatabase->neededByReadTrans( getLowTransID(),
|
|
m_ui64HighTransID));
|
|
}
|
|
|
|
// Link a cache block into the replace list as the MRU item. This routine
|
|
// assumes that the block cache mutex has already been locked.
|
|
FINLINE void linkToReplaceListAsMRU( void)
|
|
{
|
|
flmAssert( !m_ui16Flags);
|
|
|
|
if ((m_pNextInReplaceList =
|
|
gv_SFlmSysData.pBlockCacheMgr->m_pMRUReplace) != NULL)
|
|
{
|
|
m_pNextInReplaceList->m_pPrevInReplaceList = this;
|
|
}
|
|
else
|
|
{
|
|
gv_SFlmSysData.pBlockCacheMgr->m_pLRUReplace = this;
|
|
}
|
|
|
|
m_pPrevInReplaceList = NULL;
|
|
gv_SFlmSysData.pBlockCacheMgr->m_pMRUReplace = this;
|
|
gv_SFlmSysData.pBlockCacheMgr->m_uiReplaceableCount++;
|
|
gv_SFlmSysData.pBlockCacheMgr->m_uiReplaceableBytes += memSize();
|
|
}
|
|
|
|
// Link a cache block into the replace list as the LRU item. This routine
|
|
// assumes that the block cache mutex has already been locked.
|
|
FINLINE void linkToReplaceListAsLRU( void)
|
|
{
|
|
flmAssert( !m_ui16Flags);
|
|
|
|
if ((m_pPrevInReplaceList = gv_SFlmSysData.pBlockCacheMgr->m_pLRUReplace) != NULL)
|
|
{
|
|
m_pPrevInReplaceList->m_pNextInReplaceList = this;
|
|
}
|
|
else
|
|
{
|
|
gv_SFlmSysData.pBlockCacheMgr->m_pMRUReplace = this;
|
|
}
|
|
|
|
m_pNextInReplaceList = NULL;
|
|
gv_SFlmSysData.pBlockCacheMgr->m_pLRUReplace = this;
|
|
gv_SFlmSysData.pBlockCacheMgr->m_uiReplaceableCount++;
|
|
gv_SFlmSysData.pBlockCacheMgr->m_uiReplaceableBytes += memSize();
|
|
}
|
|
|
|
// Moves a block one step closer to the MRU slot in the replace list.
|
|
// This routine assumes that the block cache mutex has already been locked.
|
|
FINLINE void stepUpInReplaceList( void)
|
|
{
|
|
F_CachedBlock * pPrevSCache;
|
|
|
|
flmAssert( !m_ui16Flags);
|
|
|
|
if( (pPrevSCache = m_pPrevInReplaceList) != NULL)
|
|
{
|
|
if( pPrevSCache->m_pPrevInReplaceList)
|
|
{
|
|
pPrevSCache->m_pPrevInReplaceList->m_pNextInReplaceList = this;
|
|
}
|
|
else
|
|
{
|
|
gv_SFlmSysData.pBlockCacheMgr->m_pMRUReplace = this;
|
|
}
|
|
|
|
m_pPrevInReplaceList = pPrevSCache->m_pPrevInReplaceList;
|
|
|
|
pPrevSCache->m_pPrevInReplaceList = this;
|
|
pPrevSCache->m_pNextInReplaceList = m_pNextInReplaceList;
|
|
|
|
if( m_pNextInReplaceList)
|
|
{
|
|
m_pNextInReplaceList->m_pPrevInReplaceList = pPrevSCache;
|
|
}
|
|
else
|
|
{
|
|
gv_SFlmSysData.pBlockCacheMgr->m_pLRUReplace = pPrevSCache;
|
|
}
|
|
|
|
m_pNextInReplaceList = pPrevSCache;
|
|
}
|
|
}
|
|
|
|
// Clears the passed-in flags from the F_CachedBlock object
|
|
// This routine assumes that the block cache mutex is locked.
|
|
FINLINE void clearFlags(
|
|
FLMUINT16 ui16FlagsToClear)
|
|
{
|
|
if( m_ui16Flags)
|
|
{
|
|
if( (m_ui16Flags &= ~ui16FlagsToClear) == 0)
|
|
{
|
|
if( !m_pPrevInGlobal ||
|
|
m_ui64HighTransID == ~((FLMUINT64)0) ||
|
|
neededByReadTrans())
|
|
{
|
|
linkToReplaceListAsMRU();
|
|
}
|
|
else
|
|
{
|
|
linkToReplaceListAsLRU();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sets the passed-in flags on the object
|
|
// This routine assumes that the block cache mutex is locked.
|
|
|
|
FINLINE void setFlags(
|
|
FLMUINT16 ui16FlagsToSet)
|
|
{
|
|
flmAssert( ui16FlagsToSet);
|
|
|
|
if( !m_ui16Flags)
|
|
{
|
|
unlinkFromReplaceList();
|
|
}
|
|
|
|
m_ui16Flags |= ui16FlagsToSet;
|
|
}
|
|
|
|
// Set the dirty flag on a cache block.
|
|
// This routine assumes that the block cache mutex is locked.
|
|
|
|
FINLINE void setDirtyFlag(
|
|
F_Database * pDatabase)
|
|
{
|
|
flmAssert( !(m_ui16Flags &
|
|
(CA_DIRTY | CA_WRITE_PENDING | CA_IN_FILE_LOG_LIST | CA_IN_NEW_LIST)));
|
|
setFlags( CA_DIRTY);
|
|
pDatabase->incrementDirtyCacheCount();
|
|
}
|
|
|
|
// Unset the dirty flag on a cache block.
|
|
// This routine assumes that the block cache mutex is locked.
|
|
FINLINE void unsetDirtyFlag( void)
|
|
{
|
|
flmAssert( m_ui16Flags & CA_DIRTY);
|
|
flmAssert( m_pDatabase->getDirtyCacheCount());
|
|
|
|
if (m_ui16Flags & CA_IN_FILE_LOG_LIST)
|
|
{
|
|
unlinkFromLogList();
|
|
}
|
|
else if (m_ui16Flags & CA_IN_NEW_LIST)
|
|
{
|
|
unlinkFromNewList();
|
|
}
|
|
|
|
clearFlags( CA_DIRTY);
|
|
m_pDatabase->decrementDirtyCacheCount();
|
|
}
|
|
|
|
FINLINE FLMUINT getBlkSize( void)
|
|
{
|
|
return( (FLMUINT)m_ui16BlkSize);
|
|
}
|
|
|
|
#ifdef FLM_DBG_LOG
|
|
void logFlgChange(
|
|
FLMUINT16 ui16OldFlags,
|
|
char cPlace);
|
|
#endif
|
|
|
|
void linkToLogList( void);
|
|
|
|
void unlinkFromLogList( void);
|
|
|
|
void linkToNewList( void);
|
|
|
|
void unlinkFromNewList( void);
|
|
|
|
void linkToDatabase(
|
|
F_Database * pDatabase);
|
|
|
|
void unlinkFromDatabase( void);
|
|
|
|
void unlinkFromTransLogList( void);
|
|
|
|
// Increment the use count on a cache block for a particular
|
|
// thread. NOTE: This routine assumes that the block cache mutex
|
|
// is locked.
|
|
FINLINE void useForThread(
|
|
#ifdef FLM_DEBUG
|
|
FLMUINT uiThreadId)
|
|
#else
|
|
FLMUINT) // uiThreadId)
|
|
#endif
|
|
{
|
|
|
|
#ifdef FLM_DEBUG
|
|
if (m_pUseList ||
|
|
(gv_SFlmSysData.pBlockCacheMgr->m_bDebug && !m_uiUseCount))
|
|
{
|
|
dbgUseForThread( uiThreadId);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (!m_uiUseCount)
|
|
{
|
|
gv_SFlmSysData.pBlockCacheMgr->m_uiBlocksUsed++;
|
|
}
|
|
m_uiUseCount++;
|
|
gv_SFlmSysData.pBlockCacheMgr->m_uiTotalUses++;
|
|
}
|
|
}
|
|
|
|
// Decrement the use count on a cache block for a particular
|
|
// thread. NOTE: This routine assumes that the block cache mutex
|
|
// is locked.
|
|
FINLINE void releaseForThread( void)
|
|
{
|
|
if (!m_uiUseCount)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#ifdef FLM_DEBUG
|
|
if (m_pUseList)
|
|
{
|
|
dbgReleaseForThread();
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
|
|
#ifdef FLM_DEBUG
|
|
|
|
// If count is one, it will be decremented to zero.
|
|
|
|
if (m_uiUseCount == 1)
|
|
{
|
|
m_uiChecksum = computeChecksum();
|
|
}
|
|
#endif
|
|
|
|
m_uiUseCount--;
|
|
gv_SFlmSysData.pBlockCacheMgr->m_uiTotalUses--;
|
|
if (!m_uiUseCount)
|
|
{
|
|
gv_SFlmSysData.pBlockCacheMgr->m_uiBlocksUsed--;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tests if a block can be freed from cache.
|
|
// NOTE: This routine assumes that the block cache mutex is locked.
|
|
FINLINE FLMBOOL canBeFreed( void)
|
|
{
|
|
if (!m_uiUseCount && !m_ui16Flags)
|
|
{
|
|
F_CachedBlock * pNewerSCache = m_pPrevInVersionList;
|
|
|
|
// The following code is attempting to ensure that newer
|
|
// versions of the block have had the prior block address
|
|
// properly transferred to them from an older version of
|
|
// the block. If not, we cannot remove the current version
|
|
// of the block (pointed to by pSCache), because it is
|
|
// the older version that needs to be logged in order for
|
|
// the prior block address to be properly transferred to
|
|
// the newer version of the block.
|
|
|
|
// If there is no newer version of the block, we can remove
|
|
// this block, because it means that there was at one point
|
|
// in time a newer version, the prior block address was
|
|
// safely transferred - otherwise, the newer version would
|
|
// still be in cache.
|
|
|
|
// If there is a newer version of the block, but it is in the
|
|
// process of being read in from disk (CA_READ_PENDING bit is
|
|
// set), we can know that the prior block address has been
|
|
// properly transferred to the block being read in.
|
|
// Explanation: If the CA_READ_PENDING bit it set, the block
|
|
// had to have been written out to disk at some prior time. The
|
|
// rules for writing out a block to disk are such that it is
|
|
// impossible for a block to be written out without having a
|
|
// pointer to some prior version of the block. The only
|
|
// exception to this is a newly created block - but in that
|
|
// case, the block does not need to have a prior version pointer
|
|
// - because there are none!
|
|
// This assertion is obvious for a version of a block that is
|
|
// being read from the rollback log - it would be impossible
|
|
// to be reading such a block from the rollback log if it hadn't
|
|
// been part of a version chain! As for the current version of a
|
|
// block, it cannot be written out and removed from cache without
|
|
// having a pointer to the chain of older versions that may still
|
|
// be needed (by a read transactions, for rollback, or to recover
|
|
// a checkpoint).
|
|
|
|
// NOTE: Although we know that a block being read in from disk
|
|
// has to already have a prior block address, we cannot just
|
|
// look at the block header, because it is being read in from
|
|
// disk and the prior block address is not yet there. Usually,
|
|
// it will still be zeroes - making it look as though the block
|
|
// does not have a prior block address when, in fact, it does.
|
|
// Thus, we look at the CA_READ_PENDING bit first. If that
|
|
// is not set, we can safely look at the prior block address.
|
|
|
|
// Note also that even if there is a newer block that doesn't
|
|
// have a prior block address, we may still be able to remove
|
|
// the current block (pSCache) if it is not needed by any
|
|
// read transactions.
|
|
|
|
if (!pNewerSCache ||
|
|
(pNewerSCache->m_ui16Flags & CA_READ_PENDING) ||
|
|
pNewerSCache->getPriorImageAddress() != 0 ||
|
|
!m_pDatabase ||
|
|
!neededByReadTrans())
|
|
{
|
|
return( TRUE);
|
|
}
|
|
}
|
|
return( FALSE);
|
|
}
|
|
|
|
void linkToFreeList(
|
|
FLMUINT uiFreeTime);
|
|
|
|
void unlinkFromFreeList( void);
|
|
|
|
void * operator new(
|
|
FLMSIZET uiSize,
|
|
FLMUINT uiBlockSize)
|
|
#if !defined( FLM_NLM)
|
|
throw()
|
|
#endif
|
|
;
|
|
|
|
void * operator new(
|
|
FLMSIZET uiSize)
|
|
#if !defined( FLM_NLM)
|
|
throw()
|
|
#endif
|
|
;
|
|
|
|
void * operator new(
|
|
FLMSIZET uiSize,
|
|
const char * pszFile,
|
|
int iLine)
|
|
#if !defined( FLM_NLM)
|
|
throw()
|
|
#endif
|
|
;
|
|
|
|
void * operator new[](
|
|
FLMSIZET uiSize)
|
|
#if !defined( FLM_NLM)
|
|
throw()
|
|
#endif
|
|
;
|
|
|
|
void * operator new[](
|
|
FLMSIZET uiSize,
|
|
const char * pszFile,
|
|
int iLine)
|
|
#if !defined( FLM_NLM)
|
|
throw()
|
|
#endif
|
|
;
|
|
|
|
void operator delete(
|
|
void * ptr);
|
|
|
|
#if !defined( __WATCOMC__) && !defined( FLM_SOLARIS)
|
|
void operator delete(
|
|
void * ptr,
|
|
FLMSIZET uiSize,
|
|
const char * pszFileName,
|
|
int iLine);
|
|
#endif
|
|
|
|
void operator delete[](
|
|
void * ptr);
|
|
|
|
#if !defined( __WATCOMC__) && !defined( FLM_SOLARIS)
|
|
void operator delete(
|
|
void * ptr,
|
|
FLMUINT uiBlockSize,
|
|
FLMBOOL bAllocMutexLocked);
|
|
#endif
|
|
|
|
|
|
#if !defined( __WATCOMC__) && !defined( FLM_SOLARIS)
|
|
void operator delete(
|
|
void * ptr,
|
|
const char * file,
|
|
int line);
|
|
#endif
|
|
|
|
#if !defined( __WATCOMC__) && !defined( FLM_SOLARIS)
|
|
void operator delete[](
|
|
void * ptr,
|
|
const char * pszFileName,
|
|
int iLineNum);
|
|
#endif
|
|
|
|
private:
|
|
|
|
void unlinkFromReplaceList( void);
|
|
|
|
#ifdef FLM_DEBUG
|
|
void dbgUseForThread(
|
|
FLMUINT uiThreadId);
|
|
|
|
void dbgReleaseForThread( void);
|
|
|
|
FLMUINT computeChecksum( void);
|
|
#endif
|
|
|
|
#ifdef SCACHE_LINK_CHECKING
|
|
void verifyCache(
|
|
int iPlace);
|
|
#else
|
|
FINLINE void verifyCache(
|
|
int)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
static void FLMAPI objectAllocInit(
|
|
void * pvAlloc,
|
|
FLMUINT uiSize);
|
|
|
|
// Link a cached block into the global list as the MRU item. This routine
|
|
// assumes that the block cache mutex has already been locked.
|
|
|
|
FINLINE void linkToGlobalListAsMRU( void)
|
|
{
|
|
if( (m_pBlkHdr->ui8BlkType & BT_FREE) ||
|
|
(m_pBlkHdr->ui8BlkType & BT_LEAF) ||
|
|
(m_pBlkHdr->ui8BlkType & BT_LEAF_DATA) ||
|
|
(m_pBlkHdr->ui8BlkType & BT_DATA_ONLY))
|
|
{
|
|
gv_SFlmSysData.pBlockCacheMgr->m_MRUList.linkGlobalAsLastMRU(
|
|
(F_CachedItem *)this);
|
|
}
|
|
else
|
|
{
|
|
gv_SFlmSysData.pBlockCacheMgr->m_MRUList.linkGlobalAsMRU(
|
|
(F_CachedItem *)this);
|
|
}
|
|
|
|
if( !m_ui16Flags)
|
|
{
|
|
linkToReplaceListAsMRU();
|
|
}
|
|
}
|
|
|
|
// Link a cached block into the global list as the LRU item. This routine
|
|
// assumes that the block cache mutex has already been locked.
|
|
|
|
FINLINE void linkToGlobalListAsLRU( void)
|
|
{
|
|
if( (m_pBlkHdr->ui8BlkType & BT_FREE) ||
|
|
(m_pBlkHdr->ui8BlkType & BT_LEAF) ||
|
|
(m_pBlkHdr->ui8BlkType & BT_LEAF_DATA) ||
|
|
(m_pBlkHdr->ui8BlkType & BT_DATA_ONLY))
|
|
{
|
|
gv_SFlmSysData.pBlockCacheMgr->m_MRUList.linkGlobalAsLRU(
|
|
(F_CachedItem *)this);
|
|
}
|
|
else
|
|
{
|
|
gv_SFlmSysData.pBlockCacheMgr->m_MRUList.linkGlobalAsLastMRU(
|
|
(F_CachedItem *)this);
|
|
}
|
|
|
|
if( !m_ui16Flags)
|
|
{
|
|
linkToReplaceListAsLRU();
|
|
}
|
|
}
|
|
|
|
// Unlink a cache block from the global list. This routine
|
|
// assumes that the block cache mutex has already been locked.
|
|
|
|
FINLINE void unlinkFromGlobalList( void)
|
|
{
|
|
gv_SFlmSysData.pBlockCacheMgr->m_MRUList.unlinkGlobal(
|
|
(F_CachedItem *)this);
|
|
if( !m_ui16Flags)
|
|
{
|
|
unlinkFromReplaceList();
|
|
}
|
|
}
|
|
|
|
// Moves a block one step closer to the MRU slot in the global list. This
|
|
// routine assumes that the block cache mutex has already been locked.
|
|
|
|
FINLINE void stepUpInGlobalList( void)
|
|
{
|
|
gv_SFlmSysData.pBlockCacheMgr->m_MRUList.stepUpInGlobal(
|
|
(F_CachedItem *)this);
|
|
if( !m_ui16Flags)
|
|
{
|
|
stepUpInReplaceList();
|
|
}
|
|
}
|
|
|
|
#ifdef SCACHE_LINK_CHECKING
|
|
void checkHashLinks(
|
|
F_CachedBlock ** ppSCacheBucket);
|
|
|
|
void checkHashUnlinks(
|
|
F_CachedBlock ** ppSCacheBucket);
|
|
#endif
|
|
|
|
// Link a cache block to its hash bucket. This routine assumes
|
|
// that the block cache mutex has already been locked.
|
|
FINLINE void linkToHashBucket(
|
|
F_CachedBlock ** ppSCacheBucket)
|
|
{
|
|
#ifdef SCACHE_LINK_CHECKING
|
|
checkHashLinks( ppSCacheBucket);
|
|
#endif
|
|
m_pPrevInHashBucket = NULL;
|
|
if ((m_pNextInHashBucket = *ppSCacheBucket) != NULL)
|
|
{
|
|
m_pNextInHashBucket->m_pPrevInHashBucket = this;
|
|
}
|
|
|
|
*ppSCacheBucket = this;
|
|
}
|
|
|
|
// Unlink a cache block from its hash bucket. This routine assumes
|
|
// that the block cache mutex has already been locked.
|
|
FINLINE void unlinkFromHashBucket(
|
|
F_CachedBlock ** ppSCacheBucket)
|
|
{
|
|
#ifdef SCACHE_LINK_CHECKING
|
|
checkHashUnlinks( ppSCacheBucket);
|
|
#endif
|
|
|
|
// Make sure it is not in the list of log blocks.
|
|
|
|
flmAssert( !(m_ui16Flags & CA_WRITE_TO_LOG));
|
|
|
|
if (m_pNextInHashBucket)
|
|
{
|
|
m_pNextInHashBucket->m_pPrevInHashBucket = m_pPrevInHashBucket;
|
|
}
|
|
|
|
if (m_pPrevInHashBucket)
|
|
{
|
|
m_pPrevInHashBucket->m_pNextInHashBucket = m_pNextInHashBucket;
|
|
}
|
|
else
|
|
{
|
|
*ppSCacheBucket = m_pNextInHashBucket;
|
|
}
|
|
|
|
m_pNextInHashBucket = NULL;
|
|
m_pPrevInHashBucket = NULL;
|
|
}
|
|
|
|
void unlinkCache(
|
|
FLMBOOL bFreeIt,
|
|
RCODE NotifyRc);
|
|
|
|
void savePrevBlkAddress( void);
|
|
|
|
F_CachedBlock * m_pPrevInDatabase; // This is a pointer to the previous block
|
|
// in the linked list of blocks that are
|
|
// in the same database.
|
|
F_CachedBlock * m_pNextInDatabase; // This is a pointer to the next block in
|
|
// the linked list of blocks that are in
|
|
// the same database.
|
|
F_BLK_HDR * m_pBlkHdr; // Pointer to this block's header and data.
|
|
F_Database * m_pDatabase; // Pointer to the database this data block
|
|
// belongs to.
|
|
FLMUINT m_uiBlkAddress; // Block address.
|
|
F_CachedBlock * m_pNextInReplaceList;// This is a pointer to the next block in
|
|
// the global linked list of cache blocks
|
|
// that have a flags value of zero.
|
|
F_CachedBlock * m_pPrevInReplaceList;// This is a pointer to the previous block in
|
|
// the global linked list of cache blocks
|
|
// that have a flags value of zero.
|
|
F_CachedBlock * m_pPrevInHashBucket; // This is a pointer to the previous block
|
|
// in the linked list of blocks that are
|
|
// in the same hash bucket.
|
|
F_CachedBlock * m_pNextInHashBucket; // This is a pointer to the next block in
|
|
// the linked list of blocks that are in
|
|
// the same hash bucket.
|
|
F_CachedBlock * m_pPrevInVersionList;// This is a pointer to the previous block
|
|
// in the linked list of blocks that are
|
|
// all just different versions of the
|
|
// same block. The previous block is a
|
|
// more recent version of the block.
|
|
F_CachedBlock * m_pNextInVersionList;// This is a pointer to the next block in
|
|
// the linked list of blocks that are all
|
|
// just different versions of the same
|
|
// block. The next block is an older
|
|
// version of the block.
|
|
FNOTIFY * m_pNotifyList; // This is a pointer to a list of threads
|
|
// that want to be notified when a pending
|
|
// I/O is complete. This pointer is only
|
|
// non-null if the block is currently being
|
|
// read from disk and there are multiple
|
|
// threads all waiting for the block to
|
|
// be read in.
|
|
FLMUINT64 m_ui64HighTransID; // This indicates the highest known moment
|
|
// in the file's update history when this
|
|
// version of the block was the active
|
|
// block.
|
|
// A block's low transaction ID and high
|
|
// transaction ID indicate a span of
|
|
// transactions where this version of the
|
|
// block was the active version of the
|
|
// block.
|
|
FLMUINT m_uiUseCount; // Number of times this block has been
|
|
// retrieved for use by threads. A use
|
|
// count of zero indicates that no thread
|
|
// is currently using the block. Note that
|
|
// a block cannot be replaced when its use
|
|
// count is non-zero.
|
|
FLMUINT16 m_ui16Flags; // This is a set of flags for the block
|
|
// that indicate various things about the
|
|
// block's current state.
|
|
FLMUINT16 m_ui16BlkSize; // Block size
|
|
FLMBOOL m_bCanRelocate; // Can the block be moved in memory
|
|
|
|
// NOTE: Keep debug items at the END of the structure.
|
|
|
|
#ifdef FLM_DEBUG
|
|
FLMUINT m_uiChecksum; // Checksum for the block and header.
|
|
SCACHE_USE * m_pUseList; // This is a pointer to a list of threads
|
|
// that are currently using the block.
|
|
#endif
|
|
friend class F_BlockCacheMgr;
|
|
friend class F_GlobalCacheMgr;
|
|
friend class F_Database;
|
|
friend class F_DbSystem;
|
|
friend class F_Db;
|
|
friend class F_Btree;
|
|
friend class F_BTreeInfo;
|
|
friend class F_BlockRelocator;
|
|
};
|
|
|
|
/****************************************************************************
|
|
Desc: Class for moving rows in cache.
|
|
****************************************************************************/
|
|
class F_RowRelocator : public IF_Relocator
|
|
{
|
|
public:
|
|
|
|
F_RowRelocator()
|
|
{
|
|
}
|
|
|
|
virtual ~F_RowRelocator()
|
|
{
|
|
}
|
|
|
|
void FLMAPI relocate(
|
|
void * pvOldAlloc,
|
|
void * pvNewAlloc);
|
|
|
|
FLMBOOL FLMAPI canRelocate(
|
|
void * pvOldAlloc);
|
|
};
|
|
|
|
/****************************************************************************
|
|
Desc: Class for moving row data buffers in cache.
|
|
****************************************************************************/
|
|
class F_ColumnDataRelocator : public IF_Relocator
|
|
{
|
|
public:
|
|
|
|
F_ColumnDataRelocator()
|
|
{
|
|
}
|
|
|
|
virtual ~F_ColumnDataRelocator()
|
|
{
|
|
}
|
|
|
|
void FLMAPI relocate(
|
|
void * pvOldAlloc,
|
|
void * pvNewAlloc);
|
|
|
|
FLMBOOL FLMAPI canRelocate(
|
|
void * pvOldAlloc);
|
|
};
|
|
|
|
/****************************************************************************
|
|
Desc: Class for moving row column lists in cache.
|
|
****************************************************************************/
|
|
class F_ColumnListRelocator : public IF_Relocator
|
|
{
|
|
public:
|
|
|
|
F_ColumnListRelocator()
|
|
{
|
|
}
|
|
|
|
virtual ~F_ColumnListRelocator()
|
|
{
|
|
}
|
|
|
|
void FLMAPI relocate(
|
|
void * pvOldAlloc,
|
|
void * pvNewAlloc);
|
|
|
|
FLMBOOL FLMAPI canRelocate(
|
|
void * pvOldAlloc);
|
|
};
|
|
|
|
/****************************************************************************
|
|
Desc: This class is used to control the row cache.
|
|
****************************************************************************/
|
|
class F_RowCacheMgr : public F_Object
|
|
{
|
|
public:
|
|
F_RowCacheMgr();
|
|
|
|
~F_RowCacheMgr();
|
|
|
|
RCODE initCache( void);
|
|
|
|
void cleanupOldCache( void);
|
|
|
|
void cleanupPurgedCache( void);
|
|
|
|
void reduceCache( void);
|
|
|
|
FINLINE void setDebugMode(
|
|
FLMBOOL bDebug)
|
|
{
|
|
#ifdef FLM_DEBUG
|
|
m_bDebug = bDebug;
|
|
#else
|
|
(void)bDebug;
|
|
#endif
|
|
}
|
|
|
|
RCODE allocRow(
|
|
F_Row ** ppRow,
|
|
FLMBOOL bMutexLocked);
|
|
|
|
RCODE retrieveRow(
|
|
F_Db * pDb,
|
|
FLMUINT uiTableNum,
|
|
FLMUINT64 ui64RowId,
|
|
F_Row ** ppRow);
|
|
|
|
RCODE createRow(
|
|
F_Db * pDb,
|
|
FLMUINT uiTableNum,
|
|
F_Row ** ppRow);
|
|
|
|
RCODE makeWriteCopy(
|
|
F_Db * pDb,
|
|
F_Row ** ppRow);
|
|
|
|
void removeRow(
|
|
F_Db * pDb,
|
|
F_Row * pRow,
|
|
FLMBOOL bDecrementUseCount,
|
|
FLMBOOL bMutexLocked = FALSE);
|
|
|
|
void removeRow(
|
|
F_Db * pDb,
|
|
FLMUINT uiTableNum,
|
|
FLMUINT64 ui64RowId);
|
|
|
|
FINLINE void defragmentMemory(
|
|
FLMBOOL bMutexLocked = FALSE)
|
|
{
|
|
if( !bMutexLocked)
|
|
{
|
|
f_mutexLock( gv_SFlmSysData.hRowCacheMutex);
|
|
}
|
|
|
|
m_pRowAllocator->defragmentMemory();
|
|
m_pBufAllocator->defragmentMemory();
|
|
|
|
if( !bMutexLocked)
|
|
{
|
|
f_mutexUnlock( gv_SFlmSysData.hRowCacheMutex);
|
|
}
|
|
}
|
|
|
|
private:
|
|
|
|
// Hash function for hashing to rows in row cache.
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE F_Row ** rowHash(
|
|
FLMUINT64 ui64RowId)
|
|
{
|
|
return( &m_ppHashBuckets[(FLMUINT)ui64RowId & m_uiHashMask]);
|
|
}
|
|
|
|
RCODE rehash( void);
|
|
|
|
RCODE waitNotify(
|
|
F_Db * pDb,
|
|
F_Row ** ppRow);
|
|
|
|
void notifyWaiters(
|
|
FNOTIFY * pNotify,
|
|
F_Row * pUseRow,
|
|
RCODE NotifyRc);
|
|
|
|
void linkIntoRowCache(
|
|
F_Row * pNewerRow,
|
|
F_Row * pOlderRow,
|
|
F_Row * pRow,
|
|
FLMBOOL bLinkAsMRU);
|
|
|
|
void findRow(
|
|
F_Db * pDb,
|
|
FLMUINT uiTableNum,
|
|
FLMUINT64 ui64RowId,
|
|
FLMUINT64 ui64VersionNeeded,
|
|
FLMBOOL bDontPoisonCache,
|
|
FLMUINT * puiNumLooks,
|
|
F_Row ** ppRow,
|
|
F_Row ** ppNewerRow,
|
|
F_Row ** ppOlderRow);
|
|
|
|
RCODE readRowFromDisk(
|
|
F_Db * pDb,
|
|
FLMUINT uiTableNum,
|
|
FLMUINT64 ui64RowId,
|
|
F_Row * pRow,
|
|
FLMUINT64 * pui64LowTransId,
|
|
FLMBOOL * pbMostCurrent);
|
|
|
|
RCODE _makeWriteCopy(
|
|
F_Db * pDb,
|
|
F_Row ** ppRow);
|
|
|
|
// Private Data
|
|
|
|
F_CacheList m_MRUList; // List of all row objects in MRU order.
|
|
F_Row * m_pPurgeList; // List of F_Row objects that
|
|
// should be deleted when the use count
|
|
// goes to zero.
|
|
F_Row * m_pHeapList; // List of rows with heap allocations
|
|
F_Row * m_pOldList; // List of old versions
|
|
SFLM_CACHE_USAGE m_Usage; // Contains maximum, bytes used, etc.
|
|
F_Row ** m_ppHashBuckets; // Array of hash buckets.
|
|
FLMUINT m_uiNumBuckets; // Total number of hash buckets.
|
|
// must be an exponent of 2.
|
|
FLMUINT m_uiHashFailTime;
|
|
// Last time we tried to rehash and
|
|
// failed. Want to wait before we
|
|
// retry again.
|
|
FLMUINT m_uiHashMask; // Hash mask mask for hashing a
|
|
// row id to a hash bucket.
|
|
FLMUINT m_uiPendingReads; // Total reads currently pending.
|
|
FLMUINT m_uiIoWaits; // Number of times multiple threads
|
|
// were reading the same row from
|
|
// disk at the same time.
|
|
IF_FixedAlloc * m_pRowAllocator; // Fixed size allocator for F_Row
|
|
// objects
|
|
IF_BufferAlloc * m_pBufAllocator; // Buffer allocator for buffers in
|
|
// F_Row objects
|
|
|
|
F_RowRelocator m_rowRelocator;
|
|
F_ColumnDataRelocator m_columnDataRelocator;
|
|
F_ColumnListRelocator m_columnListRelocator;
|
|
FLMBOOL m_bReduceInProgress;
|
|
|
|
#ifdef FLM_DEBUG
|
|
FLMBOOL m_bDebug; // Debug mode?
|
|
#endif
|
|
friend class F_Row;
|
|
friend class F_GlobalCacheMgr;
|
|
friend class F_Database;
|
|
friend class F_DbSystem;
|
|
friend class F_RowRelocator;
|
|
friend class F_ColumnDataRelocator;
|
|
friend class F_ColumnListRelocator;
|
|
};
|
|
|
|
// Flags kept in F_Row::m_uiCacheFlags. Mutex needs to be locked to
|
|
// access these.
|
|
|
|
#define NCA_READING_IN 0x80000000
|
|
#define NCA_UNCOMMITTED 0x40000000
|
|
#define NCA_LATEST_VER 0x20000000
|
|
#define NCA_PURGED 0x10000000
|
|
#define NCA_LINKED_TO_DATABASE 0x08000000
|
|
|
|
#define NCA_COUNTER_BITS (~(NCA_READING_IN | NCA_UNCOMMITTED | \
|
|
NCA_LATEST_VER | NCA_PURGED | \
|
|
NCA_LINKED_TO_DATABASE))
|
|
|
|
// Flags kept in F_Row::m_uiFlags. Mutex does not have to be locked to
|
|
// access these.
|
|
|
|
#define FROW_DIRTY 0x00000010
|
|
#define FROW_NEW 0x00000020
|
|
#define FROW_HEAP_ALLOC 0x00000040
|
|
|
|
/*****************************************************************************
|
|
Desc: Header for each column
|
|
******************************************************************************/
|
|
typedef struct F_COLUMN_ITEM
|
|
{
|
|
FLMUINT uiDataLen;
|
|
FLMUINT uiDataOffset;
|
|
} F_COLUMN_ITEM;
|
|
|
|
/*****************************************************************************
|
|
Desc:
|
|
******************************************************************************/
|
|
FINLINE FLMUINT allocOverhead( void)
|
|
{
|
|
// Round sizeof( F_Row *) + 1 to nearest 8 byte boundary.
|
|
|
|
return( (sizeof( F_Row *) + 9) & (~((FLMUINT)7)));
|
|
}
|
|
|
|
/*****************************************************************************
|
|
Desc:
|
|
******************************************************************************/
|
|
FINLINE FLMBYTE * getActualPointer(
|
|
void * pvPtr)
|
|
{
|
|
if (pvPtr)
|
|
{
|
|
return( (FLMBYTE *)pvPtr - allocOverhead());
|
|
}
|
|
else
|
|
{
|
|
return( NULL);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
Desc:
|
|
******************************************************************************/
|
|
FINLINE FLMUINT calcColumnListBufSize(
|
|
FLMUINT uiColumnCount)
|
|
{
|
|
return( uiColumnCount * sizeof( F_COLUMN_ITEM) + allocOverhead());
|
|
}
|
|
|
|
/*****************************************************************************
|
|
Desc:
|
|
******************************************************************************/
|
|
FINLINE FLMUINT calcDataBufSize(
|
|
FLMUINT uiDataSize)
|
|
{
|
|
return( uiDataSize + allocOverhead());
|
|
}
|
|
|
|
/*****************************************************************************
|
|
Desc: Cached Row
|
|
******************************************************************************/
|
|
class F_Row : public F_CachedItem
|
|
{
|
|
public:
|
|
|
|
F_Row();
|
|
|
|
~F_Row();
|
|
|
|
// This method assumes that the row cache mutex has been locked.
|
|
|
|
FINLINE FLMBOOL canBeFreed( void)
|
|
{
|
|
return( (!rowInUse() && !readingInRow() && !rowIsDirty()) ? TRUE : FALSE);
|
|
}
|
|
|
|
// This method assumes that the row cache mutex has been locked.
|
|
|
|
FINLINE void freeRow( void)
|
|
{
|
|
if (rowPurged())
|
|
{
|
|
freePurged();
|
|
}
|
|
else
|
|
{
|
|
freeCache( FALSE);
|
|
}
|
|
}
|
|
|
|
FINLINE FLMUINT64 getLowTransId( void)
|
|
{
|
|
return( m_ui64LowTransId);
|
|
}
|
|
|
|
FINLINE FLMUINT64 getHighTransId( void)
|
|
{
|
|
return( m_ui64HighTransId);
|
|
}
|
|
|
|
RCODE resizeDataBuffer(
|
|
FLMUINT uiSize,
|
|
FLMBOOL bMutexAlreadyLocked);
|
|
|
|
RCODE resizeColumnList(
|
|
FLMUINT uiColumnCount,
|
|
FLMBOOL bMutexAlreadyLocked);
|
|
|
|
RCODE flushRow(
|
|
F_Db * pDb,
|
|
F_Btree * pBTree);
|
|
|
|
RCODE flushRow(
|
|
F_Db * pDb);
|
|
|
|
RCODE copyColumnList(
|
|
F_Db * pDb,
|
|
F_Row * pSourceRow,
|
|
FLMBOOL bMutexAlreadyLocked);
|
|
|
|
RCODE readRow(
|
|
F_Db * pDb,
|
|
FLMUINT uiTableNum,
|
|
FLMUINT64 ui64RowId,
|
|
IF_IStream * pIStream,
|
|
FLMUINT uiRowDataLength);
|
|
|
|
FINLINE void setRowAndDataPtr(
|
|
FLMBYTE * pucActualAlloc)
|
|
{
|
|
*((F_Row **)(pucActualAlloc)) = this;
|
|
m_pucColumnData = pucActualAlloc + allocOverhead();
|
|
}
|
|
|
|
FINLINE void setColumnListPtr(
|
|
FLMBYTE * pucActualAlloc)
|
|
{
|
|
*((F_Row **)(pucActualAlloc)) = this;
|
|
m_pColumns = (F_COLUMN_ITEM *)(pucActualAlloc + allocOverhead());
|
|
}
|
|
|
|
FINLINE FLMUINT64 getRowId( void)
|
|
{
|
|
return( m_ui64RowId);
|
|
}
|
|
|
|
FINLINE F_Database * getDatabase( void)
|
|
{
|
|
return( m_pDatabase);
|
|
}
|
|
|
|
FINLINE FLMUINT getTableNum( void)
|
|
{
|
|
return( m_uiTableNum);
|
|
}
|
|
|
|
FINLINE FLMUINT getOffsetIndex( void)
|
|
{
|
|
return( m_uiOffsetIndex);
|
|
}
|
|
|
|
FINLINE void setOffsetIndex(
|
|
FLMUINT uiOffsetIndex)
|
|
{
|
|
m_uiOffsetIndex = uiOffsetIndex;
|
|
}
|
|
|
|
FINLINE FLMUINT32 getBlkAddr( void)
|
|
{
|
|
return( m_ui32BlkAddr);
|
|
}
|
|
|
|
FINLINE void setBlkAddr(
|
|
FLMUINT32 ui32BlkAddr)
|
|
{
|
|
m_ui32BlkAddr = ui32BlkAddr;
|
|
}
|
|
|
|
FINLINE FLMBOOL isRightVersion(
|
|
FLMUINT64 ui64TransId)
|
|
{
|
|
return( (ui64TransId >= m_ui64LowTransId &&
|
|
ui64TransId <= m_ui64HighTransId)
|
|
? TRUE
|
|
: FALSE);
|
|
}
|
|
|
|
// Generally, assumes that the row cache mutex has already been locked.
|
|
// There is one case where it is not locked, but it is not
|
|
// critical that it be locked - inside syncFromDb.
|
|
|
|
FINLINE FLMBOOL rowPurged( void)
|
|
{
|
|
return( (m_uiCacheFlags & NCA_PURGED ) ? TRUE : FALSE);
|
|
}
|
|
|
|
#ifdef FLM_DEBUG
|
|
void checkReadFromDisk(
|
|
F_Db * pDb);
|
|
#endif
|
|
|
|
void * operator new(
|
|
FLMSIZET uiSize)
|
|
#if !defined( FLM_NLM)
|
|
throw()
|
|
#endif
|
|
;
|
|
|
|
void * operator new(
|
|
FLMSIZET uiSize,
|
|
const char * pszFile,
|
|
int iLine)
|
|
#if !defined( FLM_NLM)
|
|
throw()
|
|
#endif
|
|
;
|
|
|
|
void * operator new[](
|
|
FLMSIZET uiSize)
|
|
#if !defined( FLM_NLM)
|
|
throw()
|
|
#endif
|
|
;
|
|
|
|
void * operator new[](
|
|
FLMSIZET uiSize,
|
|
const char * pszFile,
|
|
int iLine)
|
|
#if !defined( FLM_NLM)
|
|
throw()
|
|
#endif
|
|
;
|
|
|
|
void operator delete(
|
|
void * ptr);
|
|
|
|
void operator delete[](
|
|
void * ptr);
|
|
|
|
#if defined( FLM_DEBUG) && !defined( __WATCOMC__) && !defined( FLM_SOLARIS)
|
|
void operator delete(
|
|
void * ptr,
|
|
const char * file,
|
|
int line);
|
|
#endif
|
|
|
|
#if defined( FLM_DEBUG) && !defined( __WATCOMC__) && !defined( FLM_SOLARIS)
|
|
void operator delete[](
|
|
void * ptr,
|
|
const char * pszFileName,
|
|
int iLineNum);
|
|
#endif
|
|
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void incrRowUseCount( void)
|
|
{
|
|
m_uiCacheFlags = (m_uiCacheFlags & (~(NCA_COUNTER_BITS))) |
|
|
(((m_uiCacheFlags & NCA_COUNTER_BITS) + 1));
|
|
}
|
|
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void decrRowUseCount( void)
|
|
{
|
|
m_uiCacheFlags = (m_uiCacheFlags & (~(NCA_COUNTER_BITS))) |
|
|
(((m_uiCacheFlags & NCA_COUNTER_BITS) - 1));
|
|
}
|
|
|
|
void setRowDirty(
|
|
F_Db * pDb,
|
|
FLMBOOL bNew);
|
|
|
|
void unsetRowDirtyAndNew(
|
|
F_Db * pDb,
|
|
FLMBOOL bMutexAlreadyLocked = FALSE);
|
|
|
|
FINLINE FLMBOOL rowIsDirty( void)
|
|
{
|
|
return( (m_uiFlags & FROW_DIRTY) ? TRUE : FALSE);
|
|
}
|
|
|
|
FINLINE FLMBOOL rowIsNew( void)
|
|
{
|
|
return( (m_uiFlags & FROW_NEW) ? TRUE : FALSE);
|
|
}
|
|
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE FLMBOOL rowUncommitted( void)
|
|
{
|
|
return( (m_uiCacheFlags & NCA_UNCOMMITTED ) ? TRUE : FALSE);
|
|
}
|
|
|
|
void freeCache(
|
|
FLMBOOL bPutInPurgeList);
|
|
|
|
// Column functions
|
|
|
|
FINLINE FLMBYTE * getColumnDataPtr(
|
|
FLMUINT uiColumnNum)
|
|
{
|
|
F_COLUMN_ITEM * pColumnItem = getColumn( uiColumnNum);
|
|
return( (FLMBYTE *)(pColumnItem->uiDataLen <= sizeof( FLMUINT)
|
|
? (FLMBYTE *)(&pColumnItem->uiDataOffset)
|
|
: (FLMBYTE *)(m_pucColumnData + pColumnItem->uiDataOffset)));
|
|
}
|
|
|
|
RCODE allocColumnDataSpace(
|
|
F_Db * pDb,
|
|
FLMUINT uiColumnNum,
|
|
FLMUINT uiSizeNeeded,
|
|
FLMBOOL bMutexAlreadyLocked);
|
|
|
|
RCODE setNumber64(
|
|
F_Db * pDb,
|
|
FLMUINT uiColumnNum,
|
|
FLMUINT64 ui64Value,
|
|
FLMBOOL bNeg);
|
|
|
|
RCODE getNumber64(
|
|
F_Db * pDb,
|
|
FLMUINT uiColumnNum,
|
|
FLMUINT64 * pui64Value,
|
|
FLMBOOL * pbNeg,
|
|
FLMBOOL * pbIsNull);
|
|
|
|
FINLINE RCODE getUINT32(
|
|
F_Db * pDb,
|
|
FLMUINT uiColumnNum,
|
|
FLMUINT32 * pui32Num,
|
|
FLMBOOL * pbIsNull)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
FLMUINT64 ui64Num;
|
|
FLMBOOL bNeg;
|
|
|
|
if( RC_OK( rc = getNumber64( pDb, uiColumnNum, &ui64Num,
|
|
&bNeg, pbIsNull)))
|
|
{
|
|
if (!(*pbIsNull))
|
|
{
|
|
rc = convertToUINT32( ui64Num, bNeg, pui32Num);
|
|
}
|
|
}
|
|
return( rc);
|
|
}
|
|
|
|
FINLINE RCODE getUINT(
|
|
F_Db * pDb,
|
|
FLMUINT uiColumnNum,
|
|
FLMUINT * puiNum,
|
|
FLMBOOL * pbIsNull)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
FLMUINT64 ui64Num;
|
|
FLMBOOL bNeg;
|
|
|
|
if( RC_OK( rc = getNumber64( pDb, uiColumnNum, &ui64Num,
|
|
&bNeg, pbIsNull)))
|
|
{
|
|
if (!(*pbIsNull))
|
|
{
|
|
rc = convertToUINT( ui64Num, bNeg, puiNum);
|
|
}
|
|
}
|
|
return( rc);
|
|
}
|
|
|
|
FINLINE RCODE getUINT64(
|
|
F_Db * pDb,
|
|
FLMUINT uiColumnNum,
|
|
FLMUINT64 * pui64Num,
|
|
FLMBOOL * pbIsNull)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
FLMUINT64 ui64Num;
|
|
FLMBOOL bNeg;
|
|
|
|
if( RC_OK( rc = getNumber64( pDb, uiColumnNum, &ui64Num,
|
|
&bNeg, pbIsNull)))
|
|
{
|
|
if (!(*pbIsNull))
|
|
{
|
|
rc = convertToUINT64( ui64Num, bNeg, pui64Num);
|
|
}
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
FINLINE RCODE getINT(
|
|
F_Db * pDb,
|
|
FLMUINT uiColumnNum,
|
|
FLMINT * piNum,
|
|
FLMBOOL * pbIsNull)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
FLMUINT64 ui64Num;
|
|
FLMBOOL bNeg;
|
|
|
|
if (RC_OK( rc = getNumber64( pDb, uiColumnNum, &ui64Num,
|
|
&bNeg, pbIsNull)))
|
|
{
|
|
if (!(*pbIsNull))
|
|
{
|
|
rc = convertToINT( ui64Num, bNeg, piNum);
|
|
}
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
FINLINE RCODE getINT64(
|
|
F_Db * pDb,
|
|
FLMUINT uiColumnNum,
|
|
FLMINT64 * pi64Num,
|
|
FLMBOOL * pbIsNull)
|
|
{
|
|
RCODE rc = NE_SFLM_OK;
|
|
FLMUINT64 ui64Num;
|
|
FLMBOOL bNeg;
|
|
|
|
if (RC_OK( rc = getNumber64( pDb, uiColumnNum, &ui64Num,
|
|
&bNeg, pbIsNull)))
|
|
{
|
|
if (!(*pbIsNull))
|
|
{
|
|
rc = convertToINT64( ui64Num, bNeg, pi64Num);
|
|
}
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
FINLINE RCODE setUINT(
|
|
F_Db * pDb,
|
|
FLMUINT uiColumnNum,
|
|
FLMUINT uiValue)
|
|
{
|
|
return( setNumber64( pDb, uiColumnNum, (FLMUINT64)uiValue, FALSE));
|
|
}
|
|
|
|
FINLINE RCODE setUINT64(
|
|
F_Db * pDb,
|
|
FLMUINT uiColumnNum,
|
|
FLMUINT64 ui64Value)
|
|
{
|
|
return( setNumber64( pDb, uiColumnNum, ui64Value, FALSE));
|
|
}
|
|
|
|
FINLINE RCODE setINT(
|
|
F_Db * pDb,
|
|
FLMUINT uiColumnNum,
|
|
FLMINT iValue)
|
|
{
|
|
FLMBOOL bNeg;
|
|
FLMUINT ui64Value;
|
|
|
|
if (iValue < 0)
|
|
{
|
|
bNeg = TRUE;
|
|
ui64Value = (FLMUINT64)((FLMINT64)(-iValue));
|
|
}
|
|
else
|
|
{
|
|
bNeg = FALSE;
|
|
ui64Value = (FLMUINT64)iValue;
|
|
}
|
|
return( setNumber64( pDb, uiColumnNum, ui64Value, bNeg));
|
|
}
|
|
|
|
FINLINE RCODE setINT64(
|
|
F_Db * pDb,
|
|
FLMUINT uiColumnNum,
|
|
FLMINT64 i64Value)
|
|
{
|
|
FLMBOOL bNeg;
|
|
FLMUINT ui64Value;
|
|
|
|
if (i64Value < 0)
|
|
{
|
|
bNeg = TRUE;
|
|
ui64Value = (FLMUINT64)(-i64Value);
|
|
}
|
|
else
|
|
{
|
|
bNeg = FALSE;
|
|
ui64Value = (FLMUINT64)i64Value;
|
|
}
|
|
return( setNumber64( pDb, uiColumnNum, ui64Value, bNeg));
|
|
}
|
|
|
|
RCODE setValue(
|
|
F_Db * pDb,
|
|
FLMUINT uiColumnNum,
|
|
const FLMBYTE * pucValue,
|
|
FLMUINT uiValueLen);
|
|
|
|
RCODE setUTF8(
|
|
F_Db * pDb,
|
|
FLMUINT uiColumnNum,
|
|
const char * pszValue,
|
|
FLMUINT uiNumBytesInValue,
|
|
FLMUINT uiNumCharsInValue);
|
|
|
|
RCODE getUTF8(
|
|
F_Db * pDb,
|
|
FLMUINT uiColumnNum,
|
|
char * pszValueBuffer,
|
|
FLMUINT uiBufferSize,
|
|
FLMBOOL * pbIsNull,
|
|
FLMUINT * puiCharsReturned,
|
|
FLMUINT * puiBufferBytesUsed);
|
|
|
|
RCODE setBinary(
|
|
F_Db * pDb,
|
|
FLMUINT uiColumnNum,
|
|
const void * pvValue,
|
|
FLMUINT uiNumBytesInBuffer);
|
|
|
|
RCODE getBinary(
|
|
F_Db * pDb,
|
|
FLMUINT uiColumnNum,
|
|
void * pvBuffer,
|
|
FLMUINT uiBufferLen,
|
|
FLMUINT * puiDataLen,
|
|
FLMBOOL * pbIsNull);
|
|
|
|
void setToNull(
|
|
F_Db * pDb,
|
|
FLMUINT uiColumnNum);
|
|
|
|
void getDataLen(
|
|
F_Db * pDb,
|
|
FLMUINT uiColumnNum,
|
|
FLMUINT * puiDataLen,
|
|
FLMBOOL * pbIsNull);
|
|
|
|
FINLINE F_COLUMN_ITEM * getColumn(
|
|
FLMUINT uiColumnNum)
|
|
{
|
|
return( (F_COLUMN_ITEM *)((uiColumnNum && uiColumnNum <= m_uiNumColumns &&
|
|
m_pColumns [uiColumnNum - 1].uiDataLen)
|
|
? &m_pColumns [uiColumnNum - 1]
|
|
: (F_COLUMN_ITEM *)NULL));
|
|
}
|
|
|
|
RCODE getIStream(
|
|
F_Db * pDb,
|
|
FLMUINT uiColumnNum,
|
|
FLMBOOL * pbIsNull,
|
|
F_BufferIStream * pBufferIStream,
|
|
eDataType * peDataType,
|
|
FLMUINT * puiDataLength);
|
|
|
|
RCODE getTextIStream(
|
|
F_Db * pDb,
|
|
FLMUINT uiColumnNum,
|
|
FLMBOOL * pbIsNull,
|
|
F_BufferIStream * pBufferIStream,
|
|
FLMUINT * puiNumChars);
|
|
|
|
FINLINE void ReleaseRow( void)
|
|
{
|
|
f_mutexLock( gv_SFlmSysData.hRowCacheMutex);
|
|
decrRowUseCount();
|
|
f_mutexUnlock( gv_SFlmSysData.hRowCacheMutex);
|
|
}
|
|
|
|
private:
|
|
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE FLMBOOL readingInRow( void)
|
|
{
|
|
return( (m_uiCacheFlags & NCA_READING_IN ) ? TRUE : FALSE);
|
|
}
|
|
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void setReadingIn( void)
|
|
{
|
|
m_uiCacheFlags |= NCA_READING_IN;
|
|
}
|
|
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void unsetReadingIn( void)
|
|
{
|
|
m_uiCacheFlags &= (~(NCA_READING_IN));
|
|
}
|
|
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void setUncommitted( void)
|
|
{
|
|
m_uiCacheFlags |= NCA_UNCOMMITTED;
|
|
}
|
|
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void unsetUncommitted( void)
|
|
{
|
|
m_uiCacheFlags &= (~(NCA_UNCOMMITTED));
|
|
}
|
|
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE FLMBOOL rowIsLatestVer( void)
|
|
{
|
|
return( (m_uiCacheFlags & NCA_LATEST_VER ) ? TRUE : FALSE);
|
|
}
|
|
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void setLatestVer( void)
|
|
{
|
|
m_uiCacheFlags |= NCA_LATEST_VER;
|
|
}
|
|
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void unsetLatestVer( void)
|
|
{
|
|
m_uiCacheFlags &= (~(NCA_LATEST_VER));
|
|
}
|
|
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void setPurged( void)
|
|
{
|
|
m_uiCacheFlags |= NCA_PURGED;
|
|
}
|
|
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void unsetPurged( void)
|
|
{
|
|
m_uiCacheFlags &= (~(NCA_PURGED));
|
|
}
|
|
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE FLMBOOL rowLinkedToDatabase( void)
|
|
{
|
|
return( (m_uiCacheFlags & NCA_LINKED_TO_DATABASE ) ? TRUE : FALSE);
|
|
}
|
|
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void setLinkedToDatabase( void)
|
|
{
|
|
m_uiCacheFlags |= NCA_LINKED_TO_DATABASE;
|
|
}
|
|
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void unsetLinkedToDatabase( void)
|
|
{
|
|
m_uiCacheFlags &= (~(NCA_LINKED_TO_DATABASE));
|
|
}
|
|
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE FLMBOOL rowInUse( void)
|
|
{
|
|
return( (m_uiCacheFlags & NCA_COUNTER_BITS ) ? TRUE : FALSE);
|
|
}
|
|
|
|
// Unlink a row from the global purged list.
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void unlinkFromPurged( void)
|
|
{
|
|
if (m_pNextInGlobal)
|
|
{
|
|
m_pNextInGlobal->m_pPrevInGlobal = m_pPrevInGlobal;
|
|
}
|
|
|
|
if (m_pPrevInGlobal)
|
|
{
|
|
m_pPrevInGlobal->m_pNextInGlobal = m_pNextInGlobal;
|
|
}
|
|
else
|
|
{
|
|
gv_SFlmSysData.pRowCacheMgr->m_pPurgeList =
|
|
(F_Row *)m_pNextInGlobal;
|
|
}
|
|
|
|
m_pPrevInGlobal = NULL;
|
|
m_pNextInGlobal = NULL;
|
|
}
|
|
|
|
// Link a row to an F_Database list at the head of the list.
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void linkToDatabaseAtHead(
|
|
F_Database * pDatabase)
|
|
{
|
|
if (!pDatabase->m_pLastDirtyRow || rowIsDirty())
|
|
{
|
|
m_pPrevInDatabase = NULL;
|
|
if ((m_pNextInDatabase = pDatabase->m_pFirstRow) != NULL)
|
|
{
|
|
pDatabase->m_pFirstRow->m_pPrevInDatabase = this;
|
|
}
|
|
else
|
|
{
|
|
pDatabase->m_pLastRow = this;
|
|
}
|
|
|
|
pDatabase->m_pFirstRow = this;
|
|
if (rowIsDirty() && !pDatabase->m_pLastDirtyRow)
|
|
{
|
|
pDatabase->m_pLastDirtyRow = this;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// pDatabase->m_pLastDirtyRow is guaranteed to be non-NULL,
|
|
// Hence, m_pPrevInDatabase will be non-NULL.
|
|
// We are also guaranteed that the row is not dirty.
|
|
|
|
m_pPrevInDatabase = pDatabase->m_pLastDirtyRow;
|
|
m_pNextInDatabase = m_pPrevInDatabase->m_pNextInDatabase;
|
|
|
|
m_pPrevInDatabase->m_pNextInDatabase = this;
|
|
if (m_pNextInDatabase)
|
|
{
|
|
m_pNextInDatabase->m_pPrevInDatabase = this;
|
|
}
|
|
else
|
|
{
|
|
pDatabase->m_pLastRow = this;
|
|
}
|
|
}
|
|
|
|
m_pDatabase = pDatabase;
|
|
setLinkedToDatabase();
|
|
}
|
|
|
|
// Link a row to an F_Database list at the end of the list.
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void linkToDatabaseAtEnd(
|
|
F_Database * pDatabase)
|
|
{
|
|
// Row cannot be a dirty row.
|
|
|
|
flmAssert( !rowIsDirty());
|
|
m_pNextInDatabase = NULL;
|
|
if( (m_pPrevInDatabase = pDatabase->m_pLastRow) != NULL)
|
|
{
|
|
pDatabase->m_pLastRow->m_pNextInDatabase = this;
|
|
}
|
|
else
|
|
{
|
|
pDatabase->m_pFirstRow = this;
|
|
}
|
|
|
|
pDatabase->m_pLastRow = this;
|
|
m_pDatabase = pDatabase;
|
|
setLinkedToDatabase();
|
|
}
|
|
|
|
// Unlink a row from its F_Database list.
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void unlinkFromDatabase( void)
|
|
{
|
|
if( rowLinkedToDatabase())
|
|
{
|
|
// If this is the last dirty row, change the database's
|
|
// last dirty pointer to point to the previous row, if any.
|
|
|
|
if (m_pDatabase->m_pLastDirtyRow == this)
|
|
{
|
|
flmAssert( rowIsDirty());
|
|
m_pDatabase->m_pLastDirtyRow = m_pPrevInDatabase;
|
|
}
|
|
|
|
// Remove the row from the database's list.
|
|
|
|
if( m_pNextInDatabase)
|
|
{
|
|
m_pNextInDatabase->m_pPrevInDatabase = m_pPrevInDatabase;
|
|
}
|
|
else
|
|
{
|
|
m_pDatabase->m_pLastRow = m_pPrevInDatabase;
|
|
}
|
|
|
|
if( m_pPrevInDatabase)
|
|
{
|
|
m_pPrevInDatabase->m_pNextInDatabase = m_pNextInDatabase;
|
|
}
|
|
else
|
|
{
|
|
m_pDatabase->m_pFirstRow = m_pNextInDatabase;
|
|
}
|
|
|
|
m_pPrevInDatabase = NULL;
|
|
m_pNextInDatabase = NULL;
|
|
m_pDatabase = NULL;
|
|
unsetLinkedToDatabase();
|
|
}
|
|
}
|
|
|
|
// Link a row into its hash bucket.
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void linkToHashBucket( void)
|
|
{
|
|
F_Row ** ppHashBucket = gv_SFlmSysData.pRowCacheMgr->rowHash(
|
|
m_ui64RowId);
|
|
|
|
flmAssert( m_pNewerVersion == NULL);
|
|
|
|
m_pPrevInBucket = NULL;
|
|
|
|
if ((m_pNextInBucket = *ppHashBucket) != NULL)
|
|
{
|
|
m_pNextInBucket->m_pPrevInBucket = this;
|
|
}
|
|
|
|
*ppHashBucket = this;
|
|
}
|
|
|
|
// Unlink a row from its hash bucket.
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void unlinkFromHashBucket( void)
|
|
{
|
|
flmAssert( m_pNewerVersion == NULL);
|
|
|
|
if (m_pNextInBucket)
|
|
{
|
|
m_pNextInBucket->m_pPrevInBucket = m_pPrevInBucket;
|
|
}
|
|
|
|
if (m_pPrevInBucket)
|
|
{
|
|
m_pPrevInBucket->m_pNextInBucket = m_pNextInBucket;
|
|
}
|
|
else
|
|
{
|
|
F_Row ** ppHashBucket =
|
|
gv_SFlmSysData.pRowCacheMgr->rowHash(
|
|
m_ui64RowId);
|
|
|
|
*ppHashBucket = m_pNextInBucket;
|
|
}
|
|
|
|
m_pPrevInBucket = NULL;
|
|
m_pNextInBucket = NULL;
|
|
}
|
|
|
|
// Unlink a row from its version list.
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void linkToVerList(
|
|
F_Row * pNewerVer,
|
|
F_Row * pOlderVer)
|
|
{
|
|
if( (m_pNewerVersion = pNewerVer) != NULL)
|
|
{
|
|
pNewerVer->m_pOlderVersion = this;
|
|
}
|
|
|
|
if ((m_pOlderVersion = pOlderVer) != NULL)
|
|
{
|
|
pOlderVer->m_pNewerVersion = this;
|
|
}
|
|
}
|
|
|
|
// Unlink a row from its version list. This routine
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void unlinkFromVerList( void)
|
|
{
|
|
if (m_pNewerVersion)
|
|
{
|
|
m_pNewerVersion->m_pOlderVersion = m_pOlderVersion;
|
|
}
|
|
|
|
if (m_pOlderVersion)
|
|
{
|
|
m_pOlderVersion->m_pNewerVersion = m_pNewerVersion;
|
|
}
|
|
|
|
m_pNewerVersion = NULL;
|
|
m_pOlderVersion = NULL;
|
|
}
|
|
|
|
// Link a row into the heap list
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void linkToHeapList( void)
|
|
{
|
|
flmAssert( !m_pPrevInHeapList);
|
|
flmAssert( (m_uiFlags & FROW_HEAP_ALLOC) == 0);
|
|
|
|
if( (m_pNextInHeapList =
|
|
gv_SFlmSysData.pRowCacheMgr->m_pHeapList) != NULL)
|
|
{
|
|
gv_SFlmSysData.pRowCacheMgr->m_pHeapList->m_pPrevInHeapList = this;
|
|
}
|
|
|
|
gv_SFlmSysData.pRowCacheMgr->m_pHeapList = this;
|
|
m_uiFlags |= FROW_HEAP_ALLOC;
|
|
}
|
|
|
|
// Unlink a row from the heap list
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void unlinkFromHeapList( void)
|
|
{
|
|
flmAssert( m_uiFlags & FROW_HEAP_ALLOC);
|
|
|
|
if (m_pNextInHeapList)
|
|
{
|
|
m_pNextInHeapList->m_pPrevInHeapList = m_pPrevInHeapList;
|
|
}
|
|
|
|
if (m_pPrevInHeapList)
|
|
{
|
|
m_pPrevInHeapList->m_pNextInHeapList = m_pNextInHeapList;
|
|
}
|
|
else
|
|
{
|
|
gv_SFlmSysData.pRowCacheMgr->m_pHeapList = m_pNextInHeapList;
|
|
}
|
|
|
|
m_pPrevInHeapList = NULL;
|
|
m_pNextInHeapList = NULL;
|
|
m_uiFlags &= ~FROW_HEAP_ALLOC;
|
|
}
|
|
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void linkToOldList( void)
|
|
{
|
|
flmAssert( !m_pPrevInOldList);
|
|
|
|
if( (m_pNextInOldList =
|
|
gv_SFlmSysData.pRowCacheMgr->m_pOldList) != NULL)
|
|
{
|
|
gv_SFlmSysData.pRowCacheMgr->m_pOldList->m_pPrevInOldList = this;
|
|
}
|
|
|
|
gv_SFlmSysData.pRowCacheMgr->m_pOldList = this;
|
|
}
|
|
|
|
// Assumes that the row cache mutex has already been locked.
|
|
FINLINE void unlinkFromOldList( void)
|
|
{
|
|
if (m_pNextInOldList)
|
|
{
|
|
m_pNextInOldList->m_pPrevInOldList = m_pPrevInOldList;
|
|
}
|
|
|
|
if (m_pPrevInOldList)
|
|
{
|
|
m_pPrevInOldList->m_pNextInOldList = m_pNextInOldList;
|
|
}
|
|
else
|
|
{
|
|
gv_SFlmSysData.pRowCacheMgr->m_pOldList = m_pNextInOldList;
|
|
}
|
|
|
|
m_pPrevInOldList = NULL;
|
|
m_pNextInOldList = NULL;
|
|
}
|
|
|
|
FINLINE FLMUINT memSize( void)
|
|
{
|
|
FLMUINT uiSize = gv_SFlmSysData.pRowCacheMgr->m_pRowAllocator->getCellSize();
|
|
|
|
if (m_pucColumnData)
|
|
{
|
|
uiSize += gv_SFlmSysData.pRowCacheMgr->m_pBufAllocator->getTrueSize(
|
|
calcDataBufSize( m_uiColumnDataBufSize),
|
|
getActualPointer( m_pucColumnData));
|
|
}
|
|
|
|
if (m_pColumns)
|
|
{
|
|
uiSize += gv_SFlmSysData.pRowCacheMgr->m_pBufAllocator->getTrueSize(
|
|
calcColumnListBufSize( m_uiNumColumns),
|
|
getActualPointer( m_pColumns));
|
|
}
|
|
|
|
return( uiSize);
|
|
}
|
|
|
|
// Assumes that the row cache mutex is locked, because
|
|
// it potentially updates the cache usage statistics.
|
|
|
|
FINLINE void setTransID(
|
|
FLMUINT64 ui64NewTransID)
|
|
{
|
|
FLMUINT uiSize;
|
|
|
|
if (m_ui64HighTransId == FLM_MAX_UINT64 &&
|
|
ui64NewTransID != FLM_MAX_UINT64)
|
|
{
|
|
uiSize = memSize();
|
|
gv_SFlmSysData.pRowCacheMgr->m_Usage.uiOldVerBytes += uiSize;
|
|
gv_SFlmSysData.pRowCacheMgr->m_Usage.uiOldVerCount++;
|
|
linkToOldList();
|
|
}
|
|
else if (m_ui64HighTransId != FLM_MAX_UINT64 &&
|
|
ui64NewTransID == FLM_MAX_UINT64)
|
|
{
|
|
uiSize = memSize();
|
|
flmAssert( gv_SFlmSysData.pRowCacheMgr->m_Usage.uiOldVerBytes >= uiSize);
|
|
flmAssert( gv_SFlmSysData.pRowCacheMgr->m_Usage.uiOldVerCount);
|
|
gv_SFlmSysData.pRowCacheMgr->m_Usage.uiOldVerBytes -= uiSize;
|
|
gv_SFlmSysData.pRowCacheMgr->m_Usage.uiOldVerCount--;
|
|
unlinkFromOldList();
|
|
}
|
|
|
|
m_ui64HighTransId = ui64NewTransID;
|
|
}
|
|
|
|
void freePurged( void);
|
|
|
|
void linkToDatabase(
|
|
F_Database * pDatabase,
|
|
F_Db * pDb,
|
|
FLMUINT64 ui64LowTransId,
|
|
FLMBOOL bMostCurrent);
|
|
|
|
void resetRow( void);
|
|
|
|
// Things that manage the row's place in row cache. These are
|
|
// always initialized in the constructor
|
|
|
|
F_Row * m_pPrevInBucket;
|
|
F_Row * m_pNextInBucket;
|
|
F_Row * m_pPrevInDatabase;
|
|
F_Row * m_pNextInDatabase;
|
|
F_Row * m_pOlderVersion;
|
|
F_Row * m_pNewerVersion;
|
|
F_Row * m_pPrevInHeapList;
|
|
F_Row * m_pNextInHeapList;
|
|
F_Row * m_pPrevInOldList;
|
|
F_Row * m_pNextInOldList;
|
|
|
|
FLMUINT64 m_ui64LowTransId;
|
|
FLMUINT64 m_ui64HighTransId;
|
|
FNOTIFY * m_pNotifyList;
|
|
FLMUINT m_uiCacheFlags;
|
|
FLMUINT m_uiStreamUseCount;
|
|
|
|
// Things we hash on - initialized by caller of constructor
|
|
|
|
F_Database * m_pDatabase;
|
|
|
|
// Items initialized in constructor
|
|
|
|
FLMUINT m_uiTableNum;
|
|
FLMUINT64 m_ui64RowId;
|
|
FLMUINT m_uiFlags;
|
|
F_COLUMN_ITEM * m_pColumns;
|
|
FLMUINT m_uiNumColumns;
|
|
FLMBYTE * m_pucColumnData;
|
|
FLMUINT m_uiColumnDataBufSize;
|
|
|
|
// Items initialized by caller of constructor, but not
|
|
// in constructor - for performance reasons - so we don't
|
|
// end up setting them twice.
|
|
|
|
FLMUINT m_uiOffsetIndex;
|
|
FLMUINT32 m_ui32BlkAddr;
|
|
|
|
// Items that m_uiFlags indicates whether they are present
|
|
|
|
friend class F_RowCacheMgr;
|
|
friend class F_GlobalCacheMgr;
|
|
friend class F_Database;
|
|
friend class F_Db;
|
|
friend class F_DbSystem;
|
|
friend class F_BTreeIStream;
|
|
friend class F_Rfl;
|
|
friend class F_RebuildRowIStream;
|
|
friend class F_DbRebuild;
|
|
friend class F_RowRelocator;
|
|
friend class F_ColumnDataRelocator;
|
|
friend class F_ColumnListRelocator;
|
|
};
|
|
|
|
#endif // FCACHE_H
|