//------------------------------------------------------------------------- // Desc: Reduce database size. // Tabs: 3 // // Copyright (c) 1992-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: flreduce.cpp 12265 2006-01-19 14:44:10 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC RCODE FLRReadBlkHdr( FDB_p pDb, FLMUINT uiBlkAddress, FLMBYTE * pucBlockHeader, FLMINT * iTypeRV); FSTATIC RCODE FLRMoveBtreeBlk( FDB_p pDb, FLMUINT uiBlkAddr, FLMUINT uiLfNumber, FLMBOOL * pbDone); FSTATIC RCODE FLRMovePcodeLFHBlk( FDB_p pDb, FLMUINT uiBlkAddr, FLMUINT uiBlkType); FSTATIC RCODE FLRFreeAvailBlk( FDB_p pDb, FLMUINT uiBlkAddr); FSTATIC RCODE FLRFindPrevAvailBlk( FDB_p pDb, FLMUINT * puiBlkAddrRV, FLMBOOL * pbFirstChainFlagRV); /*API~*********************************************************************** Desc : Reduces the size of a FLAIM database file. Notes: The size of the database file is reduced by freeing a specified number of blocks from the available (unused) block list. The blocks are moved to the end of the file and the file is truncated. If the available block list is empty, FLAIM will attemp to add blocks to the list by freeing log extent blocks. *END************************************************************************/ FLMEXP RCODE FLMAPI FlmDbReduceSize( HFDB hDb, FLMUINT uiCount, FLMUINT * puiCountRV ) { RCODE rc; FDB_p pDb = (FDB_p) hDb; F_Rfl * pRfl = NULL; FLMUINT uiLogicalEOF; /* Local variable- change dbd->logEof last*/ FLMUINT uiBlkAddr; FLMUINT uiNumBlksMoved = 0; /* Initialize before again: goto loop */ FLMUINT uiBlkSize; FLMBYTE BlkHeader [BH_OVHD + BH_OVHD ]; /* Enough for future log extent */ FLMINT iType; FLMBOOL bIgnore; FLMBOOL bLoggingWasOff = FALSE; FLMBOOL bRestoreLoggingOffFlag = FALSE; FLMBOOL bLockedDatabase = FALSE; FLMBOOL bDone = FALSE; // Lock the database if not already locked. // Cannot lose exclusive access between the checkpoint and // the update transaction that does the conversion. if( (pDb->uiFlags & FDB_HAS_FILE_LOCK) == 0) { if( RC_BAD( rc = FlmDbLock( hDb, FLM_LOCK_EXCLUSIVE, 0, 15))) { goto Exit; } bLockedDatabase = TRUE; } if (IsInCSMode( pDb)) { fdbInitCS( pDb); CS_CONTEXT_p pCSSession = pDb->pCSContext; FCL_WIRE Wire( pCSSession, pDb); /* Send a request to reduce the file. */ if (RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_DATABASE, FCS_OP_DB_REDUCE_SIZE))) { goto Exit; } if (uiCount) { if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_COUNT, uiCount))) { goto Transmission_Error; } } if (RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } /* Read the response. */ if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } uiNumBlksMoved = (FLMUINT)Wire.getCount(); rc = Wire.getRCode(); goto Exit; Transmission_Error: pCSSession->bConnectionGood = FALSE; goto Exit; } if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, FDB_TRANS_GOING_OK, 0, &bIgnore))) { goto Exit; } /* Make sure we are NOT in a database transaction. */ if (pDb->uiTransType != FLM_NO_TRANS) { rc = RC_SET( FERR_TRANS_ACTIVE); goto Exit; } // Save the state of RFL logging flag. pRfl = pDb->pFile->pRfl; bLoggingWasOff = pRfl->loggingIsOff(); // Change state of logging OFF to TRUE - don't want anything // logged during reduce except for the reduce packet. pRfl->setLoggingOffState( TRUE); bRestoreLoggingOffFlag = TRUE; /**------------------------------------------------------------------- *** Keep looping to here until the count is satisfied or there *** are not any more log extent blocks to turn into avail blks. *** The loop does a begin transaction - move blocks - set logical *** EOF and commits the transaction. During the commit if there are *** not any avail blocks left then a log extent (if any) will be turned *** into more avail blocks and we can do this again with more avail *** blocks. ***------------------------------------------------------------------*/ /* Start a database transaction */ if (RC_BAD(rc = flmBeginDbTrans( pDb, FLM_UPDATE_TRANS, 15, FLM_DONT_POISON_CACHE))) { goto Exit; } // Make sure that commit does something. pDb->bHadUpdOper = TRUE; uiBlkSize = pDb->pFile->FileHdr.uiBlockSize; /**----------------------------------------------------------------- *** Get the logical end of file and use internally. *** Loop until there are not any more free blocks left or the *** input count is matched. Switch on each block type found *** while backing up through the file. ***----------------------------------------------------------------*/ uiLogicalEOF = pDb->LogHdr.uiLogicalEOF; while( (pDb->LogHdr.uiFirstAvailBlkAddr != BT_END ) && ((!uiCount) || (uiNumBlksMoved < uiCount))) { /* Read the last block and determine block type. */ /* Special case for 3x files. */ if( FSGetFileOffset( uiLogicalEOF) == 0) { F_FileHdlImp * pFileHdl; FLMUINT uiFileNumber = FSGetFileNumber( uiLogicalEOF) - 1; FLMUINT uiFileSize; FLMUINT uiTemp; if( RC_BAD( rc = pDb->pSFileHdl->GetFileHdl( uiFileNumber, TRUE, &pFileHdl))) { goto Reduce_Size_Error; } if( RC_BAD( rc = pFileHdl->Size( &uiFileSize))) { goto Reduce_Size_Error; } // Adjust to a block bounds. uiTemp = (uiFileSize / uiBlkSize) * uiBlkSize; if( uiTemp < uiFileSize) { uiFileSize = uiTemp + uiBlkSize; } uiLogicalEOF = FSBlkAddress( uiFileNumber, uiFileSize); } uiBlkAddr = uiLogicalEOF - uiBlkSize; if (RC_BAD( rc = FLRReadBlkHdr( pDb, uiBlkAddr, BlkHeader, &iType))) { goto Reduce_Size_Error; } switch( iType ) { case BHT_FREE: rc = FLRFreeAvailBlk( pDb, uiBlkAddr ); break; case BHT_LEAF: case BHT_NON_LEAF: case BHT_NON_LEAF_DATA: rc = FLRMoveBtreeBlk( pDb, uiBlkAddr, FB2UW( &BlkHeader [BH_LOG_FILE_NUM ]), &bDone); break; case BHT_LFH_BLK: case BHT_PCODE_BLK: rc = FLRMovePcodeLFHBlk( pDb, uiBlkAddr, iType); break; default: rc = RC_SET( FERR_BTREE_ERROR); break; } if (RC_BAD(rc)) goto Reduce_Size_Error; if (bDone) { break; } uiNumBlksMoved++; // Adjust the logical EOF to the new value. // This is complex when dealing with block files. if( FSGetFileOffset( uiLogicalEOF) == 0) { FLMUINT uiFileNumber = FSGetFileNumber( uiLogicalEOF); FLMUINT uiFileOffset; F_FileHdlImp * pFileHdl; if( uiFileNumber <= 1) break; // Leave the current file at zero bytes and move to the // previous store file. uiFileNumber--; // Compute the end of the previous block file. if( RC_BAD( rc = pDb->pSFileHdl->GetFileHdl( uiFileNumber, TRUE, &pFileHdl))) { goto Exit; } if( RC_BAD( rc = pFileHdl->Size( &uiFileOffset))) { goto Exit; } uiLogicalEOF = FSBlkAddress( uiFileNumber, uiFileOffset); } uiLogicalEOF -= uiBlkSize; } // Log the reduce packet to the RFL if we are not in the middle of a // restore or recovery. Will need to re-enable logging temporarily // and then turn it back off after logging the packet. if (!(pDb->uiFlags & FDB_REPLAYING_RFL) && pDb->pFile->FileHdr.uiVersionNum >= FLM_VER_4_3) { // We would have turned logging OFF above, so we need to // turn it back on here. pRfl->setLoggingOffState( FALSE); // Log the reduce. rc = pRfl->logReduce( pDb->LogHdr.uiCurrTransID, uiCount); // Turn logging back off. pRfl->setLoggingOffState( TRUE); if (RC_BAD( rc)) { goto Reduce_Size_Error; } } /* Commit the transaction. */ { FLMBOOL bFlagSet; if (pDb->uiFlags & FDB_DO_TRUNCATE) bFlagSet = TRUE; else { bFlagSet = FALSE; pDb->uiFlags |= FDB_DO_TRUNCATE; } rc = flmCommitDbTrans( pDb, uiLogicalEOF, TRUE); if (!bFlagSet) pDb->uiFlags &= (~(FDB_DO_TRUNCATE)); if (RC_BAD( rc)) goto Exit; } Exit: if( puiCountRV) { *puiCountRV = uiNumBlksMoved; /* May be more than requested count */ } if (bRestoreLoggingOffFlag) { pRfl->setLoggingOffState( bLoggingWasOff); } if (bLockedDatabase) { (void) FlmDbUnlock( hDb); } flmExit( FLM_DB_REDUCE_SIZE, pDb, rc); return( rc); Reduce_Size_Error: (void)flmAbortDbTrans( pDb, FALSE); uiNumBlksMoved = 0; goto Exit; } /**************************************************************************** Desc: Read the block header and return the type of block it is ****************************************************************************/ FSTATIC RCODE FLRReadBlkHdr( FDB_p pDb, FLMUINT uiBlkAddress, FLMBYTE * pucBlockHeader, FLMINT * piTypeRV ) { RCODE rc = FERR_OK; FLMUINT uiBytesRead; FLMUINT uiNumLooks; F_FileHdlImp * pTmpFileHdl = NULL; SCACHE * pBlkSCache; DB_STATS * pDbStats = pDb->pDbStats; LFILE_STATS * pLFileStats; F_TMSTAMP StartTime; FLMUINT64 ui64ElapTime; /**----------------------------------------------- *** See if first the block is in cache. *** Previous writes may not have been forced out *** to cache. ***----------------------------------------------*/ if (RC_BAD( rc = ScaGetBlock( pDb, NULL, BHT_LEAF, uiBlkAddress, &uiNumLooks, &pBlkSCache))) { goto Exit; } if (pBlkSCache) // If found in cache ... { f_memcpy( pucBlockHeader, pBlkSCache->pucBlk, BH_OVHD); ScaReleaseCache( pBlkSCache, FALSE); } else { if (pDbStats) { ui64ElapTime = 0; f_timeGetTimeStamp( &StartTime); } if( RC_OK( rc = pDb->pSFileHdl->GetFileHdl( FSGetFileNumber( uiBlkAddress), TRUE, &pTmpFileHdl))) { rc = pTmpFileHdl->Read( FSGetFileOffset( uiBlkAddress), BH_OVHD, pucBlockHeader, &uiBytesRead); } if (pDbStats) { flmAddElapTime( &StartTime, &ui64ElapTime); if (RC_BAD( rc)) { pDbStats->bHaveStats = TRUE; pDbStats->uiReadErrors++; } else { FLMUINT uiLFileNum; BLOCKIO_STATS * pBlockIOStats; FLMUINT uiBlkType; uiLFileNum = FB2UW( &pucBlockHeader [BH_LOG_FILE_NUM]); if (!uiLFileNum) { pLFileStats = NULL; uiBlkType = (FLMUINT)BH_GET_TYPE( pucBlockHeader); } else { FLMBYTE ucLfType = 0xFF; LFILE * pTmpLFile; if( uiLFileNum == FLM_DICT_INDEX || RC_OK( fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, uiLFileNum, NULL, NULL, TRUE))) { ucLfType = LF_INDEX; } else if( RC_OK( fdictGetContainer( pDb->pDict, uiLFileNum, &pTmpLFile))) { ucLfType = LF_CONTAINER; } if( RC_BAD( flmStatGetLFile( pDbStats, uiLFileNum, ucLfType, 0, &pLFileStats, NULL, NULL))) { pLFileStats = NULL; } if (pLFileStats) uiBlkType = BHT_LEAF; else uiBlkType = (FLMUINT)BH_GET_TYPE( pucBlockHeader); } if ((pBlockIOStats = flmGetBlockIOStatPtr( pDbStats, pLFileStats, pucBlockHeader, uiBlkType)) != NULL) { pDbStats->bHaveStats = TRUE; if (pLFileStats) { pLFileStats->bHaveStats = TRUE; } pBlockIOStats->BlockReads.ui64ElapMilli += ui64ElapTime; pBlockIOStats->BlockReads.ui64Count++; pBlockIOStats->BlockReads.ui64TotalBytes += BH_OVHD; } } } if (RC_BAD( rc)) { if (rc != FERR_IO_END_OF_FILE && rc != FERR_MEM) { pDb->pSFileHdl->ReleaseFile( FSGetFileNumber( uiBlkAddress), TRUE); } goto Exit; } pucBlockHeader [BH_CHECKSUM_LOW] = (FLMBYTE) uiBlkAddress; } /**--------------------------------------------------- *** If the block address does not agree with what *** is expected then the block is a log extent. *** Otherwise the block contains the type it is. ***--------------------------------------------------*/ if (piTypeRV) { *piTypeRV = BH_GET_TYPE( pucBlockHeader ); } Exit: return( rc ); } /**************************************************************************** Desc: Find where in the b-tree a matching block is located. Move to a free block and change all pointers to the block. Notes: Some of this code could be called in movePcodeLFHBlk but we have to worry about if the block is a root or right most leaf block. ****************************************************************************/ FSTATIC RCODE FLRMoveBtreeBlk( FDB_p pDb, FLMUINT uiBlkAddr, // Block Address FLMUINT uiLfNumber, FLMBOOL * pbDone) { RCODE rc; FFILE_p pFile = pDb->pFile; LFILE * pLFile; SCACHE * pSCache; FLMBOOL bReleaseCache = FALSE; FLMBYTE * pucBlk; FLMBYTE * pucFreeBlk; FLMUINT uiFreeBlkAddr; FLMUINT uiLeftBlkAddr; FLMUINT uiRightBlkAddr; SCACHE * pFreeSCache; FLMBOOL bReleaseCache2 = FALSE; BTSK StackArea; /* Single stack allocation */ BTSK_p pStack = &StackArea; /* Points to stack - easier to use */ FLMUINT uiElmOvhd; /* Number of bytes in block overhead */ FLMUINT uiSearchKeyLen; /* Length of block's 1st key */ FLMBYTE ucKeyBuf [MAX_KEY_SIZ]; FLMBYTE ucSearchKey [MAX_KEY_SIZ];/* Holds block's 1st key */ FLMUINT uiTargetLevel; FLMUINT uiLevel; /* Level numbers in the btree */ FLMUINT uiRootBlkFlag; FLMUINT uiSavePrevTransID; FLMUINT uiSavePrevBlkAddr; FLMUINT uiBlockType; FSInitStackCache( &StackArea, 1); pStack->pKeyBuf = ucKeyBuf; pStack->uiKeyBufSize = MAX_KEY_SIZ; if ((RC_BAD( rc = fdictGetContainer( pDb->pDict, uiLfNumber, &pLFile))) && (RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, uiLfNumber, &pLFile, NULL, TRUE)))) { // It may be that the index or container is being deleted by a background // thread. In that case, we need to bail out, as there is nothing // more we can do until the background thread finishes its delete. if (rc == FERR_BAD_IX) { *pbDone = TRUE; rc = FERR_OK; } goto Exit; } if (RC_BAD( rc = ScaGetBlock( pDb, pLFile, BHT_LEAF, uiBlkAddr, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; if (RC_BAD( rc = ScaLogPhysBlk( pDb, &pSCache))) { goto Exit; } pucBlk = pSCache->pucBlk; /**-------------------------------------------------- *** Added 05/04/93 *** Need to make sure that LFILE is up to date. *** Force reading it in. This does a block read *** so the cache will get hit without a great *** performance loss. This is the safest place *** to do this because only pSCache is pinned. ***------------------------------------------------*/ if (RC_BAD( rc = flmLFileRead( pDb, pLFile))) { goto Exit; } /**---------------------------------------- *** Get all of the information from the *** block like linkages and a search key. ***---------------------------------------*/ uiLeftBlkAddr = FB2UD( &pucBlk [BH_PREV_BLK ]); uiRightBlkAddr = FB2UD( &pucBlk [BH_NEXT_BLK ]); uiTargetLevel = (FLMUINT)(pucBlk [BH_LEVEL ] + 1); uiBlockType = BH_GET_TYPE( pucBlk); switch( uiBlockType) { case BHT_LEAF: uiElmOvhd = BBE_KEY; break; case BHT_NON_LEAF: uiElmOvhd = BNE_KEY_START; break; case BHT_NON_LEAF_DATA: uiElmOvhd = 0; // First key starts at BH_OVHD break; case BHT_NON_LEAF_COUNTS: uiElmOvhd = BNE_KEY_COUNTS_START; break; default: rc = RC_SET( FERR_BTREE_ERROR); goto Exit; } uiSearchKeyLen = (uiBlockType == BHT_NON_LEAF_DATA ? 4 : BBE_GET_KL( &pucBlk [BH_OVHD ])); f_memcpy( ucSearchKey, &pucBlk [BH_OVHD + uiElmOvhd ], uiSearchKeyLen ); /**------------------------------- *** Get the next free block. *** Copy block to free block. ***------------------------------*/ if (RC_BAD( rc = FSBlockUseNextAvail( pDb, pLFile, &pFreeSCache))) { goto Exit; } bReleaseCache2 = TRUE; pucFreeBlk = pFreeSCache->pucBlk; uiFreeBlkAddr = GET_BH_ADDR( pucFreeBlk); /* The free block has been logged and set to dirty in FSBlockUseNextAvail(). BUT, need to preserve previous transaction ID and previous block address - those should NOT be copied over from the block we are switching with. */ uiSavePrevTransID = (FLMUINT)FB2UD( &pFreeSCache->pucBlk [BH_PREV_TRANS_ID]); uiSavePrevBlkAddr = (FLMUINT)FB2UD( &pFreeSCache->pucBlk [BH_PREV_BLK_ADDR]); f_memcpy( pFreeSCache->pucBlk, pucBlk, pFile->FileHdr.uiBlockSize); SET_BH_ADDR( pucFreeBlk, uiFreeBlkAddr ); // Restore the saved previous transaction ID and block address. UD2FBA( (FLMUINT32)uiSavePrevTransID, &pFreeSCache->pucBlk [BH_PREV_TRANS_ID]); UD2FBA( (FLMUINT32)uiSavePrevBlkAddr, &pFreeSCache->pucBlk [BH_PREV_BLK_ADDR]); ScaReleaseCache( pFreeSCache, FALSE); // Done with new block bReleaseCache2 = FALSE; uiRootBlkFlag = (FLMUINT)(BH_IS_ROOT_BLK( pucBlk )); /* Done with block. */ ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; /**---------------------------------------------- *** If this is a root block this is easy! *** Otherwise you must find the blocks parent *** and next/prev blocks and change linkages. ***---------------------------------------------*/ if( uiRootBlkFlag) { // Create a new dictionary if( !(pDb->uiFlags & FDB_UPDATED_DICTIONARY)) { if( RC_BAD( rc = fdictCloneDict( pDb))) { goto Exit; } // Re-get the LFile if ((RC_BAD( rc = fdictGetContainer( pDb->pDict, uiLfNumber, &pLFile))) && (RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, uiLfNumber, &pLFile, NULL, TRUE)))) { goto Exit; } } pLFile->uiRootBlk = uiFreeBlkAddr; rc = flmLFileWrite( pDb, pLFile); goto Exit; } /**-------------------------------------------------- *** Read left and right blocks and adjust their *** pointers to point to the new block. *** This doesn't matter what level of the b-tree *** you are on. ***--------------------------------------------------*/ if( uiLeftBlkAddr != BT_END) { if (RC_BAD( rc = ScaGetBlock( pDb, pLFile, BHT_LEAF, uiLeftBlkAddr, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; if (RC_BAD( rc = ScaLogPhysBlk( pDb, &pSCache))) { goto Exit; } pucBlk = pSCache->pucBlk; UD2FBA( uiFreeBlkAddr, &pucBlk [BH_NEXT_BLK ]); ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; } if( uiRightBlkAddr != BT_END) { if (RC_BAD( rc = ScaGetBlock( pDb, pLFile, BHT_LEAF, uiRightBlkAddr, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; if (RC_BAD( rc = ScaLogPhysBlk( pDb, &pSCache))) { goto Exit; } pucBlk = pSCache->pucBlk; UD2FBA( uiFreeBlkAddr, &pucBlk [BH_PREV_BLK ]); ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; } /**-------------------------------------------------- *** Now for the hard part! Build a search key. *** Scan down the tree one level above blk. *** Scan right until find element that has child blk *** that matches uiBlkAddr and adjust child blk addr. ***--------------------------------------------------*/ if (RC_BAD(rc = FSGetBlock( pDb, pLFile, pLFile->uiRootBlk, pStack))) { goto Exit; } pStack->uiBlkAddr = pLFile->uiRootBlk; for( ;;) { uiLevel = (FLMUINT)CABLK_ELM( pStack, BH_LEVEL ); pStack->uiLevel = uiLevel; /* Scan the block for the matching key */ if( pStack->uiBlkType != BHT_NON_LEAF_DATA) { rc = FSBtScan( pStack, ucSearchKey, uiSearchKeyLen, 0); } else { rc = FSBtScanNonLeafData( pStack, byteToLong( ucSearchKey)); } if( RC_BAD( rc)) { goto Exit; } if (uiLevel == uiTargetLevel) break; if (RC_BAD( rc = FSGetBlock( pDb, pLFile, FSChildBlkAddr( pStack), pStack))) { goto Exit; } } /**---------------------------------------------------------- *** The block MUST be a non-leaf block so our job is easier. *** Scan the elements going right to find the element that *** has the matching block address. *** Set NO_STACK flag so get next element doesn't pop stack! ***-----------------------------------------------------------*/ pStack->uiFlags = NO_STACK; for( ;;) { if (FSChildBlkAddr( pStack) == uiBlkAddr ) { /* FOUND THE BLOCK ! ! ! */ if (RC_BAD( rc = FSLogPhysBlk( pDb, pStack))) { goto Exit; } pucBlk = BLK_PTR( pStack ); FSSetChildBlkAddr( &pucBlk [pStack->uiCurElm], uiFreeBlkAddr, (FLMBYTE) pStack->uiElmOvhd); break; } if (RC_BAD( rc = FSBtNextElm( pDb, pLFile, pStack))) { if (rc == FERR_BT_END_OF_DATA) { rc = RC_SET( FERR_BTREE_ERROR); } goto Exit; } } /* WOW - WE ARE DONE ! ! ! */ Exit: FSReleaseBlock( &StackArea, FALSE); if (bReleaseCache) { ScaReleaseCache( pSCache, FALSE); } if (bReleaseCache2) { ScaReleaseCache( pFreeSCache, FALSE); } return( rc); } /**************************************************************************** Desc: Find where a pcode list the input block is located. Move to a free block and change all pointers to the block. ****************************************************************************/ FSTATIC RCODE FLRMovePcodeLFHBlk( FDB_p pDb, FLMUINT uiBlkAddr, FLMUINT uiBlkType) { RCODE rc; SCACHE * pSCache; FLMBOOL bReleaseCache = FALSE; SCACHE * pFreeSCache; FLMBOOL bReleaseCache2 = FALSE; FLMBYTE * pucBlk; FLMBYTE * pucFreeBlk; FLMUINT uiLeftBlkAddr; FLMUINT uiRightBlkAddr; FLMUINT uiFreeBlkAddr; FFILE_p pFile = pDb->pFile; FLMUINT uiSavePrevTransID; FLMUINT uiSavePrevBlkAddr; if (RC_BAD( rc = ScaGetBlock( pDb, NULL, uiBlkType, uiBlkAddr, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; if (RC_BAD( rc = ScaLogPhysBlk( pDb, &pSCache))) { goto Exit; } pucBlk = pSCache->pucBlk; // Get left and rigth block addresses. // Get next avail block and move data over uiLeftBlkAddr = FB2UD( &pucBlk [BH_PREV_BLK ]); uiRightBlkAddr = FB2UD( &pucBlk [BH_NEXT_BLK ]); if (RC_BAD( rc = FSBlockUseNextAvail( pDb, NULL, &pFreeSCache))) { goto Exit; } bReleaseCache2 = TRUE; pucFreeBlk = pFreeSCache->pucBlk; uiFreeBlkAddr = GET_BH_ADDR( pucFreeBlk ); // The free block has been logged and set to dirty in // FSBlockUseNextAvail(). // BUT, need to preserve previous transaction ID and previous // block address - those should NOT be copied over from the block // we are switching with. uiSavePrevTransID = (FLMUINT)FB2UD( &pFreeSCache->pucBlk [BH_PREV_TRANS_ID]); uiSavePrevBlkAddr = (FLMUINT)FB2UD( &pFreeSCache->pucBlk [BH_PREV_BLK_ADDR]); f_memcpy( pFreeSCache->pucBlk, pucBlk, pFile->FileHdr.uiBlockSize); SET_BH_ADDR( pucFreeBlk, uiFreeBlkAddr ); // Restore the saved previous transaction ID and block address. UD2FBA( (FLMUINT32)uiSavePrevTransID, &pFreeSCache->pucBlk [BH_PREV_TRANS_ID]); UD2FBA( (FLMUINT32)uiSavePrevBlkAddr, &pFreeSCache->pucBlk [BH_PREV_BLK_ADDR]); // Fix up any LFile entries that were pointing to the // original LFH block if( uiBlkType == BHT_LFH_BLK) { FLMUINT uiPos = BH_OVHD; FLMUINT uiEndPos = (FLMUINT)FB2UW( &pucFreeBlk[ BH_ELM_END ]); LFILE * pTmpLFile; // Create a new dictionary if( !(pDb->uiFlags & FDB_UPDATED_DICTIONARY)) { if( RC_BAD( rc = fdictCloneDict( pDb))) { goto Exit; } } // Iterate over the set of LFiles in the block and // update their LFH block addresses while( uiPos < uiEndPos) { if( pucFreeBlk[ uiPos + LFH_TYPE_OFFSET] != LF_INVALID) { FLMUINT uiTmpLfNum = (FLMUINT)FB2UW( &pucFreeBlk[ uiPos + LFH_LF_NUMBER_OFFSET]); if( RC_BAD( fdictGetContainer( pDb->pDict, uiTmpLfNum, &pTmpLFile))) { if( RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, uiTmpLfNum, &pTmpLFile, NULL, TRUE))) { flmAssert( 0); rc = RC_SET( FERR_DATA_ERROR); goto Exit; } } pTmpLFile->uiBlkAddress = uiFreeBlkAddr; } uiPos += LFH_SIZE; } } // Done with both blocks. ScaReleaseCache( pFreeSCache, FALSE); ScaReleaseCache( pSCache, FALSE); bReleaseCache2 = bReleaseCache = FALSE; // Read left and right blocks and adjust their // pointers to point to the new block. // This doesn't matter what level of the b-tree // you are on. if (uiLeftBlkAddr != BT_END) { if (RC_BAD( rc = ScaGetBlock( pDb, NULL, uiBlkType, uiLeftBlkAddr, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; if (RC_BAD( rc = ScaLogPhysBlk( pDb, &pSCache))) { goto Exit; } pucBlk = pSCache->pucBlk; UD2FBA( uiFreeBlkAddr, &pucBlk [BH_NEXT_BLK ]); ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; } if (uiRightBlkAddr != BT_END) { if (RC_BAD( rc = ScaGetBlock( pDb, NULL, uiBlkType, uiRightBlkAddr, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; if (RC_BAD( rc = ScaLogPhysBlk( pDb, &pSCache))) { goto Exit; } pucBlk = pSCache->pucBlk; UD2FBA( uiFreeBlkAddr, &pucBlk [BH_PREV_BLK ]); ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; } Exit: if (bReleaseCache) { ScaReleaseCache( pSCache, FALSE); } if (bReleaseCache2) { ScaReleaseCache( pFreeSCache, FALSE); } return( rc); } /**************************************************************************** Desc: Free the input avail block. Link the block out of the free list ****************************************************************************/ FSTATIC RCODE FLRFreeAvailBlk( FDB_p pDb, FLMUINT uiBlkAddr) { RCODE rc = FERR_OK; FFILE_p pFile = pDb->pFile; FLMBYTE ucBlkHeader [BH_OVHD]; FLMBYTE * pucBlk; FLMUINT uiPrevBlkAddr; FLMUINT uiNextBlkAddr; FLMUINT uiPbcAddr; FLMUINT uiNbcAddr; SCACHE * pSCache; FLMBOOL bReleaseCache = FALSE; FLMBOOL bFirstChainFlag; FLMBYTE * pucLogHdr = &pFile->ucUncommittedLogHdr [0]; /* Check for first avail block condition. */ if (uiBlkAddr == pDb->LogHdr.uiFirstAvailBlkAddr) { if (RC_OK( rc = FSBlockUseNextAvail( pDb, NULL, &pSCache))) { ScaReleaseCache( pSCache, FALSE); } goto Exit; } /* Not first block -- resolve pointers */ /* Read the block header and get pointers */ if (RC_BAD( rc = FLRReadBlkHdr( pDb, uiBlkAddr, ucBlkHeader, (FLMINT *)0 ))) { goto Exit; } uiPrevBlkAddr = uiBlkAddr; /* Call uses uiPrevBlkAddr */ if (RC_BAD( rc = FLRFindPrevAvailBlk( pDb, &uiPrevBlkAddr, &bFirstChainFlag))) { goto Exit; } uiNextBlkAddr = FB2UD( &ucBlkHeader [BH_NEXT_BLK]); if (pFile->FileHdr.uiVersionNum >= 111) { /* Only valid for versions 1.11 and higher */ uiPbcAddr = ALGetPBC( ucBlkHeader); uiNbcAddr = ALGetNBC( ucBlkHeader); flmDecrUint( &pucLogHdr [LOG_PF_NUM_AVAIL_BLKS], 1); } else { uiPbcAddr = uiNbcAddr = 0; } /* Check for unexpected error conditions. */ if (( uiPrevBlkAddr == uiBlkAddr) || ((!uiNbcAddr) && ( uiPbcAddr)) || (( uiNbcAddr) && (!uiPbcAddr))) { rc = RC_SET( FERR_DATA_ERROR); goto Exit; } /**-------------------------------- *** NON-CHAIN BLOCK *** This is also minor verion 0 ***-------------------------------*/ if (!uiNbcAddr) /* either both are zero or both non-zero*/ { if (RC_BAD( rc = ScaGetBlock( pDb, NULL, BHT_FREE, uiPrevBlkAddr, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; if (RC_BAD( rc = ScaLogPhysBlk( pDb, &pSCache))) { goto Exit; } pucBlk = pSCache->pucBlk; if (bFirstChainFlag) { pucLogHdr [LOG_PF_FIRST_BC_CNT]--; } UD2FBA( uiNextBlkAddr, &pucBlk [BH_NEXT_BLK]); ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; goto Exit; } /**-------------------------------- *** CHAIN BLOCK *** Only in minor verion > 0 ***-------------------------------*/ /**-------------------------------- *** CHAIN BLOCK - previous block *** is nbc (next backchain) ***-------------------------------*/ if (uiPrevBlkAddr == uiNbcAddr) { if ( RC_BAD( rc = ScaGetBlock( pDb, NULL, BHT_FREE, uiPrevBlkAddr, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; if (RC_BAD( rc = ScaLogPhysBlk( pDb, &pSCache))) { goto Exit; } pucBlk = pSCache->pucBlk; ALPutPBC( pucBlk, uiPbcAddr); UD2FBA( uiNextBlkAddr, &pucBlk [BH_NEXT_BLK]); ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; if (uiPbcAddr != BT_END) { if (RC_BAD( rc = ScaGetBlock( pDb, NULL, BHT_FREE, uiPbcAddr, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; if (RC_BAD( rc = ScaLogPhysBlk( pDb, &pSCache))) { goto Exit; } pucBlk = pSCache->pucBlk; ALPutNBC( pucBlk, uiNbcAddr); ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; } } /**-------------------------------- *** CHAIN BLOCK - next block *** is pbc (previous backchain) *** *** |----------->>-PBC->>---------| *** | | *** header ----> FREE -->>-nextBlk->>--------> FREE *** | | *** |---------<<-NBC-<<-----------| ***-------------------------------*/ else if ((uiNextBlkAddr == uiPbcAddr) && (uiNextBlkAddr != BT_END)) { if (RC_BAD( rc = ScaGetBlock( pDb, NULL, BHT_FREE, uiPrevBlkAddr, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; if (RC_BAD( rc = ScaLogPhysBlk( pDb, &pSCache))) { goto Exit; } pucBlk = pSCache->pucBlk; UD2FBA( uiNextBlkAddr, &pucBlk [BH_NEXT_BLK]); ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; if (uiPbcAddr != BT_END) { if ( RC_BAD( rc = ScaGetBlock( pDb, NULL, BHT_FREE, uiNextBlkAddr, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; if (RC_BAD( rc = ScaLogPhysBlk( pDb, &pSCache))) { goto Exit; } pucBlk = pSCache->pucBlk; ALPutNBC( pucBlk, uiNbcAddr); ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; } if (uiNbcAddr == BT_END) { UD2FBA( (FLMUINT32)uiPbcAddr, &pucLogHdr [LOG_PF_FIRST_BACKCHAIN]); } else { if (RC_BAD( rc = ScaGetBlock( pDb, NULL, BHT_FREE, uiNbcAddr, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; if (RC_BAD( rc = ScaLogPhysBlk( pDb, &pSCache))) { goto Exit; } pucBlk = pSCache->pucBlk; ALPutPBC( pucBlk, uiPbcAddr); ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; } } /**----------------------------------- *** CHAIN BLOCK - normal chain block *** No adjacent chain blocks *** May be the last block in the chain ***----------------------------------*/ else { if (RC_BAD( rc = ScaGetBlock( pDb, NULL, BHT_FREE, uiPrevBlkAddr, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; if (RC_BAD( rc = ScaLogPhysBlk( pDb, &pSCache))) { goto Exit; } pucBlk = pSCache->pucBlk; ALPutNBC( pucBlk, uiNbcAddr); ALPutPBC( pucBlk, uiPbcAddr); UD2FBA( uiNextBlkAddr, &pucBlk [BH_NEXT_BLK]); ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; if (uiPbcAddr != BT_END) { if (RC_BAD( rc = ScaGetBlock( pDb, NULL, BHT_FREE, uiPbcAddr, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; if (RC_BAD( rc = ScaLogPhysBlk( pDb, &pSCache))) { goto Exit; } pucBlk = pSCache->pucBlk; ALPutNBC( pucBlk, uiPrevBlkAddr); ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; } if (uiNbcAddr == BT_END) { UD2FBA( (FLMUINT32)uiPrevBlkAddr, &pucLogHdr [LOG_PF_FIRST_BACKCHAIN]); if (bFirstChainFlag) { pucLogHdr [LOG_PF_FIRST_BC_CNT]--; } } else { if (RC_BAD( rc = ScaGetBlock( pDb, NULL, BHT_FREE, uiNbcAddr, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; if (RC_BAD( rc = ScaLogPhysBlk( pDb, &pSCache))) { goto Exit; } pucBlk = pSCache->pucBlk; ALPutPBC( pucBlk, uiPrevBlkAddr); ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; } } Exit: if (bReleaseCache) { ScaReleaseCache( pSCache, FALSE); } return( rc); } /**************************************************************************** Desc: Move an avail block out of the avail block list. Worry about version 1.11+ back chaining ****************************************************************************/ FSTATIC RCODE FLRFindPrevAvailBlk( FDB_p pDb, FLMUINT * puiBlkAddrRV, FLMBOOL * pbFirstChainFlagRV) { RCODE rc = FERR_OK; FLMBYTE ucBlkHeader [BH_OVHD ]; FLMUINT uiTargetBlkAddr = *puiBlkAddrRV; FLMUINT uiNextBlkAddr; FLMUINT uiNbcAddr; FLMUINT uiTempBlkAddr = 0; *pbFirstChainFlagRV = FALSE; if( RC_BAD( rc = FLRReadBlkHdr( pDb, uiTargetBlkAddr, ucBlkHeader, (FLMINT *)0 ))) goto Exit; uiNextBlkAddr = FB2UD( &ucBlkHeader [BH_NEXT_BLK]); if ( uiNextBlkAddr == pDb->LogHdr.uiFirstAvailBlkAddr) { /* Done */ goto Exit; } /* Find next chain block */ uiNbcAddr = ALGetNBC( ucBlkHeader); while( ( !uiNbcAddr) && ( uiNextBlkAddr != BT_END)) { if( RC_BAD( rc = FLRReadBlkHdr( pDb, uiNextBlkAddr, ucBlkHeader, (FLMINT *)0))) goto Exit; if( ( uiTempBlkAddr = GET_BH_ADDR( ucBlkHeader)) != uiNextBlkAddr) { rc = RC_SET( FERR_DATA_ERROR); goto Exit; } uiNbcAddr = ALGetNBC( ucBlkHeader); uiNextBlkAddr = FB2UD( &ucBlkHeader [BH_NEXT_BLK]); } /* Now find the previous avail block */ if( uiNbcAddr == BT_END) { uiNextBlkAddr = pDb->LogHdr.uiFirstAvailBlkAddr; *pbFirstChainFlagRV = TRUE; /* Within first chain */ } else uiNextBlkAddr = uiNbcAddr; while( ( uiNextBlkAddr != uiTargetBlkAddr) && ( uiNextBlkAddr != BT_END)) { if (RC_BAD( rc = FLRReadBlkHdr( pDb, uiNextBlkAddr, ucBlkHeader, (FLMINT *)0 ))) goto Exit; if( ( uiTempBlkAddr = GET_BH_ADDR( ucBlkHeader)) != uiNextBlkAddr) { rc = RC_SET( FERR_DATA_ERROR); goto Exit; } uiNextBlkAddr = FB2UD( &ucBlkHeader [BH_NEXT_BLK]); } *puiBlkAddrRV = uiTempBlkAddr; Exit: return( rc ); }