windows-nt/Source/XPSP1/NT/inetsrv/query/cindex/mindex.cxx
2020-09-26 16:20:57 +08:00

2147 lines
70 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1991 - 2000.
//
// File: MINDEX.CXX
//
// Contents: Master Index
//
// Classes: CMasterMergeIndex, CSplitKeyInfo, CTrackSplitKey
//
// History: 30-Mar-94 DwightKr Created stub.
//
//----------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
#include <pdir.hxx>
#include <fretable.hxx>
#include <rwex.hxx>
#include <pidxtbl.hxx>
#include <cifailte.hxx>
#include <eventlog.hxx>
#include "mindex.hxx"
#include "mcursor.hxx"
#include "fresh.hxx"
#include "fretest.hxx"
#include "indsnap.hxx"
#include "keylist.hxx"
#include "partn.hxx"
unsigned const EIGHT_MEGABYTES = 0x800000;
//+---------------------------------------------------------------------------
//
// Member: CMasterMergeIndex::CMasterMergeIndex, public
//
// Synopsis: Restore an index from storage
//
// Arguments: [storage] -- physical storage
// [widNewMaster] -- workid of new master index
// [iid] -- indexid of new master index
// [widMax] -- largest wid within this index
// [pCurrentMasterIndex] -- current master index, if any
// [widMasterLog] -- workid of master log
// [pMMergeLog] -- optional mmerge log
//
// History: 30-Mar-94 DwightKr Created.
//
//----------------------------------------------------------------------------
CMasterMergeIndex::CMasterMergeIndex( PStorage& storage,
WORKID widNewMaster,
INDEXID iid,
WORKID widMax,
CPersIndex * pCurrentMasterIndex,
WORKID widMasterLog,
CMMergeLog * pMMergeLog ) :
CDiskIndex( iid, CDiskIndex::eMaster, widMax ),
_sigMindex(eSigMindex),
_pCurrentMasterIndex(pCurrentMasterIndex),
_pTargetMasterIndex(0),
_pTargetSink(0),
_widMasterLog(widMasterLog),
_pCompr(0),
_pTrackIdxSplitKey(0),
_ulInitSize(0),
_ulFirstPageInUse(0),
#ifdef KEYLIST_ENABLED
_pTrackKeyLstSplitKey(0),
#endif // KEYLIST_ENABLED
_pNewKeyList(0),
_fAbortMerge(FALSE),
_storage(storage),
_pRWStore(0),
_pIndSnap(0),
_fStateLoaded(FALSE)
{
TRY
{
//
// The on-disk object MUST be fully constructed by the time we
// come here.
//
_pTargetMasterIndex = new CPersIndex( storage,
widNewMaster,
iid,
widMax,
CDiskIndex::eMaster,
PStorage::eOpenForWrite,
TRUE ); // read in the directory
_ulInitSize = _pTargetMasterIndex->GetIndex().PageSize();
_pTargetMasterIndex->GetIndex().SetPageGrowth( EIGHT_MEGABYTES / CI_PAGE_SIZE );
if (0 != _pCurrentMasterIndex)
_pCurrentMasterIndex->MakeShadow();
if ( _storage.SupportsShrinkFromFront() &&
0 != pMMergeLog )
{
//
// Reload the master merge state including the split keys, etc.
// This isn't quick, but it must be done since the target index
// must be used to resolve queries because the current index
// may already have been shrunk from the front.
//
ReloadMasterMerge( *pMMergeLog );
}
}
CATCH( CException, e )
{
if ( 0 != _pCurrentMasterIndex )
_pCurrentMasterIndex->MakeMaster();
delete _pTargetMasterIndex;
SCODE scE = e.GetErrorCode();
if ( STATUS_INTEGER_DIVIDE_BY_ZERO == scE ||
STATUS_ACCESS_VIOLATION == scE ||
STATUS_IN_PAGE_ERROR == scE )
{
ciDebugOut(( DEB_ERROR,
"Corrupt index or directory, caught 0x%x\n", scE ));
_storage.ReportCorruptComponent( L"MasterMergeReload" );
THROW( CException( CI_CORRUPT_DATABASE ) );
}
RETHROW();
}
END_CATCH
} //CMasterMergeIndex
//+---------------------------------------------------------------------------
//
// Member: CMasterMergeIndex::~CMasterMergeIndex, public
//
// Synopsis: Destructor for master index
//
// History: 30-Mar-94 DwightKr Created.
//
//----------------------------------------------------------------------------
CMasterMergeIndex::~CMasterMergeIndex ()
{
delete _pIndSnap;
if ( !IsZombie() )
{
//
// We are Dismounting - must delete both.
//
delete _pCurrentMasterIndex;
delete _pTargetMasterIndex;
}
else
{
//
// If we are a zombie and being deleted, then some query
// has responsibility for both the target and current master indexes
// if they are non-null.
//
Win4Assert( !_pTargetMasterIndex || _pTargetMasterIndex->InUse() );
#if 0
//
// We are not dismounting but are being deleted because either
// the master merge ended or a query ended.
//
if ( _pCurrentMasterIndex && !_pCurrentMasterIndex->InUse() )
{
//
// Since the refcount is 0, we have the responsibility to
// destroy the current master index. This destruction is via
// a query indexsnap shot which acquired this index while
// a master merge was going on.
//
delete _pCurrentMasterIndex;
}
if ( _pTargetMasterIndex &&
_pTargetMasterIndex->IsZombie() &&
!_pTargetMasterIndex->InUse() )
{
//
// The target master index is a zombie and it is not in use.
// We must delete it. This can happen if there was a query(Q1)
// in progress at the time Merge(M1) completed. Consider the
// following sequence of events:
// 0. Query Q1 starts and ref counts T1(Target Index of M1).
// 1. M1 completes.
// 2. A second merge M2 starts. It completes and zombifies
// T1
// 3. Q1 now is completing and so releases T1 whose ref.count
// goes to 0. Hence we must delete it.
//
delete _pTargetMasterIndex;
}
#endif // 0
}
CleanupMMergeState();
} //~CMasterMergeIndex
//+---------------------------------------------------------------------------
//
// Member: CMasterMergeIndex::Fill public
//
// Synopsis: Returns a record describing the contents of this index.
//
// History: 30-Mar-94 DwightKr Created.
//
//----------------------------------------------------------------------------
void CMasterMergeIndex::FillRecord ( CIndexRecord& record )
{
record._objectId = ObjectId();
record._iid = GetId();
record._type = itNewMaster;
record._maxWorkId = MaxWorkId();
}
#if CIDBG == 1
// useful for forcing a master merge to fail due to out of memory
BOOL g_fFailMMOutOfMemory = FALSE;
#endif // CIDBG == 1
//+---------------------------------------------------------------------------
//
// Member: CMasterMergeIndex::Merge, public
//
// Synopsis: Merge index(es) into a new master index. This operation may
// be restarted
//
// Effects: Fills the persistent index with data from the input
// indexes.
//
// Arguments: [pNewKeyList] -- Keylist to merge (master merge only)
// pFreshTest -- Fresh test to use for the merge. This
// parameter must only be used - it cannot be
// acquired by this routine.
// [mergeProgress] -- % merge complete
// [fGetRW] -- if TRUE, relevant words are computed
// Currently ignored
//
// Requires: The index is initially empty.
//
// Notes: Every compressor is transacted.
//
// History: 15-May-91 KyleP Use new PutOccurrence() method.
// 16-Apr-91 KyleP Created.
// 17-Feb-93 KyleP Merge keylist
// 30-Mar-94 DwightKr Copied here from CPersIndex, and added
// restart capabilities.
// 19-Apr-94 SrikantS Split key tracking changes and
// initialization change.
// 01-May-94 DwightKr Added code to allow for queries during
// master merge.
// 12-May-94 v-dlee Added relevant word computation
// 29-Sep-94 SrikantS IndSnap ownership fix.
// 25-Oct-95 DwightKr Add merge complete measurement
//
//----------------------------------------------------------------------------
void CMasterMergeIndex::Merge(
CWKeyList * pNewKeyList,
CFreshTest * pFreshTest,
const CPartition & partn,
CCiFrmPerfCounter & mergeProgress,
CCiFrameworkParams & frmwrkParams,
BOOL fGetRW )
{
// Calculate max of all input widMax's
Win4Assert( 0 != _pIndSnap );
ciDebugOut(( DEB_ITRACE, "Master merge\n" ));
#if CIDBG == 1
unsigned cKey = 0;
//
// Assert that all the indexes in the snapshot are marked as
// being in a master merge.
//
{
unsigned cInd;
CIndex ** apIndex;
apIndex = _pIndSnap->LokGetIndexes( cInd );
for ( unsigned i = 0; i < cInd; i++ )
{
Win4Assert( apIndex[i]->InMasterMerge() );
}
}
#endif
_pNewKeyList = pNewKeyList;
Win4Assert( 0 != _pNewKeyList );
Win4Assert( 0 != pFreshTest );
WORKID widMax = _pIndSnap->MaxWorkId();
ciDebugOut (( DEB_ITRACE, "Max work id %ld\n", widMax ));
if ( MaxWorkId() != widMax )
{
Win4Assert( !"WidMax in MasterMergeIndex is Corrupt" );
_storage.ReportCorruptComponent( L"MasterMergeIndex1" );
THROW( CException( CI_CORRUPT_DATABASE ) );
}
//
// Create and open the master merge log object so that the splitkeys can
// be retrieved and stored.
//
PRcovStorageObj *pPersMMergeLog = _storage.QueryMMergeLog(_widMasterLog);
SRcovStorageObj PersMMergeLog( pPersMMergeLog );
ciFAILTEST( STATUS_NO_MEMORY );
XPtr<CMMergeLog> xMMergeLog( new CMMergeLog( *pPersMMergeLog ) );
//
// STACKSTACK - cannot declare it where it is used because this is a
// big object and we don't want to allocate inside the loop.
//
XPtr<CSplitKeyInfo> xSplitKeyInfo( new CSplitKeyInfo() );
BOOL fReOpenTargetMaster = FALSE;
TRY
{
//
// Read-ahead on the source indexes results in better merge
// performance, but slower queries. Temporarily switch modes.
//
CSetReadAhead readAhead( _storage );
_pIndSnap->SetFreshTest( pFreshTest );
//
// Initialize and start/restart the master merge.
//
CKeyCursor * pKeySrc = StartMasterMerge( *_pIndSnap,
xMMergeLog.GetReference(),
_pNewKeyList );
unsigned finalIndexSize = _pIndSnap->TotalSizeInPages();
Win4Assert( finalIndexSize > 0 );
finalIndexSize = max( 1, finalIndexSize ); // Prevent divide by 0
mergeProgress.Update( _pTargetMasterIndex->GetIndex().PagesInUse() *
100 / finalIndexSize );
CMergeSourceCursor pCurSrc( pKeySrc );
BitOffset bitOffIndex; // BitOffset of a key in the target index
Win4Assert( 0 != _pCompr );
_pCompr->GetOffset( bitOffIndex );
ULONG page; // for directory creation.
if ( bitOffIndex.Offset() == 0 )
page = bitOffIndex.Page() - 1;
else
page = bitOffIndex.Page();
#if CIDBG==1
GetTargetDir().SetBitOffsetLastAdded( page, bitOffIndex.Offset() );
#endif // CIDBG==1
if ( !pCurSrc.IsEmpty() )
{
#ifdef RELEVANT_WORDS_ENABLED
//
// Determine which wids are in use
//
UINT cWids = 0;
WORKID * pwids = 0;
SByteArray swids(0);
{
SFreshHash phash( *pFreshTest );
cWids = phash->Count();
pwids = new WORKID[cWids];
swids.Set(pwids);
CFreshHashIter freiter(*phash);
for (UINT x = 0; !freiter.AtEnd(); freiter.Advance(), x++)
pwids[x] = freiter->WorkId();
}
_SortULongArray((ULONG *) pwids,cWids);
ciDebugOut (( DEB_ITRACE,"%d wids, %d through %d\n",
cWids,pwids[0],pwids[cWids-1]));
#endif // RELEVANT_WORDS_ENABLED
//
// Everytime we restart a master merge, we must use a brand
// new split key - we must not use the old key for atomicity and
// restartability
//
_pTrackIdxSplitKey->ClearNewKeyFound();
#ifdef KEYLIST_ENABLED
_pTrackKeyLstSplitKey->ClearNewKeyFound();
#endif // KEYLIST_ENABLED
BitOffset bitOffKeyLst;
BitOffset StartBitOffset; // Starting loc in bitstream
StartBitOffset.Init(0, 0);
CKeyBuf *pKeyLast = new CKeyBuf; // initialized to min key
SKeyBuf keyLast(pKeyLast);
#if CIDBG == 1
keyLast->SetPid(pidContents); // arbitrary but not pidAll
WCHAR FirstLetter = '@';
ciDebugOut (( DEB_ITRACE,"Merging. Merge on letter: "));
#endif
#ifdef RELEVANT_WORDS_ENABLED
//
// Create the relevant word computation object
//
CRelevantWord RelWord(pwids,cWids,defRWCount);
swids.Acquire();
delete pwids;
pwids = 0;
#endif // RELEVANT_WORDS_ENABLED
//
// Get checkpoint interval and convert # Kbytes to bits.
//
ULONG MasterMergeCheckpointInterval = frmwrkParams.GetMasterMergeCheckpointInterval() * 8 * 1024;
ShrinkFromFront( _idxSplitKeyInfo.GetKey() );
//
// For each key found, add it and all wids and occurances to the
// new master index.
//
for ( const CKeyBuf * pkey = pCurSrc->GetKey();
pkey != NULL; pkey = pCurSrc->GetNextKey())
{
#if CIDBG == 1
cKey++;
if ( *(pkey->GetStr()) != FirstLetter )
{
FirstLetter = *(pkey->GetStr());
if ( FirstLetter < L'~' )
ciDebugOut(( DEB_NOCOMPNAME | DEB_ITRACE | DEB_PENDING, "%c",
FirstLetter ));
else
ciDebugOut(( DEB_NOCOMPNAME | DEB_ITRACE | DEB_PENDING, "<%04x>",
FirstLetter ));
}
#endif
//
// Don't store empty keys
//
WORKID wid = pCurSrc->WorkId();
if ( wid == widInvalid )
continue;
//
// Add the key to the new index.
//
_pCompr->PutKey(pkey, pCurSrc->WorkIdCount(), bitOffIndex);
unsigned cWids = 0; // Count of wids for current key
#ifdef RELEVANT_WORDS_ENABLED
//
// Can this possibly be a relevant word?
//
BOOL fPossibleRW = ((pkey->Pid() == pidContents) &&
(((CKeyBuf * const) pkey)->IsPossibleRW()));
#endif // RELEVANT_WORDS_ENABLED
for ( ;
wid != widInvalid;
wid = pCurSrc->NextWorkId())
{
#if CIDBG == 1
if ( g_fFailMMOutOfMemory )
THROW( CException( E_OUTOFMEMORY ) );
#endif // CIDBG == 1
// fresh test
CFreshTest::IndexSource is;
is = pFreshTest->IsCorrectIndex(pCurSrc->IndexId(),wid);
#if CIDBG==1
if ( CFreshTest::Master == is )
{
Win4Assert( 0 != _pCurrentMasterIndex );
if ( pCurSrc->IndexId() != _pCurrentMasterIndex->GetId() )
ciDebugOut(( DEB_WARN,
"src iid 0x%x, master iid 0x%x, target iid 0x%x\n",
pCurSrc->IndexId(),
_pCurrentMasterIndex->GetId(),
_pTargetMasterIndex->GetId() ));
Win4Assert( pCurSrc->IndexId() ==
_pCurrentMasterIndex->GetId() );
}
#endif // CIDBG==1
if ( is != CFreshTest::Invalid )
{
#ifdef RELEVANT_WORDS_ENABLED
if ((is == CFreshTest::Shadow) && fPossibleRW)
RelWord.Add(wid,pCurSrc->OccurrenceCount());
#endif // RELEVANT_WORDS_ENABLED
cWids++;
_pCompr->PutWorkId(wid, pCurSrc->MaxOccurrence(), pCurSrc->OccurrenceCount());
for (OCCURRENCE occ = pCurSrc->Occurrence();
occ != OCC_INVALID;
occ = pCurSrc->NextOccurrence())
{
_pCompr->PutOccurrence(occ);
}
}
} // for workid
//
// Tell the splitkey object to remember the current position
// if it is a splitkey candidate.
//
_pTrackIdxSplitKey->BeginNewKey( *pkey, bitOffIndex );
#ifdef KEYLIST_ENABLED
KEYID kid = _pNewKeyList->PutKey( pkey, bitOffKeyLst );
#ifdef RELEVANT_WORDS_ENABLED
//
// Compute ranks and possibly add keyid as relevant word
// for each wid containing the key
//
if (kid != kidInvalid && fPossibleRW)
RelWord.DoneWithKey(kid,widMax,cWids);
#endif // RELEVANT_WORDS_ENABLED
_pTrackKeyLstSplitKey->BeginNewKey( *pkey,
bitOffKeyLst,
_pNewKeyList->MaxWorkId() );
#else // !KEYLIST_ENABLED
_pNewKeyList->AddKey();
#endif // !KEYLIST_ENABLED
//
// Add directory entry, if necessary
//
if ( bitOffIndex.Page() != page )
{
page = bitOffIndex.Page();
// Protect the directory and add the key
CReleasableLock lock( _mutex,
_storage.SupportsShrinkFromFront() );
GetTargetDir().Add( bitOffIndex, *pkey );
}
else
{
#if CIDBG==1
Win4Assert (
page == GetTargetDir().GetBitOffsetLastAdded().Page()
);
#endif // CIDBG
}
*keyLast = *pkey;
//
// There's no point in special abort code. We have to handle
// exceptions anyway.
//
ciFAILTEST( STATUS_DISK_FULL );
if ( _fAbortMerge || partn.IsCleaningUp() )
{
ciDebugOut(( DEB_ITRACE, "Aborting Merge\n" ));
_fAbortMerge=FALSE; // for next time around
THROW( CException( STATUS_UNSUCCESSFUL ) );
}
//
// Have we processed & written out MASTER_MERGE_INTERVAL bytes
// to disk? If so, bring them online within the new master index
// and remove them from the current master index as follows:
//
// 1. Take a lock to prevent further queries on pages from
// the current master index.
//
// 2. Ask the _pNewMasterIndex for the highest key which
// has made it do disk on the last page which itself
// has also bee flushed to disk. This key is the
// new split key.
//
// 3. Update the directory for the new master index.
//
// 4. Write the split key to the master log.
//
// 5. Shrink from front the current master index upto
// but not including the lesser of the last page which
// is in use by a query and the number of pages moved
// to the new master index. Can't sff if there
// are mapped sections, so free the stream first.
//
// 6. Release the lock
//
//
if ( (bitOffIndex.Delta(StartBitOffset) > MasterMergeCheckpointInterval) &&
_pTrackIdxSplitKey->IsNewKeyFound() )
{
// Step #1
CLock lock( _mutex );
//
// Checkpoint our state to the master merge log.
// Step #2
//
BitOffset flushBitOff;
xSplitKeyInfo.GetReference() = _pTrackIdxSplitKey->GetSplitKey( flushBitOff );
Win4Assert(
CiPageToCommonPage( flushBitOff.Page()) >
CiPageToCommonPage( xSplitKeyInfo->GetEndOffset().Page() )
);
//
// Flush all the pages upto and including the "flushBitOff".
// This will guarantee that the page containing the split
// key will never be touched again.
//
_pTargetSink->Flush( TRUE );
xMMergeLog->SetIdxSplitKeyInfo( xSplitKeyInfo->GetKey(),
xSplitKeyInfo->GetBeginOffset(),
xSplitKeyInfo->GetEndOffset() );
//
// Step #3
//
GetTargetDir().LokFlushDir( xSplitKeyInfo->GetKey() );
#ifdef KEYLIST_ENABLED
//
// If we have a split key for the key list, then we should
// go ahead and flush it.
//
const CSplitKeyInfo & keylstSplitKey =
_pTrackKeyLstSplitKey->GetSplitKey( flushBitOff );
if ( _pTrackKeyLstSplitKey->IsNewKeyFound() )
{
_pNewKeyList->Flush();
xMMergeLog->SetKeyListSplitKeyInfo(
keylstSplitKey.GetKey(),
keylstSplitKey.GetBeginOffset(),
keylstSplitKey.GetEndOffset() );
xMMergeLog->SetKeyListWidMax( keylstSplitKey.GetWidMax() );
_pTrackKeyLstSplitKey->ClearNewKeyFound();
}
#else // !KEYLIST_ENABLED
xMMergeLog->SetKeyListWidMax( _pNewKeyList->MaxWorkId() );
#endif // !KEYLIST_ENABLED
// Step #4
//
xMMergeLog->CheckPoint();
//
// Step #4.5
//
_idxSplitKeyInfo = xSplitKeyInfo.GetReference();
//
// Step #5
//
if ( 0 != _pCurrentMasterIndex )
{
pCurSrc->FreeStream();
ShrinkFromFront( xSplitKeyInfo->GetKey() );
pCurSrc->RefillStream();
}
//
// Mark that we have used up this split key.
//
_pTrackIdxSplitKey->ClearNewKeyFound();
//
// Step #6
//
StartBitOffset = bitOffIndex;
//
// Update the size in the new master index.
//
_pTargetMasterIndex->GetIndex().UpdatePageCount( *_pTargetSink );
//
// Write the new % complete for the merge
//
mergeProgress.Update( _pTargetMasterIndex->GetIndex().PagesInUse() *
100 /
finalIndexSize );
} // checkpoint
} // for key
#ifdef RELEVANT_WORDS_ENABLED
_pRWStore = RelWord.AcquireStore();
ciDebugOut (( DEB_ITRACE,"grabbing _pRWStore: %lx\n",_pRWStore));
#endif // RELEVANT_WORDS_ENABLED
ciDebugOut(( DEB_ITRACE | DEB_PENDING, "%d keys in index\n", cKey ));
}
CKeyBuf *pKeyMax = new CKeyBuf;
SKeyBuf sKeyMax(pKeyMax);
sKeyMax->FillMax();
sKeyMax->SetPid(pidContents);
BOOL fMaxKeyAddedNow = FALSE;
BitOffset bitOffEnd;
if ( !_pCompr->IsAtSentinel() )
{
// add sentinel key
_pCompr->PutKey(pKeyMax, 1, bitOffIndex);
_pCompr->GetOffset( bitOffEnd );
fMaxKeyAddedNow = TRUE;
}
CLock lock( _mutex );
//
// Destroy the compressor, etc and clean up the master merge state.
//
_pCompr->FreeStream();
CleanupMMergeState();
//
// The compressor must be destroyed before we do the final flush.
// O/W, the last page (which is being used by the compressor) will
// not get flushed. We cannot update the directory and the master
// merge state until the index data is written out to disk.
//
_pTargetSink->Flush();
ciFAILTEST( STATUS_LOG_FILE_FULL );
if ( fMaxKeyAddedNow )
{
if ( page != bitOffIndex.Page() )
{
ciDebugOut(( DEB_ITRACE,
"Adding max key to directory. Page:0x%X Offset:0x%X\n",
bitOffIndex.Page(), bitOffIndex.Offset() ));
GetTargetDir().Add( bitOffIndex, *pKeyMax );
}
GetTargetDir().LokFlushDir( *pKeyMax );
GetTargetDir().LokBuildDir( *pKeyMax );
xSplitKeyInfo->SetBeginOffset(bitOffIndex);
xSplitKeyInfo->SetEndOffset(bitOffEnd);
xSplitKeyInfo->SetKey( *pKeyMax );
ciFAILTEST( STATUS_LOG_FILE_FULL );
xMMergeLog->SetIdxSplitKeyInfo( xSplitKeyInfo->GetKey(),
xSplitKeyInfo->GetBeginOffset(),
xSplitKeyInfo->GetEndOffset() );
xMMergeLog->CheckPoint();
ciFAILTEST( STATUS_LOG_FILE_FULL );
//
// The in-memory split key information MUST be updated ONLY
// after the checkpoint to disk is succesfully done.
//
_idxSplitKeyInfo = xSplitKeyInfo.GetReference();
}
_pTargetSink->ShrinkToFit();
_pTargetMasterIndex->GetIndex().UpdatePageCount( *_pTargetSink );
delete _pTargetSink;
_pTargetSink = 0;
//
// No need to reopen the stream as read-only if the stream supports
// SFF, since we need to leave it as writable for the next master
// merge anyway.
//
if ( !_storage.SupportsShrinkFromFront() )
{
fReOpenTargetMaster = TRUE;
_pTargetMasterIndex->GetIndex().Reopen( FALSE );
fReOpenTargetMaster = FALSE;
}
//
// The merge is 100% complete.
//
mergeProgress.Update( 100 );
}
CATCH( CException, e )
{
_pIndSnap->ResetFreshTest();
//
// The CMasterMerge object owns the key list. We should 0 it
// to prevent having dangling references.
//
_pNewKeyList = 0;
CleanupMMergeState();
delete _pTargetSink;
_pTargetSink = 0;
if ( fReOpenTargetMaster )
_pTargetMasterIndex->GetIndex().Reopen( _storage.SupportsShrinkFromFront() );
// Really bad errors indicate the index is corrupt.
SCODE scE = e.GetErrorCode();
if ( STATUS_INTEGER_DIVIDE_BY_ZERO == scE ||
STATUS_ACCESS_VIOLATION == scE ||
STATUS_IN_PAGE_ERROR == scE )
{
ciDebugOut(( DEB_ERROR,
"Corrupt index or directory, caught 0x%x\n", scE ));
_storage.ReportCorruptComponent( L"MasterMerge" );
THROW( CException( CI_CORRUPT_DATABASE ) );
}
RETHROW();
}
END_CATCH
_pIndSnap->ResetFreshTest();
_pNewKeyList = 0;
#ifdef KEYLIST_ENABLED
pNewKeyList->Done( _fAbortMerge );
#endif // KEYLIST_ENABLED
ciDebugOut(( DEB_ITRACE, "Master merge complete\n" ));
} //Merge
//+---------------------------------------------------------------------------
//
// Member: CMasterMergeIndex::Reload, private
//
// Synopsis: Restores the data for the split keys used for during a
// master merge as well as the index directory.
//
// History: 25-Apr-94 DwightKr Copied from StartMasterMerge
// 23-Aug-94 SrikantS Moved from CMasterMergeIndex
//
//----------------------------------------------------------------------------
void CMasterMergeIndex::ReloadMasterMerge( CMMergeLog & mMergeLog )
{
Win4Assert( 0 != _pTargetMasterIndex );
Win4Assert( !_fStateLoaded );
//
// Index Split Key tracking variables.
//
CKeyBuf * pIdxSplitKey = new CKeyBuf();
SKeyBuf idxSplitKey(pIdxSplitKey);
idxSplitKey->FillMin();
BitOffset idxBeginBitOff;
idxBeginBitOff.Init(0,0);
BitOffset idxEndBitOff;
idxEndBitOff.Init(0,0);
mMergeLog.GetIdxSplitKeyInfo( *pIdxSplitKey,
idxBeginBitOff,
idxEndBitOff );
RestoreIndexDirectory( *pIdxSplitKey, MaxWorkId(), idxEndBitOff );
ciDebugOut (( DEB_ITRACE, "widMax passed to compressor: %ld\n", MaxWorkId() ));
_pTargetMasterIndex->GetIndex().SetUsedPagesCount( idxEndBitOff.Page() + 1 );
_idxSplitKeyInfo.SetBeginOffset(idxBeginBitOff);
_idxSplitKeyInfo.SetEndOffset(idxEndBitOff);
_idxSplitKeyInfo.SetKey( *pIdxSplitKey );
_fStateLoaded = TRUE;
} //ReloadMasterMerge
//+---------------------------------------------------------------------------
//
// Member: CMasterMergeIndex::StartMasterMerge, private
//
// Synopsis: Initializes the merge cursor used to merge the source
// indexes.
//
// Arguments: [indSnap] -- array of indexes to be merged
// [mMergeLog] -- master merge log
// [pNewKeyList] -- keyList to update with missing keys
//
// History: 20-Apr-94 SrikantS Created
// 25-Apr-94 DwightKr Moved some functionality to
// ReloadMasterMerge()
//
//----------------------------------------------------------------------------
CKeyCursor *
CMasterMergeIndex::StartMasterMerge( CIndexSnapshot & indSnap,
CMMergeLog & mMergeLog,
CWKeyList * pNewKeyList )
{
//
// Create the target index sink for write access.
//
ciFAILTEST( CI_CORRUPT_DATABASE );
//
// In user mode, we have to Restore the mastermerge start if it is not
// already restored.
//
if ( !_fStateLoaded )
{
ciDebugOut(( DEB_ITRACE, "Reloading MasterMergeState\n" ));
ReloadMasterMerge( mMergeLog );
}
CPhysIndex & idx = _pTargetMasterIndex->GetIndex();
PMmStream * stream = idx.DupReadWriteStream( PStorage::eOpenForWrite );
XPtr<PMmStream> sStream(stream);
_pTargetSink = new CPhysIndex( idx,
PStorage::eOpenForWrite,
sStream );
_pTargetSink->SetPageGrowth( EIGHT_MEGABYTES / CI_PAGE_SIZE );
#if CIDBG == 1
if ( _idxSplitKeyInfo.GetEndOffset().Page() != 0 )
{
ULONG * pul = _pTargetSink->BorrowBuffer( _idxSplitKeyInfo.GetEndOffset().Page() );
Win4Assert( 0 != *pul );
_pTargetSink->ReturnBuffer( _idxSplitKeyInfo.GetEndOffset().Page() );
}
#endif
//
// Delete all the keys in the directory after the split key.
//
{
//
// Must serialize access to the B_Tree when we are making
// structural modifications to it.
//
CLock lock( _mutex );
GetTargetDir().DeleteKeysAfter( _idxSplitKeyInfo.GetKey() );
}
//
// If we are restarting a 'paused' master merge, then restore the
// persistent decompressor to its previous state. Else, just
// build a decompressor and initialize it state to start at the
// front of the physical index.
//
Win4Assert( !_pTrackIdxSplitKey );
ciFAILTEST( STATUS_NO_MEMORY );
_pTrackIdxSplitKey = new CTrackSplitKey( _idxSplitKeyInfo.GetKey(),
_idxSplitKeyInfo.GetBeginOffset(),
_idxSplitKeyInfo.GetEndOffset() );
ciFAILTEST( STATUS_NO_MEMORY );
_pCompr = new CPersComp( *_pTargetSink,
MaxWorkId(),
_idxSplitKeyInfo.GetEndOffset(),
_idxSplitKeyInfo.GetBeginOffset(),
_idxSplitKeyInfo.GetKey() );
_pTargetSink->SetUsedPagesCount( _idxSplitKeyInfo.GetEndOffset().Page() + 1 );
//
// Build the merge cursor and position it at the key just after the
// split key. If a master merge is NOT being restarted, this
// positions the merge cursor at the first key.
//
ciFAILTEST( STATUS_NO_MEMORY );
CMergeSourceCursor pCurSrc ( indSnap, &_idxSplitKeyInfo.GetKey() );
if ( pCurSrc.IsEmpty() )
{
ciDebugOut (( DEB_ITRACE, "No merge cursor created\n" ));
return(0);
}
ciDebugOut(( DEB_ITRACE, "widMax passed to compressor: %ld\n",
MaxWorkId() ));
if ( !_idxSplitKeyInfo.GetKey().IsMinKey() )
{
Win4Assert( AreEqual(pCurSrc->GetKey(), &_idxSplitKeyInfo.GetKey()) );
ciFAILTEST( CI_CORRUPT_DATABASE );
pCurSrc->GetNextKey();
}
#ifdef KEYLIST_ENABLED
//
// Setup the keylist splitKey tracking variables.
//
ciFAILTEST( STATUS_NO_MEMORY );
CKeyBuf * pKeylstSplitKey = new CKeyBuf();
SKeyBuf keylstSplitKey(pKeylstSplitKey);
keylstSplitKey->FillMin();
BitOffset keylstBeginBitOff;
keylstBeginBitOff.Init(0,0);
BitOffset keylstEndBitOff;
keylstEndBitOff.Init(0,0);
mMergeLog.GetKeyListSplitKeyInfo( *pKeylstSplitKey,
keylstBeginBitOff,
keylstEndBitOff );
//
// Flag set to TRUE if the keylist split key is > the index list split
// key.
//
BOOL fKLSplitKeyBigger = FALSE;
if ( _idxSplitKeyInfo.GetKey().IsMinKey() ||
pKeylstSplitKey->Compare( _idxSplitKeyInfo.GetKey() ) > 0 )
{
*pKeylstSplitKey = _idxSplitKeyInfo.GetKey();
fKLSplitKeyBigger = TRUE;
}
Win4Assert( _pTrackIdxSplitKey );
RestoreKeyListDirectory( _idxSplitKeyInfo.GetKey(),
MaxWorkId(),
pNewKeyList,
*pKeylstSplitKey );
Win4Assert( !_pTrackKeyLstSplitKey );
ciFAILTEST( STATUS_NO_MEMORY );
if ( !fKLSplitKeyBigger )
{
_pTrackKeyLstSplitKey = new CTrackSplitKey( *pKeylstSplitKey,
keylstBeginBitOff,
keylstEndBitOff
);
}
else
{
//
// Just set it to be the min key with 0,0 .
//
_pTrackKeyLstSplitKey = new CTrackSplitKey();
}
#endif // KEYLIST_ENABLED
return pCurSrc.Acquire();
} //StartMasterMerge
//+---------------------------------------------------------------------------
//
// Member: CMasterMergeIndex::CleanupMMergeState, private
//
// Synopsis: Free all memory resources used for this master merge.
//
// History: 20-Apr-94 SrikantS Created
//
//----------------------------------------------------------------------------
void CMasterMergeIndex::CleanupMMergeState()
{
//
// We will create safe pointers instead of calling delete because we
// want to be able to cope with exceptions thrown in the destructors
// also.
//
#ifdef KEYLIST_ENABLED
delete _pTrackKeyLstSplitKey;
_pTrackKeyLstSplitKey = 0;
#endif // KEYLIST_ENABLED
delete _pTrackIdxSplitKey;
_pTrackIdxSplitKey = 0;
// If the stream is still unflushed in the compressor, it isn't
// guaranteed to be flushed by this destructor. This destructor will
// not fail for any reason including failure to flush, so don't rely
// on this in success code paths.
delete _pCompr;
_pCompr = 0;
} //CleanupMMergeState
//+---------------------------------------------------------------------------
//
// Member: CMasterMergeIndex::ShrinkFromFront, private
//
// Synopsis: Shrinks the index, from the front, to release the disk
// associated with the space before the split key.
//
// Parameters: [keyBuf] -- contents of last key which can be deleted
// from the current master index
//
// History: 01-Aug-94 DwightKr Created
//
//----------------------------------------------------------------------------
void CMasterMergeIndex::ShrinkFromFront( const CKeyBuf & keyBuf )
{
//
// If there is no current master index, we have nothing to shrink
//
if ( !_pCurrentMasterIndex )
return;
//
// Locate the page # of the key in the OFS btree which can be used
// to locate this splitkey. On a restart, the merge will seek
// to the key which is the nearest key less than the split
// key stored in the OFS btree. Hence, we need to get the offset of
// the key in the OFS btree which is equal to or less than the splitkey.
// This offset is returned by the COfsdir Seek method.
//
BitOffset offset;
_pCurrentMasterIndex->GetDirectory().Seek( keyBuf, 0, offset );
ULONG ulFirstPageInUse = offset.Page();
ULONG ulMinPageInCache = MAXULONG;
if ( _pCurrentMasterIndex->GetIndex().MinPageInUse( ulMinPageInCache ) )
{
//
// The cache has some pages in it and so we must take the minimum
// of the cache page and the directory.
//
Win4Assert( MAXULONG != ulMinPageInCache );
ulFirstPageInUse = min( ulFirstPageInUse, ulMinPageInCache );
}
else
{
//
// The cache is empty implying that there none of the pages from
// the current master are in use. We must have exhausted all the
// keys from the current master.
//
}
//
// If we can't decommit any pages because they are all in use, then get
// out now.
//
Win4Assert( ulFirstPageInUse >= _ulFirstPageInUse );
ULONG numPages = ulFirstPageInUse - _ulFirstPageInUse;
if ( numPages > 0 )
_ulFirstPageInUse += _pCurrentMasterIndex->ShrinkFromFront( _ulFirstPageInUse,
numPages );
} //ShrinkFromFront
//+---------------------------------------------------------------------------
//
// Member: CMasterMergeIndex::QueryCursor, public
//
// Synopsis: Return a cursor for the master index during master merge
//
// Returns: A new cursor.
//
// History: 24-Apr-91 KyleP Created.
// 30-Mar-94 DwightKr Copied here from CPersIndex
//
//----------------------------------------------------------------------------
CKeyCursor * CMasterMergeIndex::QueryCursor()
{
CKey key;
key.FillMin();
return QueryKeyCursor( &key );
}
//+---------------------------------------------------------------------------
//
// Member: CMasterMergeIndex::QuerySplitCursor, private
//
// Synopsis: Return a cursor for the logical master index during master
// merge
//
// Returns: A new cursor.
//
// History: 30-Mar-94 DwightKr Created
//
// Notes: Three cases of interest can be found during a master merge:
//
// 1. Master merge for the first time, no 'current master index'
// hence we return a null for the cursor to the master index.
//
// 2. Master merge hasn't written a split key as yet, return a
// cursor to decompress the 'current master index'.
//
// 3. All other cases, build a cursor to decompress and span
// both the 'current master index' and the 'new master index.'
//
//----------------------------------------------------------------------------
CKeyCursor * CMasterMergeIndex::QuerySplitCursor(const CKey * pKey)
{
if ( 0 == _pCurrentMasterIndex )
return 0;
// Take the lock if the volume supports shrink from front
CReleasableLock lock( _mutex, _storage.SupportsShrinkFromFront() );
if ( _storage.SupportsShrinkFromFront() &&
!_idxSplitKeyInfo.GetKey().IsMinKey() )
{
return new CMPersDeComp( _pCurrentMasterIndex->GetDirectory(),
_pCurrentMasterIndex->GetId(),
_pCurrentMasterIndex->GetIndex(),
_pCurrentMasterIndex->MaxWorkId(),
GetTargetDir(),
GetId(),
_pTargetMasterIndex->GetIndex(),
pKey,
MaxWorkId(),
_idxSplitKeyInfo,
_mutex );
}
else
{
//
// Don't use the split cursor. The current master index is valid.
//
BitOffset posKey;
CKeyBuf keyInit;
_pCurrentMasterIndex->GetDirectory().Seek( *pKey,
&keyInit,
posKey );
return new CPersDeComp( _pCurrentMasterIndex->GetDirectory(),
_pCurrentMasterIndex->GetId(),
_pCurrentMasterIndex->GetIndex(),
posKey,
keyInit,
pKey,
_pCurrentMasterIndex->MaxWorkId() );
}
} //QuerySplitCursor
//+---------------------------------------------------------------------------
//
// Member: CMasterMergeIndex::QueryKeyCursor, public
//
// Synopsis: Return a key cursor for the master index when restarting a
// master merge
//
// Returns: A new cursor.
//
// History: 12-Apr-94 DwightKr Created.
//
//----------------------------------------------------------------------------
CKeyCursor * CMasterMergeIndex::QueryKeyCursor(const CKey * pKey)
{
// Take the lock if the volume supports shrink from front
CReleasableLock lock( _mutex, _storage.SupportsShrinkFromFront() );
XPtr<CKeyCursor> xCursor( QuerySplitCursor( pKey ) );
if ( !xCursor.IsNull() && (xCursor->GetKey() == 0) )
xCursor.Free();
return xCursor.Acquire();
} //QueryKeyCursor
//+---------------------------------------------------------------------------
//
// Member: CMasterMergeIndex::QueryCursor, public
//
// Synopsis: Return a cursor for the master index during master merge
//
// Arguments: [pkey] -- Key to initially seek for.
// [isRange] -- TRUE for range query
// [cMaxNodes] -- Max node (key) count
//
// Returns: A new cursor.
//
// History: 24-Apr-91 KyleP Created.
// 30-Mar-94 DwightKr Copied here from CPersIndex and
// modified to span multiple masters
//
//----------------------------------------------------------------------------
COccCursor * CMasterMergeIndex::QueryCursor( const CKey * pKey,
BOOL isRange,
ULONG & cMaxNodes )
{
Win4Assert( cMaxNodes > 0 );
if (isRange)
{
CKey keyEnd;
keyEnd.FillMax (*pKey);
return QueryRangeCursor( pKey, &keyEnd, cMaxNodes );
}
if (pKey->Pid() == pidAll)
return QueryRangeCursor( pKey, pKey, cMaxNodes );
cMaxNodes--;
if ( 0 == cMaxNodes )
{
ciDebugOut(( DEB_WARN, "Node limit reached in CMasterMergeIndex::QueryCursor.\n" ));
THROW( CException( STATUS_TOO_MANY_NODES ) );
}
XPtr<CKeyCursor> xCursor( QuerySplitCursor( pKey ) );
if ( ( !xCursor.IsNull() ) &&
( xCursor->GetKey() == 0
|| !pKey->MatchPid (*xCursor->GetKey())
|| pKey->CompareStr(*xCursor->GetKey()) != 0 )
)
{
xCursor.Free();
}
return xCursor.Acquire();
} //QueryCursor
//+---------------------------------------------------------------------------
//
// Member: CMasterMergeIndex::QueryRangeCursor, public
//
// Synopsis: Return a range cursor for the master index during master merge
//
// Arguments: [pkey] -- Key at beginning of the range.
// [pKeyEnd] -- Key at the end of the range.
// [cMaxNodes] -- Max node (key) count
//
// Returns: A new cursor.
//
// History: 11-Dec-91 AmyA Created.
// 31-Jan-92 AmyA Moved code to CreateRange().
// 30-Mar-94 DwightKr Copied here from CPersIndex and
// modified to span multiple masters
//
//----------------------------------------------------------------------------
COccCursor * CMasterMergeIndex::QueryRangeCursor( const CKey * pkey,
const CKey * pKeyEnd,
ULONG & cMaxNodes )
{
Win4Assert( cMaxNodes > 0 );
COccCurStack curStk;
// Take the lock if the volume supports shrink from front
CReleasableLock lock( _mutex, _storage.SupportsShrinkFromFront() );
CreateRange( curStk, pkey, pKeyEnd, cMaxNodes );
return curStk.QuerySynCursor( MaxWorkId() );
} //QueryRangeCursor
//+---------------------------------------------------------------------------
//
// Member: CMasterMergeIndex::QuerySynCursor, public
//
// Synopsis: Return a synonym cursor for master index during master merge
//
// Arguments: [keyStk] -- Stack of keys to be searched for.
// [isRange] -- Whether or not this is a range search.
// [cMaxNodes] -- Max node (key) count
//
// Returns: A new cursor.
//
// History: 31-Jan-92 AmyA Created.
// 30-Mar-94 DwightKr Copied here from CPersIndex and
// modified to span multiple masters
//
//----------------------------------------------------------------------------
COccCursor * CMasterMergeIndex::QuerySynCursor( CKeyArray & keyArr,
BOOL isRange,
ULONG & cMaxNodes )
{
Win4Assert( cMaxNodes > 0 );
if ( 0 == _pCurrentMasterIndex )
return 0;
COccCurStack curStk;
int keyCount = keyArr.Count();
// Take the lock if the volume supports shrink from front
CReleasableLock lock( _mutex, _storage.SupportsShrinkFromFront() );
for (int i = 0; i < keyCount; i++)
{
CKey& key = keyArr.Get(i);
if (isRange)
{
CKey keyEnd;
keyEnd.FillMax(key);
CreateRange( curStk, &key, &keyEnd, cMaxNodes );
}
else if ( key.Pid() == pidAll )
{
CreateRange ( curStk, &key, &key, cMaxNodes );
}
else
{
cMaxNodes--;
if ( 0 == cMaxNodes )
{
ciDebugOut(( DEB_WARN, "Node limit reached in CMasterMergeIndex::QuerySynCursor.\n" ));
THROW( CException( STATUS_TOO_MANY_NODES ) );
}
BitOffset posKey;
if ( _storage.SupportsShrinkFromFront() &&
key.CompareStr( _idxSplitKeyInfo.GetKey() ) <= 0 )
GetTargetDir().Seek ( key, 0, posKey );
else
_pCurrentMasterIndex->GetDirectory().Seek ( key, 0, posKey );
XPtr<CKeyCursor> xCursor( QuerySplitCursor( &key ) );
curStk.Push( xCursor.GetPointer() );
xCursor.Acquire();
}
}
return curStk.QuerySynCursor( MaxWorkId() );
} //QuerySynCursor
//+---------------------------------------------------------------------------
//
// Member: CMasterMergeIndex::CreateRange, private
//
// Synopsis: Adds all cursors with keys between pkey and pKeyEnd to curStk.
//
// Arguments: [curStk] -- CKeyCurStack to add cursors to.
// [pkey] -- Key at beginning of range.
// [pKeyEnd] -- End of key range.
// [cMaxNodes] -- Max node (key) count
//
// History: 31-Jan-92 AmyA Created.
// 30-Mar-94 DwightKr Copied here from CPersIndex and
// modified to span multiple masters
//
//----------------------------------------------------------------------------
void CMasterMergeIndex::CreateRange( COccCurStack & curStk,
const CKey * pKeyStart,
const CKey * pKeyEnd,
ULONG & cMaxNodes )
{
Win4Assert( cMaxNodes > 0 );
cMaxNodes--;
if ( 0 == cMaxNodes )
{
ciDebugOut(( DEB_WARN, "Node limit reached in CPersIndex::CreateRange.\n" ));
THROW( CException( STATUS_TOO_MANY_NODES ) );
}
CKeyCursor * pCursor = QuerySplitCursor( pKeyStart );
if ( !pCursor )
return;
XPtr<CCursor> xCursor( pCursor );
const CKeyBuf * pKeyCurrent = pCursor->GetKey();
if ( 0 == pKeyCurrent )
return;
PROPID pid = pKeyStart->Pid();
curStk.Push(pCursor);
xCursor.Acquire();
ciDebugOut(( DEB_ITRACE, "First key %.*ws, pid %d\n",
pKeyCurrent->StrLen(), pKeyCurrent->GetStr(), pKeyCurrent->Pid() ));
do
{
if (pid != pidAll) // exact pid match
{
// skip wrong pids
while (pid != pKeyCurrent->Pid())
{
#if CIDBG == 1 //------------------------------------------
if (pKeyCurrent)
{
ciDebugOut(( DEB_ITRACE, " skip: %.*ws, pid %d, wid %d\n",
pKeyCurrent->StrLen(),
pKeyCurrent->GetStr(),
pKeyCurrent->Pid(),
pCursor->WorkId() ));
}
else
ciDebugOut(( DEB_ITRACE, " <NULL> key\n" ));
#endif //--------------------------------------------------
pKeyCurrent = pCursor->GetNextKey();
if (pKeyCurrent == 0
|| pKeyEnd->CompareStr(*pKeyCurrent) < 0 )
break;
}
// either pid matches or we have overshot
// i.e. different pids and current string > end
}
if (pKeyCurrent == 0 || !pKeyEnd->MatchPid(*pKeyCurrent)
|| pKeyEnd->CompareStr (*pKeyCurrent) < 0 )
{
break; // <--- LOOP EXIT
}
cMaxNodes--;
if ( 0 == cMaxNodes )
{
ciDebugOut(( DEB_WARN, "Node limit reached in CPersIndex::CreateRange.\n" ));
THROW( CException( STATUS_TOO_MANY_NODES ) );
}
// Clone the previous cursor...
const CKey & key = *pCursor->GetKey();
pCursor = QuerySplitCursor( &key );
xCursor.Set( pCursor );
// Add it to avoid memory leaks if GetNextKey fails
curStk.Push(pCursor); // may be wrong pid
xCursor.Acquire();
// increment the added cursor
pKeyCurrent = pCursor->GetNextKey();
#if CIDBG == 1
if (pKeyCurrent)
{
ciDebugOut(( DEB_ITRACE, " %.*ws, wid %d\n",
pKeyCurrent->StrLen(), pKeyCurrent->GetStr(), pCursor->WorkId() ));
}
else
ciDebugOut(( DEB_ITRACE, " <NULL> key\n" ));
#endif
} while ( pKeyCurrent );
// Since we have one more cursor in curStk than we wanted...
curStk.DeleteTop();
} //CreateRange
//+---------------------------------------------------------------------------
//
// Member: CMasterMergeIndex::Remove, public
//
// Synopsis: Closes any open streams/indexes associated with this new
// master index.
//
// History: 30-Mar-94 DwightKr Copied here from CPersIndex
//
//----------------------------------------------------------------------------
void CMasterMergeIndex::Remove()
{
Win4Assert( 0 == _pTargetSink );
Win4Assert( !InUse() );
Win4Assert( IsZombie() );
Win4Assert( !_pTargetMasterIndex->InUse() );
// Win4Assert( !"If you are emptying ci press go.O/W call Srikants/DwightKr" );
ciFAILTEST( STATUS_NO_MEMORY );
_pTargetMasterIndex->Remove();
delete _pTargetMasterIndex;
_pTargetMasterIndex = 0;
if ( _pCurrentMasterIndex && !_pCurrentMasterIndex->InUse() )
{
_pCurrentMasterIndex->Remove();
delete _pCurrentMasterIndex;
_pCurrentMasterIndex = 0;
}
}
//+---------------------------------------------------------------------------
//
// Function: AcquireCurrentAndTarget
//
// Synopsis: Transfers the ownership of the current and target master
// indexes to the caller.
//
// Arguments: [ppCurrent] -- On output, will have the pointer to the
// current master (if any). NULL o/w
// [ppTarget] -- On output, will have the pointer to the
// target master. Will be non-NULL.
//
// History: 9-29-94 srikants Created
//
// Notes: This method must be called atmost ONCE during the lifetime
// of a CMasterMergeIndex. Also, this must be called after
// this has been zombified and there are no outstanding
// queries.
//
//----------------------------------------------------------------------------
void CMasterMergeIndex::AcquireCurrentAndTarget(
CPersIndex ** ppCurrent, CPersIndex ** ppTarget )
{
Win4Assert( IsZombie() );
Win4Assert( !InUse() );
*ppCurrent = _pCurrentMasterIndex;
*ppTarget = _pTargetMasterIndex;
_pCurrentMasterIndex = 0;
_pTargetMasterIndex = 0;
}
//+---------------------------------------------------------------------------
//
// Function: RestoreIndexDirectory
//
// Synopsis: This method restores the "directory" for the new index
// in a restarted master merge. It scans the index until
// it sees the split key and adds it to the directory.
//
// Effects:
//
// Arguments: [splitKey] - The split key of the new master index.
// [widMax] - The largest WORKID in the new master index
// [bitOffStart] - Offset in bits to the end of splitkey
//
// History: 4-12-94 srikants Created
// 5-01-94 DwightKr Split into two functions
//
// Notes: Only the index directory needs to be restored when the
// CMasterMergeIndex object is rebuild during boot time.
// The keyList directory needs to be rebuild only when
// actually restarting the master merge.
//
// Since it may be quite some time after rebooting a system
// before the master merge restarts, we don't attempt to rebuild
// the keylist here.
//
//----------------------------------------------------------------------------
void CMasterMergeIndex::RestoreIndexDirectory(
const CKeyBuf & idxSplitKey,
WORKID widMax,
const BitOffset & idxBitOffRestart )
{
Win4Assert( 0 != &GetTargetDir() );
if ( idxSplitKey.IsMinKey() )
return;
// Seek to the idxSplitKey in the new master index. Use the directory
BitOffset posKey;
CKeyBuf keyInit;
const CKey key = idxSplitKey;
GetTargetDir().Seek( idxSplitKey, & keyInit, posKey );
CPersDeComp Decomp( GetTargetDir(),
GetId(),
_pTargetMasterIndex->GetIndex(),
posKey,
keyInit,
&key,
widMax,
TRUE, // Use Links
FALSE ); // no dir
BitOffset idxBitOff;
idxBitOff.Init(0,0);
CKeyBuf keyLast;
#if CIDBG == 1
ciDebugOut(( DEB_ITRACE, "restoring index directory\n" ));
keyLast.SetPid(pidContents); // arbitrary but not pidAll
#endif
for ( const CKeyBuf * pKey = Decomp.GetKey();
(0 != pKey) ;
pKey = Decomp.GetNextKey(&idxBitOff) )
{
if ( AreEqual(&idxSplitKey, pKey) )
{
//
// Skip over wid-occurences and position to store the next
// key in the compressor.
//
for ( WORKID widSkipped = Decomp.WorkId();
widInvalid != widSkipped;
widSkipped = Decomp.NextWorkId() )
{
// Null Body
}
ciDebugOut(( DEB_ITRACE, "RestoreIndexDirectory - SplitKey Found \n" ));
break;
}
keyLast = *pKey;
}
//
// Make a sanity check to confirm that the compressor and the
// decompressor arrived at the same offset for the next key.
// It is extremely important that these match - o/w, the resulting
// index will be in a corrupt and unusable state.
//
BitOffset bitOffCurr;
Decomp.GetOffset( bitOffCurr );
ciFAILTEST( CI_CORRUPT_DATABASE );
if ( !idxSplitKey.IsMaxKey() &&
( (bitOffCurr.Page() != idxBitOffRestart.Page()) ||
(bitOffCurr.Offset() != idxBitOffRestart.Offset()) ) )
{
ciDebugOut(( DEB_ERROR,
"Mismatch in computed vs. stored restart offsets\n" ));
ciDebugOut(( DEB_ERROR,
"Computed Page:0x%x Offset:0x%x\n",
bitOffCurr.Page(), bitOffCurr.Offset() ));
ciDebugOut(( DEB_ERROR,
"Saved Page:0x%x Offset:0x%x\n",
idxBitOffRestart.Page(), idxBitOffRestart.Offset() ));
Win4Assert( !"Corrupt master merge index" );
_storage.ReportCorruptComponent( L"MasterMergeIndex2" );
THROW( CException( CI_CORRUPT_DATABASE ) );
}
ciDebugOut(( DEB_ITRACE, "Restart split key is '%ws'\n",
idxSplitKey.GetStr() ));
ciDebugOut(( DEB_ITRACE, "Restart page:offset = 0x%x:0x%x\n",
idxBitOffRestart.Page(), idxBitOffRestart.Offset() ));
ciDebugOut(( DEB_ITRACE, "restored index directory\n" ));
} //RestoreIndexDirectory
#ifdef KEYLIST_ENABLED
//+---------------------------------------------------------------------------
//
// Function: RestoreKeyListDirectory
//
// Synopsis: This method restores the "directory" for the new keylist
// in a restarted master merge. It scans the index until
// it sees the split key and adds it to the directory.
//
// Effects:
//
// Arguments: [splitKey] - The split key of the new master index.
// [widMax] - The largest WORKID in the new master index
// [bitOffStart] - Offset in bits to the end of splitkey
//
// History: 4-12-94 srikants Created
// 5-01-94 DwightKr Moved into this function
//
// Notes: It is assumed that the index directory has already been
// rebuild when the CMasterMergeIndex object was created. At
// this point we are restarting a master merge, and the keyList
// directory is now required.
//
//----------------------------------------------------------------------------
void CMasterMergeIndex::RestoreKeyListDirectory( const CKeyBuf & idxSplitKey,
WORKID widMax,
CWKeyList * pKeyList,
const CKeyBuf & keylstSplitKey
)
{
Win4Assert( &GetTargetDir() );
Win4Assert( _pTargetSink );
if ( idxSplitKey.IsMinKey() )
return;
Win4Assert( keylstSplitKey.Compare( idxSplitKey ) <= 0 );
//
// Seek to the keylstSplitKey in the new master index, and add all
// subsequent keys found in the new master index to the new keylist
// index. These keys are missing from the keylist and need to be
// restored.
//
ciFAILTEST( STATUS_NO_MEMORY );
if ( !GetTargetDir().IsValid() )
{
GetTargetDir().LokBuildDir( idxSplitKey );
}
BitOffset posKey;
const CKey splitKeylst = keylstSplitKey;
// STACKSTACK
XPtr<CKeyBuf> xKeyInit(new CKeyBuf());
GetTargetDir().Seek( keylstSplitKey, xKeyInit.GetPointer(), posKey );
ciFAILTEST( CI_CORRUPT_DATABASE );
// STACKSTACK
XPtr<CPersDeComp> xDecomp(
new CPersDeComp( GetTargetDir(), GetId(),
_pTargetMasterIndex->GetIndex(), posKey,
xKeyInit.GetReference(), &splitKeylst, widMax,
TRUE, // Use Links
TRUE // Use the directory.
) );
const CKeyBuf * pKey;
ULONG page = ULONG(-1);
BitOffset idxBitOff;
idxBitOff.Init(0,0);
BitOffset keylstBitOff;
#if CIDBG == 1
// STACKSTACK
XPtr<CKeyBuf> xKeyLast(new CKeyBuf()); // initialized to min key
xKeyLast->SetPid(pidContents); // arbitrary but not pidAll
#endif
for ( pKey = xDecomp->GetKey();
(0 != pKey) ;
pKey = xDecomp->GetNextKey(&idxBitOff) )
{
if ( pKeyList && (keylstSplitKey.CompareStr( *pKey ) < 0) )
{
//
// pKey is not present in the key list. It must be
// added.
//
pKeyList->PutKey( pKey, keylstBitOff );
}
if ( AreEqual(&idxSplitKey, pKey) )
{
//
// Skip over wid-occurences and position to store the next
// key in the compressor.
//
for ( WORKID widSkipped = xDecomp->WorkId();
widInvalid != widSkipped;
widSkipped = xDecomp->NextWorkId() )
{
// nothing to do.
}
ciDebugOut(( DEB_ITRACE, "RestoreKeyListDirectory - SplitKey Found \n" ));
break;
}
#if CIDBG == 1
xKeyLast.GetReference() = *pKey;
#endif
}
}
//+---------------------------------------------------------------------------
//
// Member: CPersIndex::AcquireRelevantWords, public
//
// Synopsis: Return relevant word key ids computed at the most recent
// master merge. The caller must delete the object returned.
//
// Returns: CRWStore *
//
// History: 25-Apr-94 v-dlee Created
//
//----------------------------------------------------------------------------
CRWStore * CMasterMergeIndex::AcquireRelevantWords()
{
CRWStore *p = _pRWStore;
ciDebugOut (( DEB_ITRACE,"CPersIndex::acquire _pRWStore: %lx\n",_pRWStore));
_pRWStore = 0;
return p;
} //AcquireRelevantWords
//+---------------------------------------------------------------------------
//
// Member: CPersIndex::ComputeRelevantWords, public
//
// Synopsis: Compute and return relevant word key ids
//
// Arguments: [cRows] -- # of wids in pwid array
// [cRW] -- max # of rw keys per wid
// [pwid] -- an array of wids in increasing order whose
// rw key ids are to be returned
// [pKeyList] -- keylist to use in translation of keys to ids
//
// Returns: CRWStore *
//
// History: 25-Apr-94 v-dlee Created
//
//----------------------------------------------------------------------------
CRWStore * CMasterMergeIndex::ComputeRelevantWords(ULONG cRows,ULONG cRW,
WORKID *pwid,CKeyList *pKeyList)
{
Win4Assert( !" Not Yet Implemented" );
#ifdef RELEVANT_WORDS_ENABLED
ciDebugOut((DEB_ITRACE,"ComputeRelevantWords top\n"));
//
// Get the resources needed to do the computation
//
CRelevantWord RelWord(pwid,cRows,cRW);
CPersIndexCursor indCur(this);
CKeyListCursor keylCur(pKeyList);
//
// Walk through the index and find occurances of keys in the wids
//
const CKeyBuf * pKey, * pklKey;
for (pKey = indCur->GetKey(), pklKey = keylCur->GetKey();
pKey != 0; pKey = indCur->GetNextKey())
{
if (pKey->Pid() == pidContents &&
((CKeyBuf * const) pKey)->IsPossibleRW())
{
ULONG cWids = 0;
for (WORKID wid = indCur->WorkId(); wid != widInvalid;
wid = indCur->NextWorkId())
{
cWids++;
if (RelWord.isTrackedWid(wid))
RelWord.Add(wid,indCur->OccurrenceCount());
}
//
// Walk the keylist until we match it up with where the
// index cursor is.
//
while (pklKey->CompareStr(*pKey) != 0)
pklKey = keylCur->GetNextKey();
RelWord.DoneWithKey(pklKey->Pid(),MaxWorkId(),cWids);
}
}
return RelWord.AcquireStore();
#endif // RELEVANT_WORDS_ENABLED
return(0);
} //ComputeRelevantWords
#endif // KEYLIST_ENABLED
//+---------------------------------------------------------------------------
//
// Function: CTrackSplitKey::CTrackSplitKey
//
// Synopsis: The split key tracking constructor. Initializes the object
// to have "min" keys and offsets are all set to the beginning
// of the stream.
//
// Arguments: [splitKey] -- splitkey being tracked
// [bitoffBeginSplit] -- bit offset to beginning of split ket
// [bitoffEndSplit] -- bit offset to end of splitkey
//
// History: 4-12-94 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
CTrackSplitKey::CTrackSplitKey( const CKeyBuf & splitKey,
const BitOffset & bitoffBeginSplit,
const BitOffset & bitoffEndSplit ) :
_fNewSplitKeyFound(FALSE)
{
_splitKey2.SetKey( splitKey );
_splitKey2.SetBeginOffset( bitoffBeginSplit );
_splitKey2.SetEndOffset( bitoffEndSplit );
}
//+---------------------------------------------------------------------------
//
// Function: BeginNewKey
//
// Synopsis: This method informs the split key tracker that a new key
// has been added to the compressor. It will check if the
// previous key and the current key are landing on a different
// page and check if a split key has been found.
//
// Arguments: [newKey] -- The new key added to the compressor
// [beginNewOff] -- Starting offset of the new key. This will
// be the end offset of the current key.
//
// History: 4-19-94 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
void CTrackSplitKey::BeginNewKey( const CKeyBuf & newKey,
const BitOffset & beginNewOff,
WORKID widMax )
{
//
// beginNewOff is also the end of current key. We have to determine
// if there is a new split key.
//
_currKey.SetEndOffset( beginNewOff );
if ( CiPageToCommonPage(_currKey.GetEndOffset().Page()) >
CiPageToCommonPage(_prevKey.GetEndOffset().Page()) )
{
//
// We have a candidate split key in the previous key.
//
_splitKey2 = _splitKey1;
_splitKey1 = _prevKey;
_fNewSplitKeyFound = !_splitKey2.GetKey().IsMinKey();
#if CIDBG == 1
if ( IsNewKeyFound() )
{
ciDebugOut(( DEB_PCOMP,
"Split Key Found At Page 0x%X Offset 0x%X\n",
_splitKey2.GetBeginOffset().Page(),
_splitKey2.GetBeginOffset().Offset() ));
ciDebugOut(( DEB_PCOMP,
"End of Split Key found at page 0x%x offset 0x%x\n",
_splitKey2.GetEndOffset().Page(),
_splitKey2.GetEndOffset().Offset() ));
}
#endif // CIDBG
}
_prevKey = _currKey;
_currKey.SetKey( newKey );
_currKey.SetBeginOffset( beginNewOff );
_currKey.SetWidMax( widMax );
}
//+---------------------------------------------------------------------------
//
// Function: CSplitKeyInfo
//
// Synopsis: Constructor the CSplitKeyInfo
//
// Effects: Initializes the key to be minkey and offsets to 0,0.
//
// History: 4-19-94 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
CSplitKeyInfo::CSplitKeyInfo()
{
_start.Init(0,0);
_end.Init(0,0);
_key.FillMin();
_widMax = widInvalid;
}