//------------------------------------------------------------------------------
// Desc:	Check logical integrity of indexes.
//
// 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 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $
//------------------------------------------------------------------------------

#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);
}

