995 lines
29 KiB
C++
995 lines
29 KiB
C++
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1991 - 1998.
|
|
//
|
|
// File: PINDEX.CXX
|
|
//
|
|
// Contents: Persistent Index
|
|
//
|
|
// Classes: CPersIndex, CMergeSourceCursor
|
|
//
|
|
// History: 03-Apr-91 BartoszM Created stub.
|
|
// 20-Apr-94 DwightKr Moved CMergeSourceCursor here
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include <pch.cxx>
|
|
#pragma hdrstop
|
|
|
|
#include <pdir.hxx>
|
|
#include <pstore.hxx>
|
|
#include <pidxtbl.hxx>
|
|
#include <rwex.hxx>
|
|
|
|
#include "pindex.hxx"
|
|
#include "pcomp.hxx"
|
|
#include "mcursor.hxx"
|
|
#include "fresh.hxx"
|
|
#include "physidx.hxx"
|
|
#include "pcomp.hxx"
|
|
#include "fretest.hxx"
|
|
#include "indsnap.hxx"
|
|
#include "keylist.hxx"
|
|
#include "partn.hxx"
|
|
|
|
unsigned const FOUR_MEGABYTES = 0x400000;
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPersIndex::Size, public
|
|
//
|
|
// Synopsis: Returns size in pages
|
|
//
|
|
// History: 22-May-92 BartoszM Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
unsigned CPersIndex::Size() const
|
|
{
|
|
return _xPhysIndex->PageSize();
|
|
}
|
|
|
|
void CPersIndex::FillRecord ( CIndexRecord& record )
|
|
{
|
|
record._objectId = ObjectId();
|
|
record._iid = GetId();
|
|
record._type = IsMaster()? itMaster: itShadow;
|
|
record._maxWorkId = MaxWorkId();
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPersIndex::CPersIndex, public
|
|
//
|
|
// Synopsis: Create an empty index
|
|
//
|
|
// Arguments: [id] -- index id
|
|
// [storage] -- physical storage
|
|
//
|
|
// History: 3-Apr-91 BartoszM Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CPersIndex::CPersIndex(
|
|
PStorage & storage,
|
|
WORKID objectId,
|
|
INDEXID iid,
|
|
unsigned c4KPages,
|
|
CDiskIndex::EDiskIndexType idxType ) :
|
|
CDiskIndex( iid, idxType ),
|
|
_sigPersIndex( eSigPersIndex ),
|
|
_storage( storage ),
|
|
_obj( storage.QueryObject( objectId ) ),
|
|
_fAbortMerge( FALSE )
|
|
{
|
|
XPtr<PMmStream> sStream( storage.QueryNewIndexStream( _obj.GetObj(),
|
|
CDiskIndex::eMaster == idxType ) );;
|
|
_xPhysIndex.Set( new CPhysIndex( storage,
|
|
_obj.GetObj(),
|
|
objectId,
|
|
c4KPages,
|
|
sStream ) );
|
|
_xPhysIndex->SetPageGrowth( FOUR_MEGABYTES / CI_PAGE_SIZE );
|
|
Win4Assert( 0 == sStream.GetPointer() );
|
|
|
|
_xDir.Set( _storage.QueryNewDirectory( _obj.GetObj() ) );
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPersIndex::CPersIndex, public
|
|
//
|
|
// Synopsis: Restore an index from storage
|
|
//
|
|
// Arguments: [id] -- index id
|
|
// [storage] -- physical storage
|
|
// [widMax] -- max work id
|
|
// [isMaster] -- Set to TRUE if this is a master index.
|
|
// [fWritable] -- Set to TRUE if various streams should be
|
|
// opened for Write access.
|
|
// [fReadDir] -- should the directory be opened for r or r/w
|
|
//
|
|
// History: 3-Apr-91 BartoszM Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CPersIndex::CPersIndex(
|
|
PStorage & storage,
|
|
WORKID objectId,
|
|
INDEXID iid,
|
|
WORKID widMax,
|
|
CDiskIndex::EDiskIndexType idxType,
|
|
PStorage::EOpenMode mode,
|
|
BOOL fReadDir ) :
|
|
CDiskIndex( iid, idxType, widMax ),
|
|
_sigPersIndex( eSigPersIndex ),
|
|
_storage( storage ),
|
|
_obj( storage.QueryObject( objectId ) ),
|
|
_fAbortMerge( FALSE )
|
|
{
|
|
Win4Assert( PStorage::eOpenForWrite == mode ||
|
|
PStorage::eOpenForRead == mode );
|
|
|
|
PStorage::EOpenMode modeIndex = mode;
|
|
|
|
//
|
|
// Open master indexes writable so we can shrink them from the front
|
|
//
|
|
|
|
if ( CDiskIndex::eMaster == idxType )
|
|
modeIndex = PStorage::eOpenForWrite;
|
|
|
|
PMmStream * pStream = storage.QueryExistingIndexStream( _obj.GetObj(),
|
|
modeIndex );
|
|
XPtr<PMmStream> sStream( pStream );
|
|
_xPhysIndex.Set( new CPhysIndex( storage,
|
|
_obj.GetObj(),
|
|
objectId,
|
|
modeIndex,
|
|
sStream ) );
|
|
Win4Assert( 0 == sStream.GetPointer() );
|
|
|
|
Win4Assert( fReadDir );
|
|
|
|
if ( fReadDir )
|
|
_xDir.Set( _storage.QueryExistingDirectory( _obj.GetObj(), mode ) );
|
|
else
|
|
_xDir.Set( _storage.QueryNewDirectory( _obj.GetObj() ) );
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMergeSourceCursor::CMergeSourceCursor
|
|
//
|
|
// Synopsis: Constructor
|
|
//
|
|
// History: 29-Aug-92 BartoszM Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
CMergeSourceCursor::CMergeSourceCursor ( CIndexSnapshot& indSnap,
|
|
const CKeyBuf * pKey )
|
|
{
|
|
if (0 != pKey)
|
|
{
|
|
CKey SplitKey(*pKey);
|
|
_pCurSrc = indSnap.QueryMergeCursor ( &SplitKey );
|
|
}
|
|
else
|
|
{
|
|
_pCurSrc = indSnap.QueryMergeCursor ();
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMergeSourceCursor::~CMergeSourceCursor
|
|
//
|
|
// Synopsis: Destructor
|
|
//
|
|
// History: 29-Aug-92 BartoszM Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
CMergeSourceCursor::~CMergeSourceCursor ()
|
|
{
|
|
delete _pCurSrc;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPersIndex::Merge, public
|
|
//
|
|
// Synopsis: Merge index(es) into an empty pesistent index.
|
|
//
|
|
// Effects: Fills the persistent index with data from the input
|
|
// indexes.
|
|
// [fresh] is deleted.
|
|
//
|
|
// Arguments: [indSnap] -- array of indexes to be merged
|
|
// [pNewKeyList] -- Keylist to merge (master merge only)
|
|
// [mergeProgress] -- % merge complete
|
|
//
|
|
// 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
|
|
// 25-Oct-95 DwightKr Add merge complete measurement
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CPersIndex::Merge( CIndexSnapshot& indSnap,
|
|
const CPartition & partn,
|
|
CCiFrmPerfCounter & mergeProgress,
|
|
BOOL fGetRW )
|
|
{
|
|
// Calculate max of all input widMaxs
|
|
|
|
#if CIDBG == 1
|
|
unsigned cKey = 0;
|
|
#endif
|
|
|
|
WORKID widMax = indSnap.MaxWorkId();
|
|
|
|
ciDebugOut (( DEB_ITRACE, "Max work id %ld\n", widMax ));
|
|
|
|
SetMaxWorkId ( widMax );
|
|
|
|
CFreshTest* pFreshTest = indSnap.GetFresh();
|
|
|
|
CKeyBuf keyLast;
|
|
|
|
CMergeSourceCursor pCurSrc( indSnap );
|
|
|
|
if ( !pCurSrc.IsEmpty() )
|
|
{
|
|
//
|
|
// Read-ahead on the source indexes results in better merge
|
|
// performance, but slower queries. Temporarily switch modes.
|
|
//
|
|
|
|
CSetReadAhead readAhead( _storage );
|
|
|
|
CPersComp compr( _xPhysIndex.GetReference(), _widMax );
|
|
|
|
ciDebugOut (( DEB_ITRACE, "widMax passed to compressor: %ld\n",
|
|
_widMax ));
|
|
|
|
const CKeyBuf * pKey;
|
|
ULONG page = ULONG(-1);
|
|
BitOffset bitOff;
|
|
|
|
#if CIDBG == 1
|
|
keyLast.SetPid(pidContents); // arbitrary but not pidAll
|
|
#endif
|
|
|
|
#if CIDBG == 1
|
|
WCHAR FirstLetter = '@';
|
|
#endif
|
|
|
|
mergeProgress.Update( 0 );
|
|
|
|
ciDebugOut (( DEB_ITRACE,"Merging. Merge on letter: "));
|
|
|
|
for (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.
|
|
//
|
|
|
|
// This would later lead to a divide by 0
|
|
|
|
Win4Assert( 0 != pCurSrc->WorkIdCount() );
|
|
|
|
compr.PutKey(pKey, pCurSrc->WorkIdCount(), bitOff);
|
|
|
|
for ( ;
|
|
wid != widInvalid;
|
|
wid = pCurSrc->NextWorkId())
|
|
{
|
|
// fresh test
|
|
|
|
CFreshTest::IndexSource indexSrc =
|
|
pFreshTest->IsCorrectIndex (pCurSrc->IndexId(), wid);
|
|
//
|
|
// There should always be an entry for a workid in the fresh
|
|
// test whose data is contained in a wordlist/shadow-index.
|
|
//
|
|
Win4Assert( CFreshTest::Master != indexSrc );
|
|
|
|
if ( CFreshTest::Invalid != indexSrc )
|
|
{
|
|
compr.PutWorkId(wid, pCurSrc->MaxOccurrence(), pCurSrc->OccurrenceCount());
|
|
|
|
for (OCCURRENCE occ = pCurSrc->Occurrence();
|
|
occ != OCC_INVALID;
|
|
occ = pCurSrc->NextOccurrence())
|
|
{
|
|
compr.PutOccurrence(occ);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this key didn't have any wids then we can delete it from
|
|
// the new index. Otherwise, track it as a possible splitkey
|
|
// and update the index as necessary.
|
|
//
|
|
|
|
if ( bitOff.Page() != page )
|
|
{
|
|
page = bitOff.Page();
|
|
_xDir->Add ( bitOff, *pKey );
|
|
}
|
|
else
|
|
{
|
|
Win4Assert( page == _xDir->GetBitOffsetLastAdded().Page() );
|
|
}
|
|
|
|
keyLast = *pKey;
|
|
|
|
//
|
|
// There's no point in special abort code. We have to handle
|
|
// exceptions anyway.
|
|
//
|
|
|
|
if ( _fAbortMerge || partn.IsCleaningUp() )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "Aborting Merge\n" ));
|
|
THROW( CException( STATUS_UNSUCCESSFUL ) );
|
|
}
|
|
|
|
mergeProgress.Update( _xPhysIndex->PagesInUse() * 100 / _xPhysIndex->PageSize() );
|
|
}
|
|
|
|
ciDebugOut(( DEB_ITRACE | DEB_PENDING, "%d keys in index\n", cKey ));
|
|
|
|
// add sentinel key
|
|
keyLast.FillMax();
|
|
keyLast.SetPid( pidContents );
|
|
compr.PutKey( &keyLast, 1, bitOff );
|
|
|
|
//
|
|
// If the MaxKey is the first key on a page, it must be added
|
|
// to the directory.
|
|
//
|
|
if ( bitOff.Page() != page )
|
|
{
|
|
page = bitOff.Page();
|
|
_xDir->Add ( bitOff, keyLast );
|
|
}
|
|
else
|
|
{
|
|
Win4Assert( page == _xDir->GetBitOffsetLastAdded().Page() );
|
|
}
|
|
|
|
mergeProgress.Update( 100 );
|
|
|
|
compr.FreeStream();
|
|
} // compr goes out of scope
|
|
else
|
|
{
|
|
ciDebugOut (( DEB_ITRACE, "No merge cursor created\n" ));
|
|
|
|
CPersComp compr( _xPhysIndex.GetReference(), _widMax );
|
|
keyLast.FillMax();
|
|
keyLast.SetPid(pidContents);
|
|
|
|
BitOffset bitOff;
|
|
compr.PutKey( &keyLast, 1, bitOff );
|
|
compr.FreeStream();
|
|
}
|
|
|
|
//
|
|
// Compressor MUST NOT be in scope here.
|
|
//
|
|
|
|
keyLast.FillMax();
|
|
_xDir->LokFlushDir( keyLast );
|
|
_xDir->LokBuildDir( keyLast );
|
|
|
|
// after compr is dead
|
|
_xPhysIndex->Flush();
|
|
_xPhysIndex->Reopen( FALSE );
|
|
} //Merge
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPersIndex::QueryCursor, public
|
|
//
|
|
// Synopsis: Return a cursor for the whole persistent index.
|
|
//
|
|
// Returns: A new cursor.
|
|
//
|
|
// History: 24-Apr-91 KyleP Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CKeyCursor * CPersIndex::QueryCursor()
|
|
{
|
|
CKey key;
|
|
key.FillMin();
|
|
|
|
return QueryKeyCursor( &key );
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPersIndex::QueryKeyCursor, public
|
|
//
|
|
// Synopsis: Return a key cursor for the shadow index when restarting a
|
|
// master merge
|
|
//
|
|
// Returns: A new cursor.
|
|
//
|
|
// History: 12-Apr-94 DwightKr Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
CKeyCursor * CPersIndex::QueryKeyCursor(const CKey * pKey)
|
|
{
|
|
BitOffset posKey;
|
|
|
|
CKeyBuf keyInit;
|
|
|
|
_xDir->Seek( *pKey, &keyInit, posKey );
|
|
|
|
ciDebugOut(( DEB_ITRACE, "found key %.*ws at %lx:%lx\n",
|
|
keyInit.StrLen(), keyInit.GetStr(),
|
|
posKey.Page(), posKey.Offset() ));
|
|
|
|
XPtr<CPersDeComp> xCursor( new CPersDeComp( _xDir.GetReference(),
|
|
GetId(),
|
|
_xPhysIndex.GetReference(),
|
|
posKey,
|
|
keyInit,
|
|
pKey,
|
|
_widMax ) );
|
|
|
|
if ( 0 == xCursor->GetKey() )
|
|
xCursor.Free();
|
|
|
|
return xCursor.Acquire();
|
|
} //QueryKeyCursor
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPersIndex::QueryCursor, public
|
|
//
|
|
// Synopsis: Return a cursor for the persistent index.
|
|
//
|
|
// Arguments: [pKey] -- Key to initially seek for.
|
|
// [isRange] -- TRUE for range query
|
|
// [cMaxNodes] -- Max number of nodes to create. Decremented
|
|
// on return.
|
|
//
|
|
// Returns: A new cursor.
|
|
//
|
|
// History: 24-Apr-91 KyleP Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
COccCursor * CPersIndex::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 CPersIndex::QueryCursor.\n" ));
|
|
THROW( CException( STATUS_TOO_MANY_NODES ) );
|
|
}
|
|
|
|
BitOffset posKey;
|
|
|
|
CKeyBuf keyInit;
|
|
|
|
_xDir->Seek( *pKey, &keyInit, posKey );
|
|
|
|
ciDebugOut(( DEB_ITRACE, "found key %.*ws at %lx:%lx\n",
|
|
keyInit.StrLen(), keyInit.GetStr(),
|
|
posKey.Page(), posKey.Offset() ));
|
|
|
|
XPtr<CPersDeComp> xCursor( new CPersDeComp( _xDir.GetReference(),
|
|
GetId(),
|
|
_xPhysIndex.GetReference(),
|
|
posKey,
|
|
keyInit,
|
|
pKey,
|
|
_widMax ) );
|
|
|
|
if ( xCursor->GetKey() == 0 || !pKey->MatchPid( *xCursor->GetKey())
|
|
|| pKey->CompareStr(*xCursor->GetKey()) != 0 )
|
|
{
|
|
xCursor.Free();
|
|
}
|
|
|
|
return xCursor.Acquire();
|
|
} //QueryCursor
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPersIndex::QueryRangeCursor, public
|
|
//
|
|
// Synopsis: Return a range cursor for the persistent index.
|
|
//
|
|
// Arguments: [pKey] -- Key at beginning of the range.
|
|
// [pKeyEnd] -- Key at the end of the range.
|
|
// [cMaxNodes] -- Max number of nodes to create. Decremented
|
|
// on return.
|
|
//
|
|
// Returns: A new cursor.
|
|
//
|
|
// History: 11-Dec-91 AmyA Created.
|
|
// 31-Jan-92 AmyA Moved code to CreateRange().
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
COccCursor * CPersIndex::QueryRangeCursor( const CKey * pKey,
|
|
const CKey * pKeyEnd,
|
|
ULONG & cMaxNodes )
|
|
{
|
|
Win4Assert( cMaxNodes > 0 );
|
|
|
|
COccCurStack curStk;
|
|
|
|
CreateRange(curStk, pKey, pKeyEnd, cMaxNodes );
|
|
|
|
return curStk.QuerySynCursor( MaxWorkId() );
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPersIndex::QuerySynCursor, public
|
|
//
|
|
// Synopsis: Return a synonym cursor for the persistent index.
|
|
//
|
|
// Arguments: [keyStk] -- Stack of keys to be searched for.
|
|
// [isRange] -- Whether or not this is a range search.
|
|
// [cMaxNodes] -- Max number of nodes to create. Decremented
|
|
// on return.
|
|
//
|
|
// Returns: A new cursor.
|
|
//
|
|
// History: 31-Jan-92 AmyA Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
COccCursor * CPersIndex::QuerySynCursor( CKeyArray & keyArr,
|
|
BOOL isRange,
|
|
ULONG & cMaxNodes )
|
|
{
|
|
COccCurStack curStk;
|
|
|
|
int keyCount = keyArr.Count();
|
|
|
|
for (int i = 0; i < keyCount; i++)
|
|
{
|
|
Win4Assert( cMaxNodes > 0 );
|
|
|
|
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
|
|
{
|
|
XPtr<COccCursor> xCursor( QueryCursor( &key, FALSE, cMaxNodes ) );
|
|
|
|
if ( !xCursor.IsNull() )
|
|
{
|
|
curStk.Push( xCursor.GetPointer() );
|
|
xCursor.Acquire();
|
|
}
|
|
}
|
|
}
|
|
|
|
return(curStk.QuerySynCursor( MaxWorkId()));
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPersIndex::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 number of nodes to create. Decremented
|
|
// on return.
|
|
//
|
|
// History: 31-Jan-92 AmyA Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CPersIndex::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 ) );
|
|
}
|
|
|
|
BitOffset posKey;
|
|
CKeyBuf keyInit;
|
|
|
|
_xDir->Seek( *pKeyStart, &keyInit, posKey );
|
|
|
|
ciDebugOut (( DEB_ITRACE, "CreateRange %.*ws-%.*ws. Dir seek %.*ws, pid %d\n",
|
|
pKeyStart->StrLen(), pKeyStart->GetStr(),
|
|
pKeyEnd->StrLen(), pKeyEnd->GetStr(),
|
|
keyInit.StrLen(), keyInit.GetStr(),
|
|
keyInit.Pid() ));
|
|
|
|
CPersDeComp* pCursor = new CPersDeComp( _xDir.GetReference(),
|
|
GetId(),
|
|
_xPhysIndex.GetReference(),
|
|
posKey,
|
|
keyInit,
|
|
pKeyStart,
|
|
_widMax);
|
|
|
|
XPtr<CPersDeComp> 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...
|
|
|
|
pCursor = new CPersDeComp(*pCursor);
|
|
|
|
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();
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPersIndex::Remove, public
|
|
//
|
|
// Synopsis: Remove index from storage
|
|
//
|
|
// History: 02-May-91 BartoszM Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CPersIndex::Remove()
|
|
{
|
|
_xPhysIndex->Close();
|
|
_xDir->Close();
|
|
_obj->Close();
|
|
|
|
if ( !_storage.RemoveObject( ObjectId() ) )
|
|
{
|
|
DWORD dwError = GetLastError();
|
|
ciDebugOut(( DEB_ERROR, "Delete of index %08x failed: %d\n",
|
|
ObjectId(), dwError ));
|
|
}
|
|
}
|
|
|
|
#ifdef KEYLIST_ENABLED
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// 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 * CPersIndex::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 * CPersIndex::ComputeRelevantWords(ULONG cRows,ULONG cRW,
|
|
WORKID *pwid,CKeyList *pKeyList)
|
|
{
|
|
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();
|
|
} //ComputeRelevantWords
|
|
|
|
#endif // KEYLIST_ENABLED
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPersIndex::MakeBackupCopy
|
|
//
|
|
// Synopsis: Makes a copy of the index and directory using the storage
|
|
// provided.
|
|
//
|
|
// Arguments: [storage] - Storage
|
|
//
|
|
// History: 3-17-97 srikants Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CPersIndex::MakeBackupCopy( PStorage & storage,
|
|
WORKID wid,
|
|
PSaveProgressTracker & tracker )
|
|
{
|
|
//
|
|
// Create an index in the destination storage.
|
|
//
|
|
CPersIndex * pDstIndex = new CPersIndex( storage,
|
|
wid,
|
|
GetId(),
|
|
_xPhysIndex->PageSize(),
|
|
IsMaster() ? eMaster : eShadow );
|
|
|
|
XPtr<CPersIndex> xDstIndex( pDstIndex );
|
|
|
|
//
|
|
// Make a backup copy of the stream.
|
|
//
|
|
_xPhysIndex->MakeBackupCopy( pDstIndex->_xPhysIndex.GetReference(),
|
|
tracker );
|
|
|
|
// Make a backup copy of the directory.
|
|
//
|
|
_xDir->MakeBackupCopy( storage, tracker );
|
|
}
|
|
|
|
|
|
#if CIDBG == 1
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CDiskIndex::VerifyContents, public
|
|
//
|
|
// Synopsis: Walks through an index and thus verifies each key
|
|
//
|
|
// History: 28-Oct-94 DwightKr Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void CDiskIndex::VerifyContents()
|
|
{
|
|
|
|
//
|
|
// Turn this on when we think we are missing keys.
|
|
//
|
|
|
|
#if 0
|
|
|
|
CKeyCursor *pCursor = QueryCursor();
|
|
|
|
if ( pCursor )
|
|
{
|
|
TRY
|
|
{
|
|
ciDebugOut((DEB_ITRACE, "Verifying contents of new index\n"));
|
|
WCHAR FirstLetter = '@';
|
|
|
|
for ( const CKeyBuf * pKey = pCursor->GetKey();
|
|
pKey != NULL; pKey = pCursor->GetNextKey())
|
|
{
|
|
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 ));
|
|
}
|
|
}
|
|
|
|
ciDebugOut(( DEB_NOCOMPNAME | DEB_ITRACE | DEB_PENDING, "\n" ));
|
|
}
|
|
CATCH (CException, e)
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "Error 0x%x while verifying contents of new index\n", e.GetErrorCode() ));
|
|
}
|
|
END_CATCH
|
|
|
|
delete pCursor;
|
|
}
|
|
|
|
#endif // 0
|
|
|
|
}
|
|
#endif
|