git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@1010 0109f412-320b-0410-ab79-c3e0c5ffbbe6
1029 lines
21 KiB
C++
1029 lines
21 KiB
C++
//------------------------------------------------------------------------------
|
|
// Desc: Check logical integrity of indexes.
|
|
// Tabs: 3
|
|
//
|
|
// Copyright (c) 1992, 1994-2007 Novell, Inc. All Rights Reserved.
|
|
//
|
|
// This library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
// License as published by the Free Software Foundation; version 2.1
|
|
// of the License.
|
|
//
|
|
// This library 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
|
|
// Library Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; 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$
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "flaimsys.h"
|
|
|
|
/********************************************************************
|
|
Desc: This routine builds a key tree from a collated key
|
|
********************************************************************/
|
|
RCODE F_DbCheck::keyToVector(
|
|
FLMBYTE * pucKey,
|
|
FLMUINT uiKeyLen,
|
|
IF_DataVector ** ppKeyRV
|
|
)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
|
|
// Generate the key tree
|
|
|
|
if ((*ppKeyRV = f_new F_DataVector) == NULL)
|
|
{
|
|
rc = RC_SET( NE_XFLM_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
(*ppKeyRV)->reset();
|
|
|
|
rc = (*ppKeyRV)->inputKey( m_pDb, m_pIxd->uiIndexNum, pucKey, uiKeyLen);
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Retrieves the next key from the sorted result set
|
|
*********************************************************************/
|
|
RCODE F_DbCheck::chkGetNextRSKey( void)
|
|
{
|
|
RCODE rc = NE_XFLM_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 = m_pCurrRSKey;
|
|
m_pCurrRSKey = m_pPrevRSKey;
|
|
m_pPrevRSKey = pCurrRSKey;
|
|
pCurrRSKey = m_pCurrRSKey;
|
|
|
|
if (pCurrRSKey == NULL)
|
|
{
|
|
rc = RC_SET( NE_XFLM_EOF_HIT);
|
|
goto Exit;
|
|
}
|
|
|
|
// Get the next key - call getFirst because we are deleting the
|
|
// entry after we process it.
|
|
|
|
if (RC_BAD( rc = m_pIxRSet->getFirst( m_pDb, m_pIxd, NULL,
|
|
pCurrRSKey->pucRSKeyBuf,
|
|
XFLM_MAX_KEY_SIZE,
|
|
&pCurrRSKey->uiRSKeyLen,
|
|
pCurrRSKey->pucRSDataBuf,
|
|
XFLM_MAX_KEY_SIZE,
|
|
&pCurrRSKey->uiRSDataLen)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Verify that the key meets the minimum length requirements
|
|
|
|
flmAssert( pCurrRSKey->uiRSKeyLen);
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Verifies the current index key against the result set.
|
|
*********************************************************************/
|
|
RCODE F_DbCheck::verifyIXRSet(
|
|
STATE_INFO * pStateInfo)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
FLMINT iCmpVal = 0;
|
|
FLMUINT uiIteration = 0;
|
|
FLMBOOL bRSetEmpty = FALSE;
|
|
RS_IX_KEY * pCurrRSKey;
|
|
RS_IX_KEY * pPrevRSKey;
|
|
|
|
if (!m_pCurrRSKey)
|
|
{
|
|
m_pCurrRSKey = &m_IxKey1;
|
|
m_pPrevRSKey = &m_IxKey2;
|
|
}
|
|
|
|
// Compare index and result set keys
|
|
|
|
while (!bRSetEmpty)
|
|
{
|
|
if (m_bGetNextRSKey)
|
|
{
|
|
|
|
// Get the next key from the result set. If the result set
|
|
// is empty, then m_uiRSKeyLen will be set to
|
|
// zero, forcing the problem to be resolved below.
|
|
|
|
if (RC_BAD( rc = chkGetNextRSKey()))
|
|
{
|
|
if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_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 = NE_XFLM_OK;
|
|
}
|
|
else
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// Update statistics
|
|
|
|
m_Progress.ui64NumKeysExamined++;
|
|
}
|
|
}
|
|
pCurrRSKey = m_pCurrRSKey;
|
|
pPrevRSKey = m_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.
|
|
|
|
if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIxd, NULL, NULL, NULL,
|
|
TRUE, TRUE,
|
|
pCurrRSKey->pucRSKeyBuf,
|
|
pCurrRSKey->uiRSKeyLen,
|
|
pStateInfo->pucElmKey,
|
|
pStateInfo->uiElmKeyLen, &iCmpVal)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (iCmpVal < 0)
|
|
{
|
|
|
|
// 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 = resolveIXMissingKey( pStateInfo)))
|
|
{
|
|
|
|
// 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.
|
|
|
|
m_bGetNextRSKey = (bRSetEmpty ? TRUE : 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.
|
|
|
|
m_bGetNextRSKey = TRUE;
|
|
|
|
// Delete the current key in the result set so we don't hit it again.
|
|
|
|
if (RC_BAD( rc = m_pIxRSet->deleteEntry( m_pDb, m_pIxd,
|
|
pCurrRSKey->pucRSKeyBuf, pCurrRSKey->uiRSKeyLen)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
else if (iCmpVal > 0)
|
|
{
|
|
|
|
// 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.
|
|
|
|
m_bGetNextRSKey = (bRSetEmpty ? TRUE : FALSE);
|
|
if ( RC_BAD( rc = resolveRSetMissingKey( pStateInfo)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
|
|
// The index and result set keys are equal. We want to get
|
|
// the next result set key.
|
|
|
|
m_bGetNextRSKey = TRUE;
|
|
|
|
// Delete the current key in the result set so we don't hit it again.
|
|
|
|
if (RC_BAD( rc = m_pIxRSet->deleteEntry( m_pDb, m_pIxd,
|
|
pCurrRSKey->pucRSKeyBuf, pCurrRSKey->uiRSKeyLen)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// Call the yield function periodically
|
|
|
|
uiIteration++;
|
|
if (!(uiIteration & 0x1F) )
|
|
{
|
|
f_yieldCPU();
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Resolves the case of a key found in the result set but not in
|
|
the current index.
|
|
*********************************************************************/
|
|
RCODE F_DbCheck::resolveIXMissingKey(
|
|
STATE_INFO * pStateInfo)
|
|
{
|
|
FLMBOOL bKeyInDoc;
|
|
FLMBOOL bKeyInIndex;
|
|
RCODE rc = NE_XFLM_OK;
|
|
FLMBOOL bFixCorruption = FALSE;
|
|
RS_IX_KEY * pCurrRSKey = m_pCurrRSKey;
|
|
XFLM_INDEX_STATUS ixStatus;
|
|
|
|
// Determine if the record generates the key and if the
|
|
// key is found in the index.
|
|
|
|
if (RC_BAD( rc = getKeySource( pCurrRSKey->pucRSKeyBuf,
|
|
pCurrRSKey->uiRSKeyLen,
|
|
&bKeyInDoc, &bKeyInIndex)))
|
|
{
|
|
if (rc == NE_XFLM_INDEX_OFFLINE)
|
|
{
|
|
rc = NE_XFLM_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 (!bKeyInDoc || bKeyInIndex)
|
|
{
|
|
m_Progress.ui64NumConflicts++;
|
|
goto Exit;
|
|
}
|
|
|
|
// Otherwise, the index is corrupt.
|
|
|
|
// Update statistics
|
|
|
|
m_Progress.ui64NumDocKeysNotFound++;
|
|
m_pDbInfo->m_uiLogicalCorruptions++;
|
|
|
|
// Report the error
|
|
|
|
if (RC_BAD( rc = reportIxError( pStateInfo,
|
|
FLM_KEY_NOT_IN_KEY_REFSET,
|
|
pCurrRSKey->pucRSKeyBuf,
|
|
pCurrRSKey->uiRSKeyLen,
|
|
&bFixCorruption)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Exit if the application does not want to repair the corruption.
|
|
|
|
if (!bFixCorruption)
|
|
{
|
|
|
|
// Set the logical corruption flag
|
|
|
|
m_bIndexCorrupt = TRUE;
|
|
goto Exit;
|
|
}
|
|
|
|
// Fix the corruption
|
|
|
|
if (RC_BAD( rc = m_pDb->indexStatus( m_pIxd->uiIndexNum, &ixStatus)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (ixStatus.ui64LastDocumentIndexed == (FLMUINT64)~0 &&
|
|
ixStatus.eState != XFLM_INDEX_SUSPENDED)
|
|
{
|
|
|
|
// Update statistics
|
|
|
|
m_pDbInfo->m_uiLogicalRepairs++;
|
|
|
|
// Add the key
|
|
|
|
if (RC_OK( rc = addDelKeyRef( pCurrRSKey->pucRSKeyBuf,
|
|
pCurrRSKey->uiRSKeyLen,
|
|
FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Set the logical corruption flag
|
|
|
|
m_bIndexCorrupt = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// Set the logical corruption flag
|
|
|
|
m_bIndexCorrupt = TRUE;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
|
|
/********************************************************************
|
|
Desc: Resolves the case of a key found in the current index but not
|
|
in the result set.
|
|
*********************************************************************/
|
|
RCODE F_DbCheck::resolveRSetMissingKey(
|
|
STATE_INFO * pStateInfo)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
FLMBOOL bKeyInDoc;
|
|
FLMBOOL bKeyInIndex;
|
|
FLMBOOL bFixCorruption = FALSE;
|
|
XFLM_INDEX_STATUS ixStatus;
|
|
|
|
// See if the key is found in the index and/or generated
|
|
// by the document.
|
|
|
|
if (RC_BAD( rc = getKeySource( pStateInfo->pucElmKey,
|
|
pStateInfo->uiElmKeyLen,
|
|
&bKeyInDoc, &bKeyInIndex)))
|
|
{
|
|
if (rc == NE_XFLM_INDEX_OFFLINE)
|
|
{
|
|
rc = NE_XFLM_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 (bKeyInDoc || !bKeyInIndex)
|
|
{
|
|
m_Progress.ui64NumConflicts++;
|
|
goto Exit;
|
|
}
|
|
|
|
// Otherwise, the index is corrupt.
|
|
|
|
// Update statistics
|
|
|
|
m_Progress.ui64NumKeysNotFound++;
|
|
m_pDbInfo->m_uiLogicalCorruptions++;
|
|
|
|
// Report the error
|
|
|
|
if (RC_BAD( rc = reportIxError( pStateInfo,
|
|
FLM_IX_KEY_NOT_FOUND_IN_REC,
|
|
pStateInfo->pucElmKey,
|
|
pStateInfo->uiElmKeyLen,
|
|
&bFixCorruption)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Exit if the application does not want to repair the corruption.
|
|
|
|
if (!bFixCorruption)
|
|
{
|
|
|
|
// Set the logical corruption flag
|
|
|
|
m_bIndexCorrupt = TRUE;
|
|
goto Exit;
|
|
}
|
|
|
|
// Fix the corruption
|
|
|
|
if (RC_BAD( rc = m_pDb->indexStatus( m_pIxd->uiIndexNum, &ixStatus)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (ixStatus.ui64LastDocumentIndexed == (FLMUINT64)~0 &&
|
|
ixStatus.eState != XFLM_INDEX_SUSPENDED)
|
|
{
|
|
|
|
// Update statistics
|
|
|
|
m_pDbInfo->m_uiLogicalRepairs++;
|
|
|
|
// Delete the reference from the index
|
|
|
|
if (RC_BAD( rc = addDelKeyRef( pStateInfo->pucElmKey,
|
|
pStateInfo->uiElmKeyLen,
|
|
TRUE)))
|
|
{
|
|
// Set the logical corruption flag
|
|
|
|
m_bIndexCorrupt = TRUE;
|
|
}
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Set the logical corruption flag
|
|
|
|
m_bIndexCorrupt = TRUE;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Verifies that a key component is actually in the document.
|
|
*********************************************************************/
|
|
RCODE F_DbCheck::verifyComponentInDoc(
|
|
ICD * pIcd,
|
|
FLMUINT uiComponent,
|
|
F_DataVector * pKey,
|
|
FLMBOOL * pbInDoc
|
|
)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
F_DOMNode * pDOMNode = NULL;
|
|
FLMUINT64 ui64NodeId;
|
|
FLMUINT uiNameId;
|
|
|
|
// Get the nodeId.
|
|
|
|
if ((ui64NodeId = pKey->getID( uiComponent)) != 0)
|
|
{
|
|
if (RC_BAD( rc = m_pDb->getNode( m_pIxd->uiCollectionNum, ui64NodeId,
|
|
&pDOMNode)))
|
|
{
|
|
if (rc == NE_XFLM_DOM_NODE_NOT_FOUND)
|
|
{
|
|
rc = NE_XFLM_OK;
|
|
}
|
|
*pbInDoc = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
// No need to verify the name ID if it is an element root tag.
|
|
|
|
if( pIcd->uiFlags & ICD_IS_ATTRIBUTE)
|
|
{
|
|
if( RC_BAD( rc = pDOMNode->hasAttribute( m_pDb, pIcd->uiDictNum)))
|
|
{
|
|
if( rc == NE_XFLM_DOM_NODE_NOT_FOUND)
|
|
{
|
|
rc = NE_XFLM_OK;
|
|
}
|
|
|
|
*pbInDoc = FALSE;
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if( pIcd->uiDictNum != ELM_ROOT_TAG)
|
|
{
|
|
if (RC_BAD( rc = pDOMNode->getNameId( m_pDb, &uiNameId)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (uiNameId != pIcd->uiDictNum)
|
|
{
|
|
*pbInDoc = FALSE;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Make sure these are the same type.
|
|
|
|
if ((pKey->isAttr( uiComponent) && !(pIcd->uiFlags & ICD_IS_ATTRIBUTE)) ||
|
|
(!pKey->isAttr( uiComponent) && (pIcd->uiFlags & ICD_IS_ATTRIBUTE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Verify that the node belongs to the document.
|
|
|
|
if (pKey->getDocumentID() != pDOMNode->getDocumentId())
|
|
{
|
|
*pbInDoc = FALSE;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (pDOMNode)
|
|
{
|
|
pDOMNode->Release();
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Determines if a key is generated by the current document
|
|
and/or if the key is found in the current index
|
|
*********************************************************************/
|
|
RCODE F_DbCheck::getKeySource(
|
|
FLMBYTE * pucKey,
|
|
FLMUINT uiKeyLen,
|
|
FLMBOOL * pbKeyInDoc,
|
|
FLMBOOL * pbKeyInIndex
|
|
)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
ICD * pIcd;
|
|
FLMUINT uiComponent;
|
|
F_DataVector key;
|
|
|
|
// Initialize return values.
|
|
|
|
*pbKeyInDoc = FALSE;
|
|
*pbKeyInIndex = FALSE;
|
|
|
|
if (m_pIxd->uiFlags & IXD_OFFLINE)
|
|
{
|
|
rc = RC_SET( NE_XFLM_INDEX_OFFLINE);
|
|
goto Exit;
|
|
}
|
|
|
|
// See if the key is in the index.
|
|
|
|
if (RC_BAD( rc = chkVerifyKeyExists( pucKey, uiKeyLen, pbKeyInIndex)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Put the key into a data vector structure.
|
|
|
|
if (RC_BAD( rc = key.inputKey( m_pDb, m_pIxd->uiIndexNum, pucKey, uiKeyLen)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// See if all of the nodes referenced from the key actually are in the
|
|
// document. This includes data nodes and context nodes.
|
|
|
|
*pbKeyInDoc = TRUE;
|
|
uiComponent = 0;
|
|
pIcd = m_pIxd->pFirstKey;
|
|
while (pIcd)
|
|
{
|
|
if (RC_BAD( rc = verifyComponentInDoc( pIcd, uiComponent, &key, pbKeyInDoc)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (!(*pbKeyInDoc))
|
|
{
|
|
goto Exit;
|
|
}
|
|
uiComponent++;
|
|
pIcd = pIcd->pNextKeyComponent;
|
|
}
|
|
|
|
// Go through data components.
|
|
|
|
pIcd = m_pIxd->pFirstData;
|
|
while (pIcd)
|
|
{
|
|
if (RC_BAD( rc = verifyComponentInDoc( pIcd, uiComponent, &key, pbKeyInDoc)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (!(*pbKeyInDoc))
|
|
{
|
|
goto Exit;
|
|
}
|
|
uiComponent++;
|
|
pIcd = pIcd->pNextDataComponent;
|
|
}
|
|
|
|
// Go through context components
|
|
|
|
pIcd = m_pIxd->pFirstContext;
|
|
while (pIcd)
|
|
{
|
|
if (RC_BAD( rc = verifyComponentInDoc( pIcd, uiComponent, &key, pbKeyInDoc)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (!(*pbKeyInDoc))
|
|
{
|
|
goto Exit;
|
|
}
|
|
uiComponent++;
|
|
pIcd = pIcd->pNextKeyComponent;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Verify that a key is (or is not) found in an index.
|
|
*********************************************************************/
|
|
RCODE F_DbCheck::chkVerifyKeyExists(
|
|
FLMBYTE * pucKey,
|
|
FLMUINT uiKeyLen,
|
|
FLMBOOL * pbFoundRV)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
F_Btree * pbtree = NULL;
|
|
IXKeyCompare compareObject;
|
|
|
|
compareObject.setIxInfo( m_pDb, m_pIxd);
|
|
compareObject.setCompareNodeIds( TRUE);
|
|
compareObject.setCompareDocId( TRUE);
|
|
|
|
*pbFoundRV = FALSE;
|
|
|
|
// Get a btree
|
|
|
|
if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pbtree)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pbtree->btOpen( m_pDb, &m_pIxd->lfInfo,
|
|
(m_pIxd->uiFlags & IXD_ABS_POS) ? TRUE : FALSE, FALSE,
|
|
&compareObject)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pbtree->btLocateEntry(
|
|
pucKey, uiKeyLen, &uiKeyLen, XFLM_EXACT)))
|
|
{
|
|
if( rc == NE_XFLM_NOT_FOUND)
|
|
{
|
|
rc = NE_XFLM_OK;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
*pbFoundRV = TRUE;
|
|
|
|
Exit:
|
|
|
|
if (pbtree)
|
|
{
|
|
gv_XFlmSysData.pBtPool->btpReturnBtree( &pbtree);
|
|
}
|
|
|
|
return( rc );
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: This routine adds or deletes an index key and/or reference.
|
|
*****************************************************************************/
|
|
RCODE F_DbCheck::addDelKeyRef(
|
|
FLMBYTE * pucKey,
|
|
FLMUINT uiKeyLen,
|
|
FLMBOOL bDelete)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
FLMBYTE ucKeyBuf[ sizeof( KREF_ENTRY) + XFLM_MAX_KEY_SIZE];
|
|
KREF_ENTRY * pKrefEntry = (KREF_ENTRY *)(&ucKeyBuf[ 0]);
|
|
FLMBOOL bStartedUpdate = FALSE;
|
|
FLMBOOL bKeyInDoc;
|
|
FLMBOOL bKeyInIndex;
|
|
|
|
// Start an update transaction, if necessary
|
|
|
|
if (RC_BAD( rc = startUpdate()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
bStartedUpdate = TRUE;
|
|
|
|
// Verify that the state has not changed
|
|
|
|
if (RC_BAD( rc = getKeySource( pucKey, uiKeyLen,
|
|
&bKeyInDoc, &bKeyInIndex)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if ((bKeyInIndex && bDelete) || (!bKeyInIndex && !bDelete))
|
|
{
|
|
// Setup the KrefEntry structure
|
|
|
|
f_memcpy( &(ucKeyBuf[ sizeof( KREF_ENTRY)]), pucKey, uiKeyLen);
|
|
pKrefEntry->ui16KeyLen = (FLMUINT16)uiKeyLen;
|
|
pKrefEntry->uiDataLen = 0;
|
|
pKrefEntry->ui16IxNum = (FLMUINT16)m_pIxd->uiIndexNum;
|
|
pKrefEntry->uiSequence = 1;
|
|
pKrefEntry->bDelete = bDelete;
|
|
|
|
// Add or delete the key/reference.
|
|
|
|
if (RC_BAD( rc = m_pDb->refUpdate( &m_pIxd->lfInfo, m_pIxd, pKrefEntry,
|
|
FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Update statistics
|
|
|
|
m_Progress.ui32NumProblemsFixed++;
|
|
}
|
|
|
|
Exit:
|
|
|
|
// End the update
|
|
|
|
if (bStartedUpdate)
|
|
{
|
|
RCODE rc2;
|
|
|
|
if (RC_BAD( rc2 = chkEndUpdate()))
|
|
{
|
|
if (RC_OK( rc))
|
|
{
|
|
rc = rc2;
|
|
}
|
|
}
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Populates the XFLM_CORRUPT_INFO structure and calls the user's
|
|
callback routine.
|
|
*********************************************************************/
|
|
RCODE F_DbCheck::reportIxError(
|
|
STATE_INFO * pStateInfo,
|
|
FLMINT32 i32ErrCode,
|
|
FLMBYTE * pucErrKey,
|
|
FLMUINT uiErrKeyLen,
|
|
FLMBOOL * pbFixErrRV
|
|
)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
void * pDbPoolMark = NULL;
|
|
FLMBOOL bResetKRef = FALSE;
|
|
XFLM_CORRUPT_INFO CorruptInfo;
|
|
|
|
f_memset( &CorruptInfo, 0, sizeof( XFLM_CORRUPT_INFO));
|
|
|
|
// 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 = m_pDb->m_tempPool.poolMark();
|
|
|
|
// Set up the KRef so that flmGetRecKeys will work
|
|
|
|
if (RC_BAD( rc = m_pDb->krefCntrlCheck()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
bResetKRef = TRUE;
|
|
|
|
// Fix corruptions by default unless the app says not to.
|
|
|
|
CorruptInfo.ui32ErrLocale = XFLM_LOCALE_INDEX;
|
|
CorruptInfo.i32ErrCode = i32ErrCode;
|
|
CorruptInfo.ui32ErrLfNumber = (FLMUINT32)m_pIxd->uiIndexNum;
|
|
CorruptInfo.ui32ErrElmOffset = (FLMUINT32)pStateInfo->uiElmOffset;
|
|
|
|
// Generate the key tree using the key that caused the error
|
|
|
|
if (RC_BAD( rc = keyToVector( pucErrKey, uiErrKeyLen, &CorruptInfo.ifpErrIxKey)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Report the error
|
|
|
|
*pbFixErrRV = FALSE;
|
|
if (m_pDbCheckStatus && RC_OK( m_LastStatusRc))
|
|
{
|
|
m_LastStatusRc = m_pDbCheckStatus->reportCheckErr( &CorruptInfo, pbFixErrRV);
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (CorruptInfo.ifpErrIxKey)
|
|
{
|
|
CorruptInfo.ifpErrIxKey->Release();
|
|
CorruptInfo.ifpErrIxKey = NULL;
|
|
}
|
|
|
|
// Remove any keys added to the KRef
|
|
|
|
if (bResetKRef)
|
|
{
|
|
m_pDb->krefCntrlFree(); // VISIT: Is this correct?
|
|
}
|
|
|
|
// Reset the index check pool
|
|
|
|
m_pDb->m_tempPool.poolReset(pDbPoolMark);
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: Start an update transaction
|
|
*****************************************************************************/
|
|
RCODE F_DbCheck::startUpdate( void)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
FLMBOOL bAbortedReadTrans = FALSE;
|
|
FLMUINT uiSaveIndexNum = m_pIxd->uiIndexNum;
|
|
|
|
// This routine should never be called unless
|
|
// XFLM_ONLINE flag was passed in to the check.
|
|
|
|
flmAssert( m_uiFlags & XFLM_ONLINE);
|
|
|
|
if (m_pDb->getTransType() == XFLM_READ_TRANS)
|
|
{
|
|
|
|
// Free the KrefCntrl
|
|
|
|
m_pDb->krefCntrlCheck();
|
|
|
|
// Abort the read transaction
|
|
|
|
m_pIxd = NULL;
|
|
if (RC_BAD( rc = m_pDb->transAbort()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
bAbortedReadTrans = TRUE;
|
|
|
|
// Try to start an update transaction
|
|
|
|
if (RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT,
|
|
XFLM_DONT_POISON_CACHE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
m_bStartedUpdateTrans = TRUE;
|
|
|
|
// Must reget the IXD.
|
|
|
|
if (RC_BAD( rc = m_pDb->getDict()->getIndex( uiSaveIndexNum, &m_pLFile,
|
|
&m_pIxd, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (RC_BAD( m_LastStatusRc))
|
|
{
|
|
rc = m_LastStatusRc;
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
// If something went wrong after the update transaction was started,
|
|
// abort the transaction.
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
if (m_bStartedUpdateTrans)
|
|
{
|
|
m_pDb->transAbort();
|
|
m_bStartedUpdateTrans = FALSE;
|
|
}
|
|
}
|
|
|
|
// Re-start the read transaction.
|
|
|
|
if (bAbortedReadTrans && !m_bStartedUpdateTrans)
|
|
{
|
|
RCODE rc2;
|
|
|
|
m_pIxd = NULL;
|
|
if (RC_BAD( rc2 = m_pDb->transBegin( XFLM_READ_TRANS, FLM_NO_TIMEOUT,
|
|
XFLM_DONT_POISON_CACHE)))
|
|
{
|
|
if (RC_OK( rc))
|
|
{
|
|
rc = rc2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (RC_BAD( rc2 = m_pDb->getDict()->getIndex( uiSaveIndexNum, &m_pLFile,
|
|
&m_pIxd, TRUE)))
|
|
{
|
|
if (RC_OK( rc))
|
|
{
|
|
rc = rc2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: End an update transaction.
|
|
*****************************************************************************/
|
|
RCODE F_DbCheck::chkEndUpdate( void)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
FLMUINT uiSaveIndexNum = m_pIxd->uiIndexNum;
|
|
|
|
if (m_bStartedUpdateTrans)
|
|
{
|
|
|
|
// Commit the update transaction that was started. If the transaction
|
|
// started by the application, do not commit it.
|
|
|
|
m_pIxd = NULL;
|
|
m_bStartedUpdateTrans = FALSE;
|
|
if (RC_BAD( rc = m_pDb->transCommit()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
// Re-start read transaction
|
|
|
|
if (m_pDb->getTransType() == XFLM_NO_TRANS)
|
|
{
|
|
RCODE rc2;
|
|
|
|
if (RC_BAD( rc2 = m_pDb->transBegin(
|
|
XFLM_READ_TRANS, FLM_NO_TIMEOUT, XFLM_DONT_POISON_CACHE)))
|
|
{
|
|
if (RC_OK( rc))
|
|
{
|
|
rc = rc2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (RC_BAD( rc2 = m_pDb->getDict()->getIndex( uiSaveIndexNum, &m_pLFile,
|
|
&m_pIxd, TRUE)))
|
|
{
|
|
if (RC_OK( rc))
|
|
{
|
|
rc = rc2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|