Files
mars-flaim/xflaim/src/fslfile.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

649 lines
14 KiB
C++

//------------------------------------------------------------------------------
// Desc: Routines for reading and writing logical file headers.
//
// Tabs: 3
//
// Copyright (c) 1991-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: fslfile.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $
//------------------------------------------------------------------------------
#include "flaimsys.h"
FSTATIC FLMUINT FSLFileFindEmpty(
FLMUINT uiBlockSize,
F_BLK_HDR * pBlkHdr);
/***************************************************************************
Desc: Searches a block for an empty LFH slot. This is called whenever
a new logical file is create so we re-use the slots.
Ret: 0-Empty slot not found
non-zero - offset in the block of the empty slot
***************************************************************************/
FSTATIC FLMUINT FSLFileFindEmpty(
FLMUINT uiBlockSize,
F_BLK_HDR * pBlkHdr)
{
FLMUINT uiPos = SIZEOF_STD_BLK_HDR;
FLMUINT uiEndPos = blkGetEnd( uiBlockSize, SIZEOF_STD_BLK_HDR,
pBlkHdr);
F_LF_HDR * pLfHdr = (F_LF_HDR *)((FLMBYTE *)pBlkHdr + SIZEOF_STD_BLK_HDR);
while (uiPos < uiEndPos)
{
if (pLfHdr->ui32LfType == XFLM_LF_INVALID)
{
break;
}
uiPos += sizeof( F_LF_HDR);
pLfHdr++;
}
return( (uiPos < uiEndPos) ? uiPos : 0);
}
/***************************************************************************
Desc: Retrieves the logical file header record from disk & updates LFILE.
Called when it is discovered that the LFH for a
particular logical file is out of date.
*****************************************************************************/
RCODE F_Database::lFileRead(
F_Db * pDb,
LFILE * pLFile,
F_COLLECTION * pCollection
)
{
RCODE rc = NE_XFLM_OK;
F_CachedBlock * pSCache;
FLMBOOL bReleaseCache = FALSE;
// Read in the block containing the logical file header
if (RC_BAD( rc = getBlock( pDb, NULL,
pLFile->uiBlkAddress, NULL, &pSCache)))
{
goto Exit;
}
bReleaseCache = TRUE;
// Copy the LFH from the block to the LFILE
FSLFileIn( (FLMBYTE *)(pSCache->m_pBlkHdr) + pLFile->uiOffsetInBlk,
pLFile, pCollection, pLFile->uiBlkAddress, pLFile->uiOffsetInBlk);
Exit:
if (bReleaseCache)
{
ScaReleaseCache( pSCache, FALSE);
}
return( rc);
}
/***************************************************************************
Desc: Update the LFH data on disk.
*****************************************************************************/
RCODE F_Database::lFileWrite(
F_Db * pDb,
F_COLLECTION * pCollection,
LFILE * pLFile)
{
RCODE rc = NE_XFLM_OK;
F_CachedBlock * pSCache;
FLMBOOL bReleaseCache = FALSE;
F_LF_HDR * pLfHdr;
#ifdef DEBUG
F_CachedBlock * pTmpSCache = NULL;
#endif
flmAssert( !pDb->m_pDatabase->m_pRfl ||
!pDb->m_pDatabase->m_pRfl->isLoggingEnabled());
// Read in the block containing the logical file header
if (RC_BAD( rc = getBlock( pDb, NULL,
pLFile->uiBlkAddress, NULL, &pSCache)))
{
goto Exit;
}
bReleaseCache = TRUE;
// Log the block before modifying it
if (RC_BAD( rc = logPhysBlk( pDb, &pSCache)))
{
goto Exit;
}
// Now modify the block and set its status to dirty
pLfHdr = (F_LF_HDR *)((FLMBYTE *)(pSCache->m_pBlkHdr) +
pLFile->uiOffsetInBlk);
// If deleted, fill with 0, except for type - it is set below
if (pLFile->eLfType == XFLM_LF_INVALID)
{
f_memset( pLfHdr, 0, sizeof( F_LF_HDR));
pLfHdr->ui32LfType = XFLM_LF_INVALID;
}
else
{
pLfHdr->ui32LfNumber = (FLMUINT32)pLFile->uiLfNum;
pLfHdr->ui32LfType = (FLMUINT32)pLFile->eLfType;
pLfHdr->ui32EncId = (FLMUINT32)pLFile->uiEncId;
#ifdef DEBUG
if (RC_BAD( rc = getBlock( pDb, NULL,
pLFile->uiRootBlk, NULL, &pTmpSCache)))
{
goto Exit;
}
if (!isRootBlk( (F_BTREE_BLK_HDR *)pTmpSCache->m_pBlkHdr))
{
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
}
#endif
pLfHdr->ui32RootBlkAddr = (FLMUINT32)pLFile->uiRootBlk;
if (pCollection)
{
flmAssert( pLFile == &pCollection->lfInfo);
flmAssert( pLFile->eLfType == XFLM_LF_COLLECTION);
pLfHdr->ui64NextNodeId = pCollection->ui64NextNodeId;
pLfHdr->ui64FirstDocId = pCollection->ui64FirstDocId;
pLfHdr->ui64LastDocId = pCollection->ui64LastDocId;
pCollection->bNeedToUpdateNodes = FALSE;
}
else
{
flmAssert( pLFile->eLfType == XFLM_LF_INDEX);
pLfHdr->ui64NextNodeId = 0;
pLfHdr->ui64FirstDocId = 0;
pLfHdr->ui64LastDocId = 0;
}
}
// If the LFILE was deleted, we need to set pLFile->uiLfNum to zero.
// This should happen only AFTER the LFILE update is logged, because
// logLFileUpdate relies on pLFile->uiLfNum being non-zero.
if (pLFile->eLfType == XFLM_LF_INVALID)
{
pLFile->uiLfNum = 0;
}
Exit:
if (bReleaseCache)
{
ScaReleaseCache( pSCache, FALSE);
}
#ifdef DEBUG
if (pTmpSCache)
{
ScaReleaseCache( pTmpSCache, FALSE);
}
#endif
return( rc);
}
/***************************************************************************
Desc: Creates and initializes a LFILE structure on disk and in memory.
*****************************************************************************/
RCODE F_Database::lFileCreate(
F_Db * pDb,
LFILE * pLFile,
F_COLLECTION * pCollection,
FLMUINT uiLfNum,
eLFileType eLfType,
FLMBOOL bCounts,
FLMBOOL bHaveData,
FLMUINT uiEncId)
{
RCODE rc = NE_XFLM_OK;
F_CachedBlock * pNewSCache;
F_CachedBlock * pSCache = NULL;
F_BLK_HDR * pBlkHdr;
FLMUINT uiBlkAddress;
FLMUINT uiNextBlkAddress;
FLMUINT uiEndPos;
FLMUINT uiPos;
FLMBOOL bReleaseCache2 = FALSE;
FLMBOOL bReleaseCache = FALSE;
F_Btree * pbTree = NULL;
flmAssert( !pDb->m_pDatabase->m_pRfl ||
!pDb->m_pDatabase->m_pRfl->isLoggingEnabled());
if (eLfType == XFLM_LF_COLLECTION)
{
if( bCounts)
{
// Force bCounts to be FALSE in this case
flmAssert( 0);
bCounts = FALSE;
}
if( !bHaveData)
{
// Force bHaveData to be TRUE in this case
flmAssert( 0);
bHaveData = TRUE;
}
}
// Find an available slot to create the LFH -- follow the linked list
// of LFH blocks to find one.
uiNextBlkAddress = (FLMUINT)m_uncommittedDbHdr.ui32FirstLFBlkAddr;
// Better be at least one LFH block.
if (uiNextBlkAddress == 0)
{
rc = RC_SET( NE_XFLM_DATA_ERROR);
goto Exit;
}
while (uiNextBlkAddress != 0)
{
if (bReleaseCache)
{
ScaReleaseCache( pSCache, FALSE);
bReleaseCache = FALSE;
}
uiBlkAddress = uiNextBlkAddress;
if (RC_BAD( rc = getBlock( pDb, NULL,
uiBlkAddress, NULL, &pSCache)))
{
goto Exit;
}
bReleaseCache = TRUE;
pBlkHdr = pSCache->m_pBlkHdr;
uiNextBlkAddress = (FLMUINT)pBlkHdr->ui32NextBlkInChain;
uiEndPos = blkGetEnd( m_uiBlockSize, SIZEOF_STD_BLK_HDR, pBlkHdr);
if ((uiPos = FSLFileFindEmpty( m_uiBlockSize, pBlkHdr)) != 0)
{
break;
}
}
// If we did not find a deleted slot we can use, see if there
// is room for a new logical file in the last block
// in the chain. If not, allocate a new block.
if (uiPos == 0)
{
uiEndPos = blkGetEnd( m_uiBlockSize, SIZEOF_STD_BLK_HDR, pBlkHdr);
// Allocate new block?
if (uiEndPos + sizeof( F_LF_HDR) >= m_uiBlockSize)
{
if (RC_BAD( rc = createBlock( pDb, &pNewSCache)))
{
goto Exit;
}
bReleaseCache2 = TRUE;
pBlkHdr = pNewSCache->m_pBlkHdr;
uiNextBlkAddress = (FLMUINT)pBlkHdr->ui32BlkAddr;
// Modify the new block's next pointer and other fields.
pBlkHdr->ui32NextBlkInChain = 0;
pBlkHdr->ui32PrevBlkInChain = (FLMUINT32)uiBlkAddress;
pBlkHdr->ui8BlkType = BT_LFH_BLK;
pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)(m_uiBlockSize -
SIZEOF_STD_BLK_HDR);
if (RC_BAD( rc = logPhysBlk( pDb, &pSCache)))
{
goto Exit;
}
pSCache->m_pBlkHdr->ui32NextBlkInChain = (FLMUINT32)uiNextBlkAddress;
// Set everything up so we are pointing to the new block.
ScaReleaseCache( pSCache, FALSE);
pSCache = pNewSCache;
bReleaseCache2 = FALSE;
uiEndPos = blkGetEnd( m_uiBlockSize, SIZEOF_STD_BLK_HDR,
pSCache->m_pBlkHdr);
uiBlkAddress = uiNextBlkAddress;
}
// Modify the end of block pointer -- log block before modifying.
uiPos = uiEndPos;
uiEndPos += sizeof( F_LF_HDR);
}
// Call memset to ensure unused bytes are zero.
// pBlkHdr, uiPos and uiEndPos should ALL be set.
if (RC_BAD( rc = logPhysBlk( pDb, &pSCache)))
{
goto Exit;
}
pBlkHdr = pSCache->m_pBlkHdr;
f_memset( (FLMBYTE *)(pBlkHdr) + uiPos, 0, sizeof( F_LF_HDR));
flmAssert( uiEndPos >= SIZEOF_STD_BLK_HDR &&
uiEndPos <= m_uiBlockSize);
pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)(m_uiBlockSize - uiEndPos);
// Done with block in this routine
ScaReleaseCache( pSCache, FALSE);
bReleaseCache = FALSE;
// Set the variables in the LFILE structure to later save to disk
pLFile->uiLfNum = uiLfNum;
pLFile->eLfType = eLfType;
pLFile->uiBlkAddress = uiBlkAddress;
pLFile->uiOffsetInBlk = uiPos;
pLFile->uiEncId = uiEncId;
if (pCollection)
{
pCollection->ui64NextNodeId = 1;
pCollection->ui64FirstDocId = 0;
pCollection->ui64LastDocId = 0;
pCollection->bNeedToUpdateNodes = TRUE;
}
// Get the btree...
if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pbTree)))
{
goto Exit;
}
if (RC_BAD( rc = pbTree->btCreate( pDb, pLFile, bCounts, bHaveData)))
{
goto Exit;
}
if (RC_BAD( rc = lFileWrite( pDb, pCollection, pLFile)))
{
goto Exit;
}
Exit:
if (bReleaseCache)
{
ScaReleaseCache( pSCache, FALSE);
}
if (bReleaseCache2)
{
ScaReleaseCache( pNewSCache, FALSE);
}
if (pbTree)
{
gv_XFlmSysData.pBtPool->btpReturnBtree( &pbTree);
}
return( rc);
}
/***************************************************************************
Desc: Delete a logical file.
*****************************************************************************/
RCODE F_Database::lFileDelete(
F_Db * pDb,
F_COLLECTION * pCollection,
LFILE * pLFile,
FLMBOOL bCounts,
FLMBOOL bHaveData)
{
RCODE rc = NE_XFLM_OK;
F_Btree * pbTree = NULL;
F_DOMNode * pDoc = NULL;
F_DOMNode * pChainNode = NULL;
F_DOMNode * pAddrNode = NULL;
FLMUINT uiLoop;
flmAssert( pDb->m_uiFlags & FDB_UPDATED_DICTIONARY);
// Get a btree
if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pbTree)))
{
goto Exit;
}
// Delete the logical file's B-Tree blocks.
// If there is no root block, no need to do anything.
flmAssert( pLFile->uiRootBlk);
if (pLFile->eLfType == XFLM_LF_COLLECTION)
{
flmAssert( pCollection);
flmAssert( !bCounts && bHaveData);
bCounts = FALSE;
bHaveData = TRUE;
if( RC_BAD( rc = pbTree->btOpen( pDb, pLFile, bCounts, bHaveData)))
{
goto Exit;
}
// Delete the B-Tree
if (RC_BAD( rc = pbTree->btDeleteTree( pDb->m_pDeleteStatus)))
{
goto Exit;
}
}
else
{
FLMUINT puiBlkChains[ BH_MAX_LEVELS];
FLMUINT uiChainCount;
flmAssert( !pCollection);
flmAssert( pLFile->eLfType == XFLM_LF_INDEX);
if( RC_BAD( rc = pbTree->btOpen( pDb, pLFile, bCounts, bHaveData)))
{
goto Exit;
}
if( RC_BAD( rc = pbTree->btGetBlockChains(
puiBlkChains, &uiChainCount)))
{
goto Exit;
}
// Indexes are always deleted in the background.
// Set up a maintenance document to free the blocks of
// the B-Tree.
if( RC_BAD( rc = pDb->createRootNode( XFLM_MAINT_COLLECTION,
ELM_DELETE_TAG, ELEMENT_NODE, &pDoc)))
{
goto Exit;
}
for( uiLoop = 0; uiLoop < uiChainCount; uiLoop++)
{
if( RC_BAD( rc = pDoc->createNode( pDb,
ELEMENT_NODE, ELM_BLOCK_CHAIN_TAG, XFLM_LAST_CHILD,
(IF_DOMNode **)&pChainNode)))
{
goto Exit;
}
if( RC_BAD( rc = pChainNode->createAttribute(
pDb, ATTR_ADDRESS_TAG, (IF_DOMNode **)&pAddrNode)))
{
goto Exit;
}
if( RC_BAD( rc = pAddrNode->setUINT(
pDb, puiBlkChains[ uiLoop])))
{
goto Exit;
}
if( RC_BAD( rc = pAddrNode->addModeFlags(
pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE)))
{
goto Exit;
}
if( RC_BAD( rc = pChainNode->addModeFlags(
pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE)))
{
goto Exit;
}
}
if( RC_BAD( rc = pDoc->addModeFlags(
pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE)))
{
goto Exit;
}
if( RC_BAD( rc = pDb->documentDone( pDoc)))
{
goto Exit;
}
// Signal the maintenance thread that it has work to do
f_semSignal( m_hMaintSem);
}
// Delete the LFILE entry.
pLFile->uiRootBlk = 0;
pLFile->eLfType = XFLM_LF_INVALID;
if( RC_BAD( rc = lFileWrite( pDb, pCollection, pLFile)))
{
goto Exit;
}
Exit:
if( pChainNode)
{
pChainNode->Release();
}
if( pAddrNode)
{
pAddrNode->Release();
}
if( pDoc)
{
pDoc->Release();
}
if( pbTree)
{
gv_XFlmSysData.pBtPool->btpReturnBtree( &pbTree);
}
return( rc);
}
/***************************************************************************
Desc: Set the next node ID for a collection. Must be inside an update
transaction.
*****************************************************************************/
RCODE F_Db::setNextNodeId(
FLMUINT uiCollection,
FLMUINT64 ui64NextNodeId)
{
RCODE rc = NE_XFLM_OK;
F_COLLECTION * pCollection;
F_Rfl * pRfl = m_pDatabase->m_pRfl;
FLMBOOL bStartedTrans = FALSE;
FLMUINT uiRflToken = 0;
if( RC_BAD( rc = checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans)))
{
goto Exit;
}
// Disable RFL logging
pRfl->disableLogging( &uiRflToken);
// Get a pointer to the collection
if (RC_BAD( rc = m_pDict->getCollection( uiCollection, &pCollection)))
{
goto Exit;
}
// Set the next NODE ID for the collection
if (ui64NextNodeId > pCollection->ui64NextNodeId)
{
pCollection->ui64NextNodeId = ui64NextNodeId;
pCollection->bNeedToUpdateNodes = TRUE;
}
// Log the operation
pRfl->enableLogging( &uiRflToken);
if( RC_BAD( rc = pRfl->logSetNextNodeId( this, uiCollection, ui64NextNodeId)))
{
goto Exit;
}
Exit:
if( RC_BAD( rc))
{
setMustAbortTrans( rc);
}
if( uiRflToken)
{
pRfl->enableLogging( &uiRflToken);
}
if( bStartedTrans)
{
if( RC_BAD( rc))
{
transAbort();
}
else
{
rc = transCommit();
}
}
return( rc);
}