Files
mars-flaim/flaim/src/flchkix.cpp
dsandersoremutah c55dab446f Renamed version4 to flaim and version5 to xflaim
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@7 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2006-01-27 21:06:39 +00:00

2062 lines
45 KiB
C++

//-------------------------------------------------------------------------
// Desc: Check database indexes for logical corruptions.
// Tabs: 3
//
// Copyright (c) 1992,1994-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: flchkix.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $
//-------------------------------------------------------------------------
#include "flaimsys.h"
FSTATIC RCODE chkVerifyTrackerCounts(
STATE_INFO * pStateInfo,
IX_CHK_INFO * pIxChkInfo,
FLMUINT uiIndexNum,
FLMUINT uiKeyCount,
FLMUINT uiRefCount);
FSTATIC RCODE chkIsCountIndex(
STATE_INFO * pStateInfo,
FLMUINT uiIndexNum,
FLMBOOL * pbIsCountIndex);
FSTATIC RCODE chkResolveRSetMissingKey(
STATE_INFO * pStateInfo,
IX_CHK_INFO * pIxChkInfo,
FLMUINT uiIxRefDrn);
FSTATIC RCODE chkVerifyDelNonUniqueRec(
STATE_INFO * pStateInfo,
IX_CHK_INFO * pIxChkInfo,
FLMUINT uiIndex,
FLMBYTE * pucKey,
FLMUINT uiKeyLen,
FLMUINT uiRecDrn,
FLMUINT * puiRecContainerRV,
FLMBOOL * pbDelRecRV);
FSTATIC RCODE chkVerifyKeyExists(
FDB * pDb,
LFILE * pLFile,
FLMBYTE * pucKey,
FLMUINT uiKeyLen,
FLMUINT uiRefDrn,
FLMBOOL * pbFoundRV);
FSTATIC RCODE chkAddDelKeyRef(
STATE_INFO * pStateInfo,
IX_CHK_INFO * pIxChkInfo,
FLMUINT uiIndexNum,
FLMBYTE * pucKey,
FLMUINT uiKeyLen,
FLMUINT uiDrn,
FLMUINT uiFlags);
FSTATIC RCODE chkGetKeySource(
STATE_INFO * pStateInfo,
IX_CHK_INFO * pIxChkInfo,
FLMUINT uiIndex,
FLMBYTE * pucKey,
FLMUINT uiKeyLen,
FLMUINT uiDrn,
FLMUINT * puiRecordContainerRV,
FLMBOOL * pbKeyInRecRV,
FLMBOOL * pbKeyInIndexRV);
FSTATIC RCODE chkReportIxError(
STATE_INFO * pStateInfo,
IX_CHK_INFO * pIxChkInfo,
eCorruptionType eCorruption,
FLMUINT uiErrIx,
FLMUINT uiErrDrn,
FLMBYTE * pucErrKey,
FLMUINT uiErrKeyLen,
FLMBOOL * pbFixErrRV);
RCODE chkGetNextRSKey(
IX_CHK_INFO * pIxChkInfo);
FSTATIC RCODE chkVerifyKeyNotUnique(
STATE_INFO * pStateInfo,
FLMUINT uiIndex,
FLMBYTE * pucKey,
FLMUINT uiKeyLen,
FLMUINT * puiRefCountRV);
FSTATIC RCODE chkStartUpdate(
STATE_INFO * pStateInfo,
IX_CHK_INFO * pIxChkInfo);
FSTATIC RCODE chkEndUpdate(
STATE_INFO * pStateInfo,
IX_CHK_INFO * pIxChkInfo);
FLMINT chkRSCallbackFunc(
RSET_CB_INFO * pCBInfo);
RCODE chkCompareIxRSEntries(
void * vpData1,
FLMUINT uiLength1,
void * vpData2,
FLMUINT uiLength2,
void * UserValue,
FLMINT * piCompare);
/****************************************************************************
Desc:
****************************************************************************/
FINLINE RCODE chkRSGetNext(
void * pRSet,
FLMBYTE * pBuffer,
FLMUINT uiBufferLength,
FLMUINT * puiReturnLength)
{
FLMUINT uiReturnLen;
RCODE rc;
rc = ((FResultSet *)pRSet)->GetNext( (void *)pBuffer,
uiBufferLength, &uiReturnLen);
*puiReturnLength = (rc == FERR_OK) ? uiReturnLen : 0;
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
FINLINE RCODE chkKeyToTree(
IXD * pIxd,
FLMBYTE * pucKey,
FLMUINT uiKeyLen,
FlmRecord ** ppKeyRV)
{
return flmIxKeyOutput( pIxd, pucKey, uiKeyLen, ppKeyRV, FALSE);
}
/********************************************************************
Desc: Verifies the key and reference counts against the counts that
are stored in the tracker record.
*********************************************************************/
FSTATIC RCODE chkVerifyTrackerCounts(
STATE_INFO * pStateInfo,
IX_CHK_INFO * pIxChkInfo,
FLMUINT uiIndexNum,
FLMUINT uiKeyCount,
FLMUINT uiRefCount
)
{
RCODE rc = FERR_OK;
FDB * pDb = pStateInfo->pDb;
FlmRecord * pRecord = NULL;
eCorruptionType eCorruption;
FLMUINT uiTrackerKeyCount = 0;
FLMUINT uiTrackerRefCount = 0;
void * pvField;
// Retrieve the tracker record from record cache.
if (RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL, FLM_TRACKER_CONTAINER,
uiIndexNum, TRUE, NULL, NULL, &pRecord)))
{
if (rc != FERR_NOT_FOUND)
{
goto Exit;
}
rc = FERR_OK;
}
else
{
if ((pvField = pRecord->find( pRecord->root(), FLM_KEY_TAG)) != NULL)
{
if (RC_BAD( rc = pRecord->getUINT( pvField, &uiTrackerKeyCount)))
{
goto Exit;
}
}
if ((pvField = pRecord->find( pRecord->root(), FLM_REFS_TAG)) != NULL)
{
if (RC_BAD( rc = pRecord->getUINT( pvField, &uiTrackerRefCount)))
{
goto Exit;
}
}
}
// See if the counts match what we got from the tracker record.
if (uiKeyCount != uiTrackerKeyCount || uiRefCount != uiTrackerRefCount)
{
// Log an error.
eCorruption = (eCorruptionType)((uiKeyCount != uiTrackerKeyCount)
? FLM_KEY_COUNT_MISMATCH
: FLM_REF_COUNT_MISMATCH);
if (RC_BAD( rc = chkReportError( pIxChkInfo->pDbInfo, eCorruption,
LOCALE_INDEX, uiIndexNum, LF_INDEX,
0xFF, 0, 0, 0, 0, 0xFFFF, 0, NULL)))
{
goto Exit;
}
}
Exit:
if (pRecord)
{
pRecord->Release();
}
return( rc);
}
/********************************************************************
Desc: Determine if an index is an index that keeps key and reference
counts.
*********************************************************************/
FSTATIC RCODE chkIsCountIndex(
STATE_INFO * pStateInfo,
FLMUINT uiIndexNum,
FLMBOOL * pbIsCountIndex
)
{
RCODE rc = FERR_OK;
FDB * pDb = pStateInfo->pDb;
IXD * pIxd;
if( RC_BAD( rc = fdictGetIndex( pDb->pDict,
pDb->pFile->bInLimitedMode, uiIndexNum,
NULL, &pIxd, TRUE)))
{
goto Exit;
}
*pbIsCountIndex = (FLMBOOL)((pIxd->uiFlags & IXD_COUNT)
? (FLMBOOL)TRUE
: (FLMBOOL)FALSE);
Exit:
return( rc);
}
/********************************************************************
Desc: Verifies the current index key against the result set.
*********************************************************************/
RCODE chkVerifyIXRSet(
STATE_INFO * pStateInfo,
IX_CHK_INFO * pIxChkInfo,
FLMUINT uiIxRefDrn
)
{
FLMINT iCmpVal = 0;
FLMUINT uiIteration = 0;
FLMBOOL bRSetEmpty = FALSE;
RCODE rc = FERR_OK;
RS_IX_KEY * pCurrRSKey;
RS_IX_KEY * pPrevRSKey;
if (!pIxChkInfo->pCurrRSKey)
{
pIxChkInfo->pCurrRSKey = &pIxChkInfo->IxKey1;
pIxChkInfo->pPrevRSKey = &pIxChkInfo->IxKey2;
}
/* Compare index and result set keys */
while( !bRSetEmpty)
{
if( pIxChkInfo->bGetNextRSKey)
{
/*
Get the next key from the result set. If the result set
is empty, then pIxChkInfo->uiRSKeyLen will be set to
zero, forcing the problem to be resolved below.
*/
if( RC_BAD( rc = chkGetNextRSKey( pIxChkInfo)))
{
if( rc == FERR_EOF_HIT || rc == FERR_NOT_FOUND)
{
/*
Set bRSetEmpty to TRUE so that the loop will exit after the
current key is resolved. Otherwise, conflict resolution on
the current key will be repeated forever (infinite loop).
*/
bRSetEmpty = TRUE;
rc = FERR_OK;
}
else
{
goto Exit;
}
}
else
{
/* Updated statistics */
pIxChkInfo->pDbInfo->pProgress->ui64NumKeysExamined++;
}
}
pCurrRSKey = pIxChkInfo->pCurrRSKey;
pPrevRSKey = pIxChkInfo->pPrevRSKey;
if( pCurrRSKey->uiRSKeyLen == 0 || bRSetEmpty)
{
/*
We don't have a key because we got an EOF when
reading the result set. Need to resolve the
fact that the result set does not have a key
that is found in the index. Set iCmpVal to
1 to force this resolution.
*/
iCmpVal = 1;
}
else
{
/*
Compare the index key and result set key.
*/
iCmpVal = chkCompareKeySet(
pCurrRSKey->uiRSIxNum,
&(pCurrRSKey->pucRSKeyBuf[ RS_KEY_OFFSET]),
pCurrRSKey->uiRSKeyLen - RS_KEY_OVERHEAD,
pCurrRSKey->uiRSRefDrn,
pStateInfo->pLogicalFile->pLFile->uiLfNum,
pStateInfo->pCurKey,
pStateInfo->uiCurKeyLen,
uiIxRefDrn);
}
if( iCmpVal < 0)
{
// If a comparison is done where the keys from the result set
// don't match what we got from the index, we will forego verifying
// the tracker counts. Verifying of tracker counts can only
// occur if we have an otherwise clean check of the index keys.
pIxChkInfo->bCheckCounts = FALSE;
/*
The result set key is less than the index key. This could mean
that the result set key needs to be added to the index.
*/
if(( RC_BAD( rc = chkResolveIXMissingKey( pStateInfo,
pIxChkInfo))) ||
(pIxChkInfo->pDbInfo->bReposition))
{
/*
If the key was added to the index (bReposition == TRUE)
or we got some other error, we don't want to get the next
result set key.
*/
pIxChkInfo->bGetNextRSKey = FALSE;
goto Exit;
}
else
{
/*
False alarm. The index is missing the key because of
a concurrent update. We want to get the next RS key.
*/
pIxChkInfo->bGetNextRSKey = TRUE;
}
}
else if( iCmpVal > 0)
{
// If a comparison is done where the keys from the result set
// don't match what we got from the index, we will forego verifying
// the tracker counts. Verifying of tracker counts can only
// occur if we have an otherwise clean check of the index keys.
pIxChkInfo->bCheckCounts = FALSE;
/*
The result set key is greater than the index key. This could mean
that the index key needs to be deleted from the index. Whether we
delete the index key or not, we don't need to get the next result
set key, but we do want to reposition and get the next index key.
*/
pIxChkInfo->bGetNextRSKey = FALSE;
if(( RC_BAD( rc = chkResolveRSetMissingKey( pStateInfo,
pIxChkInfo, uiIxRefDrn))) ||
(pIxChkInfo->pDbInfo->bReposition))
{
goto Exit;
}
break;
}
else
{
/*
The index and result set keys are equal. We want to get
the next result set and index keys.
*/
pIxChkInfo->bGetNextRSKey = TRUE;
// Determine if we have switched indexes. If so, verify the key
// and reference counts against the counts in the tracker record.
if (pCurrRSKey->uiRSIxNum != pPrevRSKey->uiRSIxNum)
{
if (pIxChkInfo->bCheckCounts)
{
// Verify the key and reference counts against tracker record.
if (RC_BAD( rc = chkVerifyTrackerCounts( pStateInfo,
pIxChkInfo,
pPrevRSKey->uiRSIxNum,
pIxChkInfo->uiRSIxKeyCount,
pIxChkInfo->uiRSIxRefCount)))
{
goto Exit;
}
}
// Determine if the new index is one that supports counts.
if (RC_BAD( rc = chkIsCountIndex( pStateInfo,
pCurrRSKey->uiRSIxNum,
&pIxChkInfo->bCheckCounts)))
{
goto Exit;
}
if (pIxChkInfo->bCheckCounts)
{
// Set the counts to one.
pIxChkInfo->uiRSIxKeyCount = 1;
pIxChkInfo->uiRSIxRefCount = 1;
}
}
else
{
if (pIxChkInfo->bCheckCounts)
{
// Always increment the reference count.
pIxChkInfo->uiRSIxRefCount++;
// See if the key changed.
if (pCurrRSKey->uiRSKeyLen !=
pPrevRSKey->uiRSKeyLen ||
(pCurrRSKey->uiRSKeyLen > RS_KEY_OFFSET &&
f_memcmp( &pCurrRSKey->pucRSKeyBuf [RS_KEY_OFFSET],
&pPrevRSKey->pucRSKeyBuf [RS_KEY_OFFSET],
pCurrRSKey->uiRSKeyLen - RS_KEY_OFFSET) != 0))
{
pIxChkInfo->uiRSIxKeyCount++;
}
else
{
// If the keys are the same, at least the DRNs better
// be different.
flmAssert( pCurrRSKey->uiRSRefDrn !=
pPrevRSKey->uiRSRefDrn);
}
}
}
break;
}
/* Call the yield function periodically */
uiIteration++;
if( !(uiIteration & 0x1F) )
{
f_yieldCPU();
}
}
Exit:
return( rc);
}
/********************************************************************
Desc: Retrieves the next key from the sorted result set
*********************************************************************/
RCODE chkGetNextRSKey(
IX_CHK_INFO * pIxChkInfo
)
{
RCODE rc = FERR_OK;
RS_IX_KEY * pCurrRSKey;
// Swap current and last key pointers - this allows us to always keep
// the last key without having to memcpy the keys.
pCurrRSKey = pIxChkInfo->pCurrRSKey;
pIxChkInfo->pCurrRSKey = pIxChkInfo->pPrevRSKey;
pIxChkInfo->pPrevRSKey = pCurrRSKey;
pCurrRSKey = pIxChkInfo->pCurrRSKey;
/* Get the next key */
if( RC_BAD( rc = chkRSGetNext( pIxChkInfo->pRSet,
pCurrRSKey->pucRSKeyBuf,
MAX_KEY_SIZ + RS_KEY_OVERHEAD,
&(pCurrRSKey->uiRSKeyLen))))
{
goto Exit;
}
/* Verify that the key meets the minimum length requirements */
flmAssert( pCurrRSKey->uiRSKeyLen >= RS_KEY_OVERHEAD);
/* Extract the index number and reference DRN */
pCurrRSKey->uiRSIxNum =
(FLMUINT)FB2UW( &(pCurrRSKey->pucRSKeyBuf[ RS_IX_OFFSET]));
pCurrRSKey->uiRSRefDrn =
(FLMUINT)FB2UD( &(pCurrRSKey->pucRSKeyBuf[ RS_REF_OFFSET]));
Exit:
return( rc);
}
/********************************************************************
Desc: Resolves the case of a key found in the result set but not in
the current index.
*********************************************************************/
RCODE
chkResolveIXMissingKey(
STATE_INFO * pStateInfo,
IX_CHK_INFO * pIxChkInfo
)
{
FLMBOOL bKeyInRec;
FLMBOOL bKeyInIndex;
RCODE rc = FERR_OK;
FLMBOOL bFixCorruption = FALSE;
RS_IX_KEY * pCurrRSKey = pIxChkInfo->pCurrRSKey;
/*
Determine if the record generates the key and if the
key is found in the index.
*/
if( RC_BAD( rc = chkGetKeySource( pStateInfo, pIxChkInfo,
pCurrRSKey->uiRSIxNum, &(pCurrRSKey->pucRSKeyBuf[ RS_KEY_OFFSET]),
(FLMUINT)(pCurrRSKey->uiRSKeyLen - RS_KEY_OVERHEAD),
pCurrRSKey->uiRSRefDrn, NULL,
&bKeyInRec, &bKeyInIndex)))
{
if( rc == FERR_INDEX_OFFLINE)
{
rc = FERR_OK;
}
goto Exit;
}
/*
If the record does not generate the key or the key+ref is in the index,
the index is not corrupt.
*/
if( !bKeyInRec || bKeyInIndex)
{
pIxChkInfo->pDbInfo->pProgress->ui64NumConflicts++;
goto Exit;
}
/*
Otherwise, the index is corrupt.
*/
/* Update statistics */
pIxChkInfo->pDbInfo->pProgress->ui64NumRecKeysNotFound++;
pIxChkInfo->pDbInfo->pProgress->uiLogicalIndexCorruptions++;
/* Report the error */
if( RC_BAD( rc = chkReportIxError( pStateInfo, pIxChkInfo,
FLM_DRN_NOT_IN_KEY_REFSET, pCurrRSKey->uiRSIxNum,
pCurrRSKey->uiRSRefDrn,
&(pCurrRSKey->pucRSKeyBuf[ RS_KEY_OFFSET]),
(FLMUINT)(pCurrRSKey->uiRSKeyLen - RS_KEY_OVERHEAD),
&bFixCorruption)))
{
goto Exit;
}
/*
Exit if the application does not want to repair the corruption.
*/
if( !bFixCorruption)
{
/* Set the logical corruption flag */
pIxChkInfo->pDbInfo->pProgress->bLogicalIndexCorrupt = TRUE;
goto Exit;
}
/* Fix the corruption */
/* Update statistics */
pIxChkInfo->pDbInfo->pProgress->uiLogicalIndexRepairs++;
/* Add the key. */
if( RC_OK( rc = chkAddDelKeyRef(
pStateInfo, pIxChkInfo, pCurrRSKey->uiRSIxNum,
&(pCurrRSKey->pucRSKeyBuf[ RS_KEY_OFFSET]),
(FLMUINT)(pCurrRSKey->uiRSKeyLen - RS_KEY_OVERHEAD),
pCurrRSKey->uiRSRefDrn, 0)))
{
pIxChkInfo->pDbInfo->bReposition = TRUE;
goto Exit;
}
else
{
if( rc == FERR_NOT_UNIQUE)
{
/*
A subsequent record probably also generates this key,
but the index is a unique index so we were not allowed
to add the missing key + ref to the index. This record
should probably be deleted.
*/
if( RC_OK( rc = chkResolveNonUniqueKey( pStateInfo,
pIxChkInfo, pCurrRSKey->uiRSIxNum,
&(pCurrRSKey->pucRSKeyBuf[ RS_KEY_OFFSET]),
(FLMUINT)(pCurrRSKey->uiRSKeyLen - RS_KEY_OVERHEAD),
pCurrRSKey->uiRSRefDrn)))
{
pIxChkInfo->pDbInfo->bReposition = TRUE;
goto Exit;
}
}
else
{
/* Set the logical corruption flag */
pIxChkInfo->pDbInfo->pProgress->bLogicalIndexCorrupt = TRUE;
}
}
Exit:
return( rc);
}
/********************************************************************
Desc: Resolves the case of a key found in the current index but not
in the result set.
*********************************************************************/
FSTATIC RCODE
chkResolveRSetMissingKey(
STATE_INFO * pStateInfo,
IX_CHK_INFO * pIxChkInfo,
FLMUINT uiIxRefDrn
)
{
FLMBOOL bKeyInRec;
FLMBOOL bKeyInIndex;
RCODE rc = FERR_OK;
FLMBOOL bFixCorruption = FALSE;
/*
See if the key is found in the index and/or generated
by the record.
*/
if( RC_BAD( rc = chkGetKeySource( pStateInfo, pIxChkInfo,
pStateInfo->pLogicalFile->pLFile->uiLfNum,
pStateInfo->pCurKey, pStateInfo->uiCurKeyLen,
uiIxRefDrn, NULL, &bKeyInRec, &bKeyInIndex)))
{
if( rc == FERR_INDEX_OFFLINE)
{
rc = FERR_OK;
}
goto Exit;
}
/*
If the key is generated by the record or the key is not found
in the index, the index is not corrupt.
*/
if( bKeyInRec || !bKeyInIndex)
{
pIxChkInfo->pDbInfo->pProgress->ui64NumConflicts++;
goto Exit;
}
/*
Otherwise, the index is corrupt.
*/
/* Update statistics */
pIxChkInfo->pDbInfo->pProgress->ui64NumKeysNotFound++;
pIxChkInfo->pDbInfo->pProgress->uiLogicalIndexCorruptions++;
/* Report the error */
if( RC_BAD( rc = chkReportIxError( pStateInfo, pIxChkInfo,
FLM_IX_KEY_NOT_FOUND_IN_REC,
pStateInfo->pLogicalFile->pLFile->uiLfNum, uiIxRefDrn,
pStateInfo->pCurKey, pStateInfo->uiCurKeyLen,
&bFixCorruption)))
{
goto Exit;
}
/*
Exit if the application does not want to repair the corruption.
*/
if( !bFixCorruption)
{
/* Set the logical corruption flag */
pIxChkInfo->pDbInfo->pProgress->bLogicalIndexCorrupt = TRUE;
goto Exit;
}
/* Fix the corruption */
/* Update statistics */
pIxChkInfo->pDbInfo->pProgress->uiLogicalIndexRepairs++;
/* Delete the reference from the index */
if( RC_OK( rc = chkAddDelKeyRef(
pStateInfo, pIxChkInfo,
pStateInfo->pLogicalFile->pLFile->uiLfNum,
pStateInfo->pCurKey, pStateInfo->uiCurKeyLen,
uiIxRefDrn, KREF_DELETE_FLAG)))
{
pIxChkInfo->pDbInfo->bReposition = TRUE;
}
else
{
/* Set the logical corruption flag */
pIxChkInfo->pDbInfo->pProgress->bLogicalIndexCorrupt = TRUE;
}
Exit:
return( rc);
}
/********************************************************************
Desc: Resolves the case of multiple references associated with a
key in a unique index.
*********************************************************************/
RCODE
chkResolveNonUniqueKey(
STATE_INFO * pStateInfo,
IX_CHK_INFO * pIxChkInfo,
FLMUINT uiIndex,
FLMBYTE * pucKey,
FLMUINT uiKeyLen,
FLMUINT uiDrn
)
{
FDB * pDb = pStateInfo->pDb;
LFILE * pRecLFile = NULL;
FLMBOOL bDeleteRec = FALSE;
FLMUINT uiRecContainer;
RCODE rc = FERR_OK;
RCODE rc2 = FERR_OK;
FLMBOOL bFixCorruption = FALSE;
FlmRecord * pOldRecord = NULL;
/*
Verify that the record violates the constraints of the unique
index and should be deleted.
*/
if( RC_BAD( rc = chkVerifyDelNonUniqueRec( pStateInfo, pIxChkInfo,
uiIndex, pucKey, uiKeyLen, uiDrn, &uiRecContainer, &bDeleteRec)))
{
goto Exit;
}
if( bDeleteRec)
{
/* Update statistics */
pIxChkInfo->pDbInfo->pProgress->ui64NumNonUniqueKeys++;
pIxChkInfo->pDbInfo->pProgress->uiLogicalIndexCorruptions++;
/* Report the error */
if( RC_BAD( rc = chkReportIxError( pStateInfo, pIxChkInfo,
FLM_NON_UNIQUE_ELM_KEY_REF, uiIndex, uiDrn,
pucKey, uiKeyLen, &bFixCorruption)))
{
goto Exit;
}
if( !bFixCorruption)
{
/* Set the logical corruption flag */
pIxChkInfo->pDbInfo->pProgress->bLogicalIndexCorrupt = TRUE;
goto Exit;
}
/*
Delete the record that generated the non-unique
reference.
*/
/* Update statistics */
pIxChkInfo->pDbInfo->pProgress->uiLogicalIndexRepairs++;
/*
Start an update transaction, if necessary.
*/
if( RC_BAD( rc = chkStartUpdate( pStateInfo, pIxChkInfo)))
{
goto Exit;
}
/*
Re-verify that the record should be deleted.
*/
if( RC_BAD( rc = chkVerifyDelNonUniqueRec( pStateInfo, pIxChkInfo,
uiIndex, pucKey, uiKeyLen, uiDrn, &uiRecContainer, &bDeleteRec)))
{
goto Exit;
}
if( bDeleteRec == TRUE)
{
void * pvMark;
/*
Mark the DB's temporary pool.
*/
pvMark = GedPoolMark( &(pDb->TempPool));
/*
Call the internal delete function, passing boolean flags
indicating that missing keys should not prevent the
record deletion and that the record validator callback
should not be called.
*/
if( RC_BAD( rc = fdictGetContainer( pDb->pDict,
uiRecContainer, &pRecLFile)))
{
goto Exit;
}
rc = flmDeleteRecord( pDb, pRecLFile,
uiDrn, &pOldRecord, TRUE);
if (gv_FlmSysData.EventHdrs [F_EVENT_UPDATES].pEventCBList)
{
flmUpdEventCallback( pDb,
F_EVENT_DELETE_RECORD, (HFDB)pDb, rc, uiDrn,
uiRecContainer, NULL, pOldRecord);
}
/*
Reset the DB's temporary pool. The flmDeleteRecord
call allocates space for the record that is being deleted.
*/
GedPoolReset( &(pDb->TempPool), pvMark);
if( RC_BAD( rc))
{
/*
If the record had already been deleted, continue the
check without reporting the error.
*/
if( rc == FERR_NOT_FOUND)
{
rc = FERR_OK;
/* Update statistics */
pIxChkInfo->pDbInfo->pProgress->uiNumProblemsFixed++;
}
else
{
/* Set the logical corruption flag */
pIxChkInfo->pDbInfo->pProgress->bLogicalIndexCorrupt = TRUE;
}
goto Exit;
}
/* Update statistics */
pIxChkInfo->pDbInfo->pProgress->uiNumProblemsFixed++;
}
}
else
{
/* Increment the conflict counter */
pIxChkInfo->pDbInfo->pProgress->ui64NumConflicts++;
}
Exit:
/*
End the update. chkEndUpdate will be a no-op if an update
transaction was not started.
*/
rc2 = chkEndUpdate( pStateInfo, pIxChkInfo);
if( pOldRecord)
{
pOldRecord->Release();
}
return( (RCODE)((rc != FERR_OK) ? (RCODE)rc : (RCODE)rc2));
}
/********************************************************************
Desc: Verifies that the specified record should be deleted because it
generates key(s) which violate the constraints of a unique
index.
*********************************************************************/
FSTATIC RCODE chkVerifyDelNonUniqueRec(
STATE_INFO * pStateInfo,
IX_CHK_INFO * pIxChkInfo,
FLMUINT uiIndex,
FLMBYTE * pucKey,
FLMUINT uiKeyLen,
FLMUINT uiRecDrn,
FLMUINT * puiRecContainerRV,
FLMBOOL * pbDelRecRV
)
{
FLMBOOL bKeyInRec;
FLMBOOL bRecRefdByKey;
FLMUINT uiRefCount;
FLMUINT uiRecContainer;
RCODE rc = FERR_OK;
*pbDelRecRV = FALSE;
*puiRecContainerRV = 0;
/*
See if the key is found in the index and/or generated
by the record.
*/
if( RC_BAD( rc = chkGetKeySource( pStateInfo, pIxChkInfo,
uiIndex, pucKey, uiKeyLen, uiRecDrn,
&uiRecContainer, &bKeyInRec, &bRecRefdByKey)))
{
if( rc == FERR_INDEX_OFFLINE)
{
rc = FERR_OK;
}
goto Exit;
}
*puiRecContainerRV = uiRecContainer;
if( bKeyInRec == TRUE)
{
/* Verify that the key is not unique */
if( RC_BAD( rc = chkVerifyKeyNotUnique(
pStateInfo, uiIndex, pucKey,
uiKeyLen, &uiRefCount)))
{
goto Exit;
}
if( uiRefCount > 1)
{
/*
The unique index has multiple references for the
specified key. Since the current record generates
a non-unique key, it should be deleted even if
it is not one of the records referenced by the
key. Of course, if it is already referenced by
the key, deleting the record will reduce the
number of references associated with the key
by one.
*/
*pbDelRecRV = TRUE;
}
else if( uiRefCount == 1 && bRecRefdByKey == FALSE)
{
/*
The unique index already has a key corresponding
to the key being generated by the current record.
However, the record is not referenced from the
unique index. The record should still be
deleted since it generates a non-unique key.
*/
*pbDelRecRV = TRUE;
}
}
Exit:
return( rc);
}
/********************************************************************
Desc: Determines if a key is generated by the current record
and/or if the key is found in the current index
*********************************************************************/
FSTATIC RCODE chkGetKeySource(
STATE_INFO * pStateInfo,
IX_CHK_INFO * pIxChkInfo,
FLMUINT uiIndex,
FLMBYTE * pucKey,
FLMUINT uiKeyLen,
FLMUINT uiDrn,
FLMUINT * puiRecordContainerRV,
FLMBOOL * pbKeyInRecRV,
FLMBOOL * pbKeyInIndexRV
)
{
FlmRecord * pRecord = NULL;
FDB * pDb = pStateInfo->pDb;
LFILE * pLFile;
LFILE * pIxLFile;
REC_KEY * pKeys = NULL;
REC_KEY * pTempKey = NULL;
IXD * pIxd;
FLMBYTE ucRecKeyBuf[ MAX_KEY_SIZ];
FLMUINT uiRecKeyLen;
FLMUINT uiKeyCount;
FLMBOOL bResetKRef = FALSE;
void * pIxPoolMark;
void * pDbPoolMark;
FLMUINT uiContainerNum;
RCODE rc = FERR_OK;
/*
Initialize return values.
*/
*pbKeyInRecRV = FALSE;
*pbKeyInIndexRV = FALSE;
if( puiRecordContainerRV)
{
*puiRecordContainerRV = 0;
}
/* Initialize variables */
pIxPoolMark = GedPoolMark( &(pIxChkInfo->pool));
/*
Need to mark the DB's temporary pool. The index code allocates
memory for new CDL entries from the DB pool. If the pool is not
reset, it grows during the check and becomes VERY large.
*/
pDbPoolMark = GedPoolMark( &(pDb->TempPool));
/* Set up the KRef so that flmGetRecKeys will work */
if( RC_BAD( rc = KrefCntrlCheck( pDb)))
{
goto Exit;
}
bResetKRef = TRUE;
/* Get the LFile and IXD of the index */
if( RC_BAD( rc = fdictGetIndex( pDb->pDict,
pDb->pFile->bInLimitedMode, uiIndex,
&pIxLFile, &pIxd)))
{
// Return FERR_INDEX_OFFLINE error.
goto Exit;
}
if ((uiContainerNum = pIxd->uiContainerNum) == 0)
{
// Container number is always the last two bytes of the key.
flmAssert( uiKeyLen > getIxContainerPartLen( pIxd));
uiContainerNum = getContainerFromKey( pucKey, uiKeyLen);
}
// Get the LFile of the record that caused the error
if( RC_BAD( rc = fdictGetContainer( pDb->pDict, uiContainerNum, &pLFile)))
{
goto Exit;
}
// Set the record container return value
if( puiRecordContainerRV)
{
*puiRecordContainerRV = uiContainerNum;
}
/* See if the key is in the index. */
if( RC_BAD( rc = chkVerifyKeyExists( pDb,
pIxLFile, pucKey, uiKeyLen, uiDrn, pbKeyInIndexRV)))
{
goto Exit;
}
/* Read the record */
if( RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL,
pLFile->uiLfNum, uiDrn, FALSE, NULL, NULL, &pRecord)))
{
if (rc != FERR_NOT_FOUND)
goto Exit;
// NOTE: Deliberately not bringing in to cache if not found there.
if( RC_BAD( rc = FSReadRecord( pDb, pLFile, uiDrn,
&pRecord, NULL, NULL)))
{
if( rc == FERR_NOT_FOUND)
{
*pbKeyInRecRV = FALSE;
rc = FERR_OK;
}
else
{
goto Exit;
}
}
}
if( pRecord)
{
/* Generate record keys */
if (RC_BAD( rc = flmGetRecKeys( pDb, pIxd, pRecord,
pRecord->getContainerID(),
TRUE, &(pIxChkInfo->pool), &pKeys)))
{
goto Exit;
}
uiKeyCount = 0;
pTempKey = pKeys;
while( pTempKey != NULL)
{
/* Build the collated keys for each key tree. */
if( RC_BAD( rc = KYTreeToKey( pDb, pIxd,
pTempKey->pKey, pTempKey->pKey->getContainerID(),
ucRecKeyBuf, &uiRecKeyLen, 0)))
{
goto Exit;
}
if( KYKeyCompare( pucKey, uiKeyLen,
ucRecKeyBuf, uiRecKeyLen) == BT_EQ_KEY)
{
*pbKeyInRecRV = TRUE;
break;
}
pTempKey = pTempKey->pNextKey;
uiKeyCount++;
/*
Release the CPU periodically to prevent CPU hog
problems.
*/
f_yieldCPU();
}
}
Exit:
if (pKeys)
{
pTempKey = pKeys;
while (pTempKey)
{
pTempKey->pKey->Release();
pTempKey = pTempKey->pNextKey;
}
}
if( pRecord)
{
pRecord->Release();
}
// Remove any keys added to the KRef
if (bResetKRef)
{
KYAbortCurrentRecord( pDb);
}
// Reset the DB's temporary pool
GedPoolReset( &(pDb->TempPool), pDbPoolMark);
// Reset the index check pool
GedPoolReset( &(pIxChkInfo->pool), pIxPoolMark);
return( rc);
}
/********************************************************************
Desc: Verify that a key is (or is not) found in an index.
*********************************************************************/
FSTATIC RCODE chkVerifyKeyExists(
FDB * pDb,
LFILE * pLFile,
FLMBYTE * pucKey,
FLMUINT uiKeyLen,
FLMUINT uiRefDrn,
FLMBOOL * pbFoundRV)
{
RCODE rc = FERR_OK; /* Return code */
BTSK stackBuf[ BH_MAX_LEVELS ]; /* Stack to hold b-tree variables */
BTSK * stack = stackBuf; /* Points to proper stack frame */
FLMUINT uiDinDomain = DIN_DOMAIN( uiRefDrn) + 1; /* Lower bounds */
FLMBYTE ucBtKeyBuf[ MAX_KEY_SIZ ]; /* Key buffer pointed to by stack */
DIN_STATE dinState;
FLMUINT uiTmpDrn;
*pbFoundRV = FALSE;
f_memset( &dinState, 0, sizeof( DIN_STATE));
/* Initialize stack cache. */
FSInitStackCache( &(stackBuf[ 0]), BH_MAX_LEVELS);
stack = stackBuf;
stack->pKeyBuf = ucBtKeyBuf;
/* Search for the key. */
if( RC_BAD( rc = FSBtSearch( pDb,
pLFile, &stack, pucKey, uiKeyLen, uiDinDomain)))
{
goto Exit;
}
if( stack->uiCmpStatus == BT_EQ_KEY)
{
uiTmpDrn = uiRefDrn;
/* Reading the current element, position to or after uiTmpDrn */
rc = FSRefSearch( stack, &dinState, &uiTmpDrn);
/* If the entry was not found, returns FERR_FAILURE */
if( rc == FERR_OK)
{
*pbFoundRV = TRUE;
}
else if( rc != FERR_FAILURE)
{
goto Exit;
}
else /* rc == FERR_FAILURE */
{
rc = FERR_OK;
}
}
Exit:
/* Free the stack cache */
FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE);
return( rc );
}
/********************************************************************
Desc: Compares a composite key (index, ref, key) for equality.
Note: Since index references are sorted in decending order, a
composite key with a lower ref DRN will sort after a key
with a higher ref DRN.
*********************************************************************/
FLMINT
chkCompareKeySet(
FLMUINT uiIxNum1,
FLMBYTE * pData1,
FLMUINT uiLength1,
FLMUINT uiDrn1,
FLMUINT uiIxNum2,
FLMBYTE * pData2,
FLMUINT uiLength2,
FLMUINT uiDrn2
)
{
FLMINT iCmpVal = RS_EQUALS;
FLMUINT uiMinLen;
/* Compare index numbers */
if( uiIxNum1 > uiIxNum2)
{
iCmpVal = RS_GREATER_THAN;
goto Exit;
}
else if( uiIxNum1 < uiIxNum2)
{
iCmpVal = RS_LESS_THAN;
goto Exit;
}
/* Compare keys */
uiMinLen = (FLMUINT)(uiLength1 < uiLength2) ? uiLength1 : uiLength2;
iCmpVal = f_memcmp( pData1, pData2, uiMinLen);
if( iCmpVal == 0)
{
/* Compare references */
if( uiLength1 == uiLength2)
{
/*
A key with a lower ref DRN will sort after a key
with a higher ref DRN.
*/
if( uiDrn1 > uiDrn2)
{
iCmpVal = RS_LESS_THAN;
}
else if( uiDrn1 < uiDrn2)
{
iCmpVal = RS_GREATER_THAN;
}
else
{
iCmpVal = RS_EQUALS;
goto Exit;
}
}
else if( uiLength1 > uiLength2)
{
iCmpVal = RS_GREATER_THAN;
}
else
{
iCmpVal = RS_LESS_THAN;
}
}
else
{
iCmpVal = (FLMINT)((iCmpVal > 0)
? (FLMINT)RS_GREATER_THAN
: (FLMINT)RS_LESS_THAN);
}
Exit:
return( iCmpVal);
}
/***************************************************************************
Desc: This routine adds or deletes an index key and/or reference.
*****************************************************************************/
FSTATIC RCODE
chkAddDelKeyRef(
STATE_INFO * pStateInfo,
IX_CHK_INFO * pIxChkInfo,
FLMUINT uiIndexNum,
FLMBYTE * pucKey,
FLMUINT uiKeyLen,
FLMUINT uiDrn,
FLMUINT uiFlags
)
{
RCODE rc = FERR_OK;
RCODE rc2 = FERR_OK;
FLMBYTE ucKeyBuf[ sizeof( KREF_ENTRY) + MAX_KEY_SIZ];
KREF_ENTRY * pKrefEntry = (KREF_ENTRY *)(&ucKeyBuf[ 0]);
IXD * pIxd;
LFILE * pLFile;
FLMBOOL bStartedUpdate = FALSE;
FLMBOOL bKeyInRec;
FLMBOOL bKeyInIndex;
/* Start an update transaction, if necessary */
if( RC_BAD( rc = chkStartUpdate( pStateInfo, pIxChkInfo)))
{
goto Exit;
}
bStartedUpdate = TRUE;
/* Look up the LFILE and IXD for the index. */
if( RC_BAD( rc = fdictGetIndex( pStateInfo->pDb->pDict,
pStateInfo->pDb->pFile->bInLimitedMode, uiIndexNum,
&pLFile, &pIxd)))
{
// Shouldn't get FERR_INDEX_OFFLINE in here.
goto Exit;
}
/* Verify that the state has not changed */
if( RC_BAD( rc = chkGetKeySource( pStateInfo, pIxChkInfo,
uiIndexNum, pucKey, uiKeyLen, uiDrn, NULL,
&bKeyInRec, &bKeyInIndex)))
{
goto Exit;
}
if( (bKeyInIndex == TRUE && ((uiFlags & KREF_DELETE_FLAG) != 0)) ||
(bKeyInIndex == FALSE && uiFlags == 0))
{
/* Setup the KrefEntry structure */
flmAssert( uiIndexNum > 0 && uiIndexNum < FLM_UNREGISTERED_TAGS); // Sanity check
f_memcpy( &(ucKeyBuf[ sizeof( KREF_ENTRY)]), pucKey, uiKeyLen);
pKrefEntry->ui16KeyLen = (FLMUINT16)uiKeyLen;
pKrefEntry->ui16IxNum = (FLMUINT16)uiIndexNum;
pKrefEntry->uiDrn = uiDrn;
pKrefEntry->uiTrnsSeq = 1;
pKrefEntry->uiFlags = uiFlags;
if( (pIxd->uiFlags & IXD_UNIQUE) != 0)
{
/*
Do not allow duplicate keys to be added to a unique index.
*/
pKrefEntry->uiFlags |= KREF_UNIQUE_KEY;
}
/* Add or delete the key/reference. */
if( RC_BAD( rc = FSRefUpdate( pStateInfo->pDb, pLFile, pKrefEntry)))
{
goto Exit;
}
/* Update statistics */
pIxChkInfo->pDbInfo->pProgress->uiNumProblemsFixed++;
}
Exit:
/* End the update. */
if( bStartedUpdate == TRUE)
{
if( RC_BAD( rc2 = chkEndUpdate( pStateInfo, pIxChkInfo)))
{
goto Exit;
}
}
rc = (RCODE)((rc != FERR_OK) ? (RCODE)rc : (RCODE)rc2);
return( rc);
}
/********************************************************************
Desc: Populates the CORRUPT_INFO structure and calls the user's
callback routine.
*********************************************************************/
FSTATIC RCODE chkReportIxError(
STATE_INFO * pStateInfo,
IX_CHK_INFO * pIxChkInfo,
eCorruptionType eCorruption,
FLMUINT uiErrIx,
FLMUINT uiErrDrn,
FLMBYTE * pucErrKey,
FLMUINT uiErrKeyLen,
FLMBOOL * pbFixErrRV
)
{
FDB * pDb = pStateInfo->pDb;
POOL * pTmpPool;
IXD * pIxd;
LFILE * pLFile;
void * pIxPoolMark;
void * pDbPoolMark = NULL;
FLMBOOL bResetKRef = FALSE;
RCODE rc = FERR_OK;
CORRUPT_INFO CorruptInfo;
FLMUINT uiContainerNum;
f_memset( &CorruptInfo, 0, sizeof( CORRUPT_INFO));
// Mark the index check pool
pIxPoolMark = GedPoolMark( &(pIxChkInfo->pool));
pTmpPool = &(pIxChkInfo->pool);
// Need to mark the DB's temporary pool. The index code allocates
// memory for new CDL entries from the DB pool. If the pool is not
// reset, it grows during the check and becomes VERY large.
pDbPoolMark = GedPoolMark( &(pDb->TempPool));
// Set up the KRef so that flmGetRecKeys will work
if( RC_BAD( rc = KrefCntrlCheck( pDb)))
{
goto Exit;
}
bResetKRef = TRUE;
// Report the error
CorruptInfo.eErrLocale = LOCALE_INDEX;
CorruptInfo.eCorruption = eCorruption;
CorruptInfo.uiErrLfNumber = uiErrIx;
CorruptInfo.uiErrDrn = uiErrDrn;
CorruptInfo.uiErrElmOffset = pStateInfo->uiElmOffset;
if (RC_BAD( rc = fdictGetIndex( pDb->pDict,
pDb->pFile->bInLimitedMode, uiErrIx,
NULL, &pIxd, TRUE)))
{
goto Exit;
}
/* Generate the key tree using the key that caused the error */
if( RC_BAD( rc = chkKeyToTree( pIxd, pucErrKey, uiErrKeyLen,
&(CorruptInfo.pErrIxKey))))
{
goto Exit;
}
/* Get the LFile */
if ((uiContainerNum = pIxd->uiContainerNum) == 0)
{
// Container number is always the last two bytes of the key.
flmAssert( uiErrKeyLen > getIxContainerPartLen( pIxd));
uiContainerNum = getContainerFromKey( pucErrKey, uiErrKeyLen);
}
/* Get the LFile */
if( RC_BAD( rc = fdictGetContainer( pDb->pDict, uiContainerNum, &pLFile)))
{
goto Exit;
}
/* Read the record */
if( RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL,
pLFile->uiLfNum, uiErrDrn, FALSE, NULL, NULL, &(CorruptInfo.pErrRecord))))
{
if (rc != FERR_NOT_FOUND)
goto Check_Error;
// NOTE: Deliberately not bringing in to cache if not found there.
if( RC_BAD( rc = FSReadRecord( pDb, pLFile, uiErrDrn,
&(CorruptInfo.pErrRecord), NULL, NULL)))
{
Check_Error:
/*
Record may have been deleted or cannot be returned
because of an old view error.
*/
if( rc == FERR_NOT_FOUND)
{
rc = FERR_OK;
}
else if( FlmErrorIsFileCorrupt( rc))
{
pIxChkInfo->pDbInfo->pProgress->bPhysicalCorrupt = TRUE;
rc = FERR_OK;
goto Exit;
}
else
{
goto Exit;
}
}
}
/* Generate index keys for the current index and record */
if( CorruptInfo.pErrRecord != NULL)
{
if (RC_BAD( rc = flmGetRecKeys( pDb, pIxd,
CorruptInfo.pErrRecord,
CorruptInfo.pErrRecord->getContainerID(),
TRUE, pTmpPool,
&(CorruptInfo.pErrRecordKeyList))))
{
goto Exit;
}
}
*pbFixErrRV = FALSE;
if ((pIxChkInfo->pDbInfo->fnStatusFunc) &&
(RC_OK( pIxChkInfo->pDbInfo->LastStatusRc)))
{
pIxChkInfo->pDbInfo->LastStatusRc =
(*pIxChkInfo->pDbInfo->fnStatusFunc)( FLM_PROBLEM_STATUS,
(void *)&CorruptInfo,
(void *)pbFixErrRV,
pIxChkInfo->pDbInfo->pProgress->AppArg);
}
Exit:
if( CorruptInfo.pErrRecord)
{
CorruptInfo.pErrRecord->Release();
}
if( CorruptInfo.pErrIxKey)
{
CorruptInfo.pErrIxKey->Release();
}
if( CorruptInfo.pErrRecordKeyList)
{
REC_KEY * pTempKey = CorruptInfo.pErrRecordKeyList;
while (pTempKey)
{
pTempKey->pKey->Release();
pTempKey = pTempKey->pNextKey;
}
}
// Remove any keys added to the KRef
if (bResetKRef)
{
KYAbortCurrentRecord( pDb);
}
// Reset the DB's temporary pool
GedPoolReset( &(pDb->TempPool), pDbPoolMark);
// Reset the index check pool
GedPoolReset( &(pIxChkInfo->pool), pIxPoolMark);
return( rc);
}
/***************************************************************************
Desc: This routine verifies that a key is not unique
*****************************************************************************/
FSTATIC RCODE
chkVerifyKeyNotUnique(
STATE_INFO * pStateInfo,
FLMUINT uiIndex,
FLMBYTE * pucKey,
FLMUINT uiKeyLen,
FLMUINT * puiRefCountRV
)
{
FDB * pDb = pStateInfo->pDb;
RCODE rc = FERR_OK;
FlmRecord * pKeyTree = NULL;
IXD * pIxd;
FLMUINT uiRefDrn;
*puiRefCountRV = 0;
/* Get the IXD */
if (RC_BAD( rc = fdictGetIndex( pDb->pDict,
pDb->pFile->bInLimitedMode,
uiIndex, NULL, &pIxd)))
{
goto Exit;
}
/*
This routine should not be called unless the index is
a unique index.
*/
flmAssert( ((pIxd->uiFlags & IXD_UNIQUE) != 0));
/*
Generate the key tree from the collation key.
*/
if( RC_BAD( rc = chkKeyToTree( pIxd, pucKey, uiKeyLen, &pKeyTree)))
{
goto Exit;
}
// Count up to the first two references for the key.
if (RC_BAD( rc = FlmKeyRetrieve( (HFDB)pDb,
uiIndex, pKeyTree->getContainerID(),
pKeyTree, 0, FO_EXACT,
NULL, &uiRefDrn)))
{
// If the key is NOT found, the problem no longer exists.
if ((rc == FERR_NOT_FOUND) ||
(rc == FERR_BOF_HIT) ||
(rc == FERR_EOF_HIT))
{
rc = FERR_OK;
}
goto Exit;
}
// Found at least one reference.
*puiRefCountRV = 1;
// Go exclusive of the last key/reference found to see if there
// are more references for the key.
if (RC_BAD( rc = FlmKeyRetrieve( (HFDB)pDb,
uiIndex, pKeyTree->getContainerID(),
pKeyTree, uiRefDrn, FO_KEY_EXACT | FO_EXCL,
NULL, &uiRefDrn)))
{
if ((rc == FERR_NOT_FOUND) ||
(rc == FERR_BOF_HIT) ||
(rc == FERR_EOF_HIT))
{
rc = FERR_OK;
}
goto Exit;
}
// May be more references, but it is sufficient to know that there
// are at least two.
*puiRefCountRV = 2;
Exit:
return( rc);
}
/***************************************************************************
Desc:
*****************************************************************************/
FSTATIC RCODE chkStartUpdate(
STATE_INFO * pStateInfo,
IX_CHK_INFO * pIxChkInfo
)
{
FDB * pDb = pStateInfo->pDb;
FLMBOOL bAbortedReadTrans = FALSE;
RCODE rc = FERR_OK;
RCODE rc2 = FERR_OK;
if( flmGetDbTransType( pDb) == FLM_READ_TRANS)
{
/* Free the KrefCntrl */
KrefCntrlFree( pDb);
/* Abort the read transaction */
if( RC_BAD( rc = flmAbortDbTrans( pDb)))
{
goto Exit;
}
bAbortedReadTrans = TRUE;
/* Try to start an update transaction */
if( RC_BAD( rc = flmBeginDbTrans( pDb,
FLM_UPDATE_TRANS, pIxChkInfo->pDbInfo->uiMaxLockWait,
FLM_DONT_POISON_CACHE)))
{
goto Exit;
}
pIxChkInfo->pDbInfo->bStartedUpdateTrans = TRUE;
}
if( RC_BAD( pIxChkInfo->pDbInfo->LastStatusRc))
{
rc = pIxChkInfo->pDbInfo->LastStatusRc;
goto Exit;
}
Exit:
/*
If something went wrong after the update transaction was started,
abort the transaction.
*/
if( RC_BAD( rc))
{
if( pIxChkInfo->pDbInfo->bStartedUpdateTrans == TRUE)
{
(void)flmAbortDbTrans( pDb);
pIxChkInfo->pDbInfo->bStartedUpdateTrans = FALSE;
}
}
/*
Re-start the read transaction.
*/
if( bAbortedReadTrans == TRUE &&
pIxChkInfo->pDbInfo->bStartedUpdateTrans == FALSE)
{
rc2 = flmBeginDbTrans( pDb, FLM_READ_TRANS, 0, FLM_DONT_POISON_CACHE);
}
rc = (RCODE)((rc != FERR_OK) ? (RCODE)rc : (RCODE)rc2);
return( rc);
}
/***************************************************************************
Desc:
*****************************************************************************/
FSTATIC RCODE
chkEndUpdate(
STATE_INFO * pStateInfo,
IX_CHK_INFO * pIxChkInfo
)
{
RCODE rc = FERR_OK;
RCODE rc2 = FERR_OK;
if( pIxChkInfo->pDbInfo->bStartedUpdateTrans == TRUE)
{
/*
Commit the update transaction that was started. If the transaction
started by the application, do not commit it.
*/
if( RC_BAD( rc = flmCommitDbTrans( pStateInfo->pDb, 0, FALSE)))
{
goto Exit;
}
pIxChkInfo->pDbInfo->bStartedUpdateTrans = FALSE;
}
Exit:
/* Re-start read transaction */
if( flmGetDbTransType( pStateInfo->pDb) == FLM_NO_TRANS)
{
rc2 = flmBeginDbTrans( pStateInfo->pDb,
FLM_READ_TRANS, 0, FLM_DONT_POISON_CACHE);
}
rc = (RCODE)((rc != FERR_OK) ? (RCODE)rc : (RCODE)rc2);
return( rc);
}
/***************************************************************************
Desc: Initializes a result set for use by the logical check code
*****************************************************************************/
RCODE chkRSInit(
const char * pIoPath,
void ** ppvRSetRV)
{
RCODE rc = FERR_OK;
FResultSet * pRSet;
if( (pRSet = f_new FResultSet) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
if( RC_BAD( rc = pRSet->Setup( pIoPath,
chkCompareIxRSEntries, 0, 0, TRUE, FALSE)))
{
goto Exit;
}
*ppvRSetRV = (void *)pRSet;
Exit:
return( rc);
}
/***************************************************************************
Desc: Sorts a result set.
*****************************************************************************/
RCODE
chkRSFinalize(
IX_CHK_INFO * pIxChkInfo,
FLMUINT64 * pui64TotalEntries
)
{
FResultSet * pRSet = (FResultSet *)(pIxChkInfo->pRSet);
DB_CHECK_PROGRESS * pProgress = pIxChkInfo->pDbInfo->pProgress;
DB_CHECK_PROGRESS saveInfo;
RCODE rc = FERR_OK;
/*
Save the current check phase information.
*/
f_memcpy( &saveInfo, pProgress, sizeof( DB_CHECK_PROGRESS));
/*
Set information for the result set sort phase.
*/
pProgress->iCheckPhase = CHECK_RS_SORT;
pProgress->bStartFlag = TRUE;
pProgress->ui64NumRSUnits = 0;
pProgress->ui64NumRSUnitsDone = 0;
pRSet->SetCallback( chkRSCallbackFunc, (void *)pIxChkInfo);
if( RC_BAD( rc = pRSet->Finalize( pui64TotalEntries)))
{
goto Exit;
}
Exit:
(void)pRSet->SetCallback( NULL, 0);
/*
Reset the pProgress information.
*/
f_memcpy( pProgress, &saveInfo, sizeof( DB_CHECK_PROGRESS));
pProgress->bStartFlag = TRUE;
return( rc);
}
/***************************************************************************
Desc: Compares result set entries during the finalization stage to allow
the result set to be sorted and to remove duplicates.
*****************************************************************************/
RCODE chkCompareIxRSEntries(
void * vpData1,
FLMUINT uiLength1,
void * vpData2,
FLMUINT uiLength2,
void * UserValue,
FLMINT * piCompare)
{
FLMBYTE * pucData1 = (FLMBYTE *)vpData1;
FLMBYTE * pucData2 = (FLMBYTE *)vpData2;
FLMUINT uiIxNum1;
FLMUINT uiIxNum2;
FLMUINT uiDrn1;
FLMUINT uiDrn2;
F_UNREFERENCED_PARM( UserValue);
uiIxNum1 = (FLMUINT)FB2UW( &(pucData1[ RS_IX_OFFSET]));
uiIxNum2 = (FLMUINT)FB2UW( &(pucData2[ RS_IX_OFFSET]));
uiDrn1 = (FLMUINT)FB2UD( &(pucData1[ RS_REF_OFFSET]));
uiDrn2 = (FLMUINT)FB2UD( &(pucData2[ RS_REF_OFFSET]));
*piCompare = chkCompareKeySet(
uiIxNum1, &(pucData1[ RS_KEY_OFFSET]),
uiLength1 - RS_KEY_OVERHEAD, uiDrn1,
uiIxNum2, &(pucData2[ RS_KEY_OFFSET]),
uiLength2 - RS_KEY_OVERHEAD, uiDrn2);
return( FERR_OK);
}
/***************************************************************************
Desc: Callback for result set sort progress.
*****************************************************************************/
FLMINT chkRSCallbackFunc(
RSET_CB_INFO * pCBInfo)
{
IX_CHK_INFO * pIxChkInfo = (IX_CHK_INFO *)pCBInfo->UserValue;
DB_CHECK_PROGRESS * pProgress = pIxChkInfo->pDbInfo->pProgress;
FLMINT iRetVal = 0;
f_yieldCPU();
/*
Set the status values.
*/
pProgress->ui64NumRSUnits = pCBInfo->ui64EstTotalUnits;
pProgress->ui64NumRSUnitsDone = pCBInfo->ui64UnitsDone;
/*
Call the progress callback.
*/
if (RC_BAD( chkCallProgFunc( pIxChkInfo->pDbInfo)))
{
iRetVal = -1;
goto Exit;
}
Exit:
pProgress->bStartFlag = FALSE;
return( iRetVal);
}