Files
mars-flaim/flaim/src/fsdelelm.cpp

798 lines
21 KiB
C++

//-------------------------------------------------------------------------
// Desc: Delete element from b-tree block.
// Tabs: 3
//
// Copyright (c) 1991-2001,2003-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: fsdelelm.cpp 12284 2006-01-19 14:54:14 -0700 (Thu, 19 Jan 2006) dsanders $
//-------------------------------------------------------------------------
#include "flaimsys.h"
/***************************************************************************
Desc: Delete the current element from a b-tree block and write block.
The stack must point to the next element after the deleted element.
The next element could be in a different block so be careful.
Notes: The order of the high level if's is VERY important.
The order of changing the blocks is not important because of logging.
*****************************************************************************/
RCODE FSBtDelete(
FDB * pDb,
LFILE * pLFile,
BTSK ** pStackRV)
{
RCODE rc;
BTSK * pStack = *pStackRV;
SCACHE * pTmpSCache;
FLMBYTE * pBlk;
FLMBOOL bReleaseTmpCache = FALSE;
FLMUINT uiNextBlk;
FLMUINT uiPrevBlk;
FLMUINT uiLastElm; // Offset of last element in a block
FLMUINT uiElmOvhd = pStack->uiElmOvhd;
FLMBYTE pLEMBuffer[12]; // Last element marker buffer
// Log block before modifying it
if (RC_BAD( rc = FSLogPhysBlk( pDb, pStack)))
{
goto Exit;
}
// If this is a non-leaf positioning index, update parent counts.
if (pLFile->pIxd && (pLFile->pIxd->uiFlags & IXD_POSITIONING))
{
if (pStack->uiLevel)
{
FLMUINT uiRefCount;
FLMBYTE* pElm = pStack->pBlk + pStack->uiCurElm;
// Reduce the counts from the parent up.
uiRefCount = FB2UD( &pElm[BNE_CHILD_COUNT]);
if (RC_BAD( rc = FSChangeBlkCounts( pDb, pStack,
(FLMINT) (0 - uiRefCount))))
{
goto Exit;
}
}
}
if (RC_BAD( rc = FSBlkDelElm( pStack)))
{
goto Exit;
}
// Take care of deletion of the ONLY element in the block. There is NO
// WAY this could be a root or right end block because of the last
// element marker (LEM) takes up 2 or 4 bytes
if (pStack->uiBlkEnd == BH_OVHD)
{
// Free up this empty block and fixup the NEXT/PREV links.
uiNextBlk = FB2UD( &pStack->pBlk[BH_NEXT_BLK]);
rc = FSBlockFixLinks( pDb, pLFile, pStack->pSCache);
pStack->pSCache = NULL;
pStack->pBlk = NULL;
if (RC_BAD( rc))
{
goto Exit;
}
// Remove the element that pointed to this block
if (RC_OK( rc = FSDelParentElm( pDb, pLFile, &pStack)))
{
pStack->uiBlkAddr = uiNextBlk;
// Read the next block and set stack to point to next element
if (RC_OK( rc = FSGetBlock( pDb, pLFile, uiNextBlk, pStack)))
{
pStack->uiCurElm = BH_OVHD;
FSBlkBuildPKC( pStack, pStack->pKeyBuf, FSBBPKC_AT_CURELM);
}
}
*pStackRV = pStack;
}
// Deleting a RIGHT MOST B-tree block (along the right side). Check if
// only the LEM (last element marker) is left in the block. A block
// CANNOT contain just a LEM. Move the LEM and free the block. There are
// 3 cases to watch for:
//
// EMPTY B-TREE - free the root/block save next record in lfArea
// EMPTY ROOT - free root and goto child and make that root
// EMPTY BLOCK - cannot contain only a LEM - move to previous block
//
// Must not move last DRN Marker - there is no need to do this.
else if ((FB2UD( &pStack->pBlk[BH_NEXT_BLK]) == BT_END) &&
((pStack->uiBlkEnd == BH_OVHD + uiElmOvhd)))
{
// EMPTY B-TREE Level is not in the current LFILE version so check
// if the root block is the same as this block.
if ((pStack->uiBlkType == BHT_LEAF) &&
(pLFile->uiRootBlk == pStack->uiBlkAddr)) // Root is leaf
{
// Return the empty root block to the system
// Code supports empty b-trees. We have gone back and forth on
// returning empty root blocks to the system. Setup stack for
// emtpy state and modify the LFILE on disk. The data record
// b-tree can NEVER be empty because of the next record DRN
// record should always hang around. ALL CALLING ROUTINES MUST
// CHECK for STACK->uiBlkAddr == BT_END.
pStack->uiBlkAddr = BT_END;
{
// Get the next DRN and save in the LFD
FLMBYTE * ptr = &pStack->pBlk[BH_OVHD];
ptr += BBE_GETR_KL( ptr) + BBE_KEY;
pLFile->uiNextDrn = (FLMUINT) FB2UD( ptr);
}
rc = FSBlockFree( pDb, pStack->pSCache);
pStack->pSCache = NULL;
pStack->pBlk = NULL;
if (RC_BAD( rc))
{
goto Exit;
}
pLFile->uiRootBlk = BT_END;
rc = flmLFileWrite( pDb, pLFile);
}
// EMPTY ROOT BLOCK Remove root block and set new root block to
// child.
else if (pLFile->uiRootBlk == pStack->uiBlkAddr)
{
// Obtain child block and set to lfArea to assign new root block
uiNextBlk = FSChildBlkAddr( pStack);
rc = FSBlockFree( pDb, pStack->pSCache);
pStack->pSCache = NULL;
pStack->pBlk = NULL;
if (RC_BAD( rc))
{
goto Exit;
}
pLFile->uiRootBlk = uiNextBlk;
if (RC_BAD( rc = flmLFileWrite( pDb, pLFile)))
{
goto Exit;
}
// Assign the new root block
if (RC_BAD( rc = ScaGetBlock( pDb, pLFile, BHT_LEAF, uiNextBlk, NULL,
&pTmpSCache)))
{
goto Exit;
}
bReleaseTmpCache = TRUE;
if (RC_BAD( rc = ScaLogPhysBlk( pDb, &pTmpSCache)))
{
goto Exit;
}
// Set the root block flag on this block & mark dirty
BH_SET_ROOT_BLK( pTmpSCache->pucBlk);
ScaReleaseCache( pTmpSCache, FALSE);
bReleaseTmpCache = FALSE;
// It is possible to add an element so that a new root block
// gets created AND THEN Deleted. Because of this
// the ROOT block in the stack MUST always be pStack[0]. The
// three lines below do this.
shiftN( (FLMBYTE*) pStack + sizeof(BTSK),
sizeof(BTSK) * pStack->uiLevel, // think uiLevel + 1 - 1
0 - (sizeof(BTSK)));
// We don't want a pointer to the same shared cache in two
// different elements of the pStack. NULL out the one that was
// moved down.
(pStack + pStack->uiLevel + 1)->pSCache = NULL;
(pStack + pStack->uiLevel + 1)->pBlk = NULL;
pStack--;
*pStackRV = pStack;
// Don't worry about positioning to the next element - root gone
}
else
{
// ONLY LAST ELEMENT MARKER (LEM) REMAINS (leaf or (non-leaf but
// NOT ROOT)). A block must NEVER contain only the last element
// marker (LEM). This may free up a leaf block or a non-leaf
// block but never a root block (root checks above.) This is the
// most complex case. Move the last element marker (LEM) to the
// end of the previous block. Be carefull to delete the correct
// element in the parent block. The algorithm states that there
// MUST ALWAYS be room to insert the LEM into a block without
// splitting that block. Because of logging the block
// modifications do not need to be flushed in a specific order to
// prevent corruption.
uiPrevBlk = FB2UD( &pStack->pBlk[BH_PREV_BLK]);
// Save the last element marker, may be a non-leaf element
f_memcpy( pLEMBuffer, &pStack->pBlk[BH_OVHD], uiElmOvhd);
// Free the block
rc = FSBlockFree( pDb, pStack->pSCache);
pStack->pSCache = NULL;
pStack->pBlk = NULL;
if (RC_BAD( rc))
{
goto Exit;
}
// Pop stack - point to parent
pStack--;
// Modify the parent blocks last element marker (LEM) to point to
// the new last block. THEN delete the previous element that also
// points to the new last block. Read the parent block.
if (RC_BAD( rc = FSGetBlock( pDb, pLFile, pStack->uiBlkAddr, pStack)))
{
goto Exit;
}
if (RC_BAD( rc = FSLogPhysBlk( pDb, pStack)))
{
goto Exit;
}
// Change where element points to in the last element marker (LEM)
FSSetChildBlkAddr( BLK_ELM_ADDR( pStack, pStack->uiCurElm), uiPrevBlk,
pStack->uiElmOvhd);
// FSBtPrevElm may return -1 which is NOT OK
if (RC_BAD( rc = FSBtPrevElm( pDb, pLFile, pStack)))
{
rc = (rc == FERR_BT_END_OF_DATA) ? RC_SET( FERR_BTREE_ERROR) : rc;
goto Exit;
}
// Delete the element that points to the new last block
if (RC_BAD( rc = FSBtDelete( pDb, pLFile, &pStack)))
{
goto Exit;
}
pStack++;
// Read in the new last block Move in the old LEM to the end of
// the block Position pStack to LEM
if (RC_BAD( rc = FSGetBlock( pDb, pLFile, uiPrevBlk, pStack)))
{
goto Exit;
}
// Log block before modifying
if (RC_BAD( rc = FSLogPhysBlk( pDb, pStack)))
{
goto Exit;
}
pBlk = pStack->pBlk;
// Set the pStack elements
uiLastElm = pStack->uiBlkEnd;
pStack->uiCurElm = uiLastElm;
// Place the LEM (last element marker) in the block
f_memcpy( &pBlk[uiLastElm], pLEMBuffer, uiElmOvhd);
// Set next block pointer to BT_END
UD2FBA( BT_END, &pBlk[BH_NEXT_BLK]);
// Adjust end pointers
pStack->uiBlkEnd = uiLastElm + uiElmOvhd;
UW2FBA( (FLMUINT16) pStack->uiBlkEnd, &pBlk[BH_BLK_END]);
*pStackRV = pStack;
}
}
// Deleted the LAST (but not only) element in the block. The parent
// block must have its current element changed to reflect the new last
// element key in this block. The algoritm MUST insert the next last
// element key and THEN delete the old key or you may have a new root
// block which is your next block! Comentary: Some b-tree algorithms
// have key markers that are shorter in the non-leaf blocks. This
// right-end non-leaf trunctaion is a very good idea, but ALL of the
// b-tree code has to conform to this rule and it is not a trivial
// thing to support.
else if (pStack->uiBlkEnd == pStack->uiCurElm)
{
pBlk = pStack->pBlk;
// Double check in case of corrupt this is not a right most block
if (FB2UD( &pBlk[BH_NEXT_BLK]) != BT_END)
{
rc = FSNewLastBlkElm( pDb, pLFile, &pStack,
FSNLBE_LESS | FSNLBE_POSITION);
*pStackRV = pStack;
}
}
// Else - normal delete - everything should be correct and the stack is
// positioned to the next element after the deleted element. The
// pKeyBuf[] however, is NOT always set to be the current elms key.
// Check to see if there is room to make a 3/2 combine and do it.
else if (pStack->uiBlkEnd <=
(FFILE_MIN_FILL * pDb->pFile->FileHdr.uiBlockSize / 100))
{
rc = FSCombineBlks( pDb, pLFile, &pStack);
*pStackRV = pStack; // Stack may have changed
}
Exit:
if (bReleaseTmpCache)
{
ScaReleaseCache( pTmpSCache, FALSE);
}
return (rc);
}
/****************************************************************************
Desc: Delete the parent element from where you are at in the stack.
Notes: In the future we should either pin down the current block or
reread it in.
****************************************************************************/
RCODE FSDelParentElm(
FDB * pDb,
LFILE * pLFile,
BTSK ** pStackRV)
{
RCODE rc;
BTSK * pStack = *pStackRV;
pStack--;
if (RC_BAD( rc = FSGetBlock( pDb, pLFile, pStack->uiBlkAddr, pStack)))
{
goto Exit;
}
// Ignore status value from FSBtScanTo - just position for delete
if (RC_BAD( rc = FSBtScanTo( pStack, NULL, 0, (FLMUINT) 0)))
{
goto Exit;
}
// Call will position to the next element which pts to the next blk
rc = FSBtDelete( pDb, pLFile, &pStack);
Exit:
pStack++; // Go down the pStack to the original level
*pStackRV = pStack;
return (rc);
}
/****************************************************************************
Desc: There is a new last block element, store key in parent and
remove current element if( uiFlags & FSNLBE_LESS or GREATER is set
Notes: Take note of the uiFlags - they can change what is done
****************************************************************************/
RCODE FSNewLastBlkElm(
FDB * pDb,
LFILE * pLFile,
BTSK ** pStackRV,
FLMUINT uiFlags) // FSNLBE_GREATER, *_LESS, CK_COMBINE, POSITION or 0
{
RCODE rc;
BTSK* pStack = *pStackRV;
FLMBYTE * pBlk = pStack->pBlk;
FLMBYTE * pCurElm; // Points to the current last element
FLMUINT uiOldCurElm = pStack->uiCurElm;
FLMUINT uiNextBlk = 0;
FLMUINT uiDomain = 0; // Domain is index reference range
FLMUINT uiElmLen; // Element length
FLMUINT uiKeyLen;
FLMUINT uiNewElmOvhd;
FLMUINT uiRefCount;
FLMBYTE * pKey;
FLMBYTE pElmBuffer[MAX_KEY_SIZ + BNE_KEY_COUNTS_START + BNE_DOMAIN_LEN];
uiNewElmOvhd = (pStack - 1)->uiElmOvhd;
uiElmLen = uiNewElmOvhd;
if (uiNewElmOvhd == BNE_DATA_OVHD)
{
pKey = pElmBuffer;
}
else
{
pElmBuffer[BBE_PKC] = 0; // Set PKC, DOMAIN to zero
pElmBuffer[BBE_KL] = 0;
pKey = pElmBuffer + uiNewElmOvhd;
// Only update the counts if the inserting a key and not replacing.
if (uiNewElmOvhd == BNE_KEY_COUNTS_START)
{
if (RC_BAD( rc = FSBlockCounts( pStack, BH_OVHD, pStack->uiBlkEnd,
NULL, NULL, &uiRefCount)))
{
goto Exit;
}
UD2FBA( uiRefCount, &pElmBuffer[BNE_CHILD_COUNT]);
}
}
// Build the pElmBuffer[] with the new last key in the block
FSSetChildBlkAddr( pElmBuffer, pStack->uiBlkAddr, uiNewElmOvhd);
if ((uiNextBlk = FB2UD( &pBlk[BH_NEXT_BLK])) == BT_END)
{
if (uiNewElmOvhd == BNE_DATA_OVHD)
{
UD2FBA( 0xFFFFFFFF, pElmBuffer);
}
uiKeyLen = 0;
uiElmLen = uiNewElmOvhd;
goto no_domain;
}
// else - fall through
pStack->uiCurElm = pStack->uiBlkEnd; // Position past last element
FSBtPrevElm( pDb, pLFile, pStack); // Build full key in pKeyBuf[]
uiKeyLen = pStack->uiKeyLen;
// Copy the key
if (uiNewElmOvhd == BNE_DATA_OVHD)
{
flmCopyDrnKey( pElmBuffer, pStack->pKeyBuf);
}
else if (uiKeyLen)
{
f_memcpy( &pElmBuffer[uiNewElmOvhd], pStack->pKeyBuf, uiKeyLen);
BBE_SET_KL( pElmBuffer, uiKeyLen);
uiElmLen += uiKeyLen;
}
// If this is a boundqed index reference set then store DOMAIN
pCurElm = CURRENT_ELM( pStack);
uiDomain = (pLFile->uiLfType == LF_INDEX)
? FSGetDomain( &pCurElm, pStack->uiElmOvhd)
: (FLMUINT) 0;
if (uiDomain)
{
BNE_SET_DOMAIN( pElmBuffer);
pElmBuffer[uiElmLen++] = (FLMBYTE) (uiDomain >> 16);
pElmBuffer[uiElmLen++] = (FLMBYTE) (uiDomain >> 8);
pElmBuffer[uiElmLen++] = (FLMBYTE) (uiDomain & 0xFF);
}
no_domain:
// Go to the parent block, insert new element and delete the old last
// element in the block. Pinning the current block is not suggested
// because you could pin number of (levels - 1) blocks.
pStack--;
if (RC_BAD( rc = FSGetBlock( pDb, pLFile, pStack->uiBlkAddr, pStack)))
{
goto Exit;
}
// If greater you should be positioned AFTER the matching element
if (pStack->uiBlkEnd > BH_OVHD) // Don't call if NO elements
{
// Set up the pStack elements for insert - passing keyLen sets up
// pStack
if (RC_BAD( rc = FSBtScanTo( pStack, pKey, uiKeyLen, uiDomain)))
{
goto Exit;
}
}
else
{
pStack->uiPrevElmPKC = pStack->uiPKC = 0;
}
// Insert the element into the parent block - watch for splits!
if (RC_OK( rc = FSBtInsert( pDb, pLFile, &pStack, pElmBuffer, uiElmLen)))
{
if (uiFlags & FSNLBE_LESS)
{
// Go to the next element, may read a new block!
if (RC_OK( rc = FSBtNextElm( pDb, pLFile, pStack)))
{
if (RC_OK( rc = FSBtDelete( pDb, pLFile, &pStack)))
{
// Now position to the current element - back one
if (!(uiFlags & FSNLBE_POSITION))
{
rc = FSBtPrevElm( pDb, pLFile, pStack);
rc = (rc == FERR_BT_END_OF_DATA) ? FERR_OK : rc;
}
}
else
{
rc = (rc == FERR_BT_END_OF_DATA) ? FERR_OK : rc;
}
}
}
else if (uiFlags & FSNLBE_GREATER)
{
if (RC_OK( rc = FSBtPrevElm( pDb, pLFile, pStack)))
{
if (RC_OK( rc = FSBtDelete( pDb, pLFile, &pStack)))
{
// Position to the next element if flag is set
if (uiFlags & FSNLBE_POSITION)
{
rc = FSBtNextElm( pDb, pLFile, pStack);
rc = (rc == FERR_BT_END_OF_DATA) ? FERR_OK : rc;
}
}
}
}
}
Exit:
// Pop the pStack and position to next element in the block that you
// expect to be positioned to. You should be positioned to the correct
// parent element.
pStack++;
*pStackRV = pStack; // Update caller's pStack
if (RC_OK( rc))
{
if ((uiFlags & FSNLBE_POSITION) && (uiNextBlk != BT_END))
{
pStack->uiBlkAddr = uiNextBlk;
uiOldCurElm = BH_OVHD;
}
// Read the next block and set pStack.
if (RC_OK( rc = FSGetBlock( pDb, pLFile, pStack->uiBlkAddr, pStack)))
{
pStack->uiCurElm = uiOldCurElm; // Restore original curElm value
FSBlkBuildPKC( pStack, pStack->pKeyBuf, FSBBPKC_AT_CURELM);
}
}
return (rc);
}
/***************************************************************************
Desc: Delete the current element from a b-tree block without writing block.
The pStack must point to the next element after the deleted element.
Notes: Code handles deletion of an element at any level of the b-tree.
*****************************************************************************/
RCODE FSBlkDelElm(
BTSK * pStack) // Stack of variables for each level
{
RCODE rc;
FLMBYTE* pDelElm; // Points to deleted element
FLMBYTE* pCurElm; // Points to current elm to move down
FLMBYTE* pBlk = pStack->pBlk; // Points to block for speed
FLMUINT uiCurElmOfs; // Current (next) element's offset
FLMUINT uiDelElmPkc; // # of carry bytes for deleted elm
FLMUINT uiCurElmPkc; // Current element's Prev key count
FLMUINT uiCurElmKeyLen; // Current element's key length
FLMUINT uiOldCurElm = pStack->uiCurElm;
FLMUINT uiElmOvhd = pStack->uiElmOvhd;
FLMINT iDelElmSize; // Deleted element's size
FLMINT iPkcLost; // Number bytes to expand for next elm
pDelElm = &pBlk[pStack->uiCurElm];
if (RC_OK( rc = FSBlkNextElm( pStack)))
{
// Setup to remove what pDelElm is pointing to. This is NOT the last
// element in the block so move down the rest of the block data.
uiCurElmOfs = pStack->uiCurElm;
iDelElmSize = (FLMINT) (uiCurElmOfs - uiOldCurElm);
pCurElm = &pBlk[uiCurElmOfs];
if (pStack->uiBlkType != BHT_NON_LEAF_DATA)
{
uiDelElmPkc = (FLMUINT) (BBE_GET_PKC( pDelElm));
uiCurElmPkc = (FLMUINT) (BBE_GET_PKC( pCurElm));
// If current element uses bytes from the deleted element...
if (uiCurElmPkc > uiDelElmPkc)
{
iPkcLost = (FLMINT) (uiCurElmPkc - uiDelElmPkc);
// Create the new deleted element and setup for the shiftN()
// below moving pCurElm.
uiCurElmPkc -= iPkcLost;
uiCurElmKeyLen = (FLMUINT) (BBE_GET_KL( pCurElm) + iPkcLost);
BBE_SET_PKC( pDelElm, uiCurElmPkc); // Clears all bits
BBE_SET_KL( pDelElm, uiCurElmKeyLen);
*pDelElm |= (FLMBYTE) (BBE_IS_FIRST_LAST( pCurElm));
if (pStack->uiBlkType == BHT_LEAF)
{
BBE_SET_RL( pDelElm, BBE_GET_RL( pCurElm));
}
else
{
f_memcpy( pDelElm + BNE_CHILD_BLOCK, pCurElm + BNE_CHILD_BLOCK,
uiElmOvhd - BNE_CHILD_BLOCK);
}
// Adjust iDelElmSize and uiCurElmOfs to delete current element
// overhead
iDelElmSize -= iPkcLost;
uiCurElmOfs += uiElmOvhd;
// Move any extra bytes from the deleted element refered in
// curElm
f_memcpy( pDelElm + uiElmOvhd, &pStack->pKeyBuf[uiDelElmPkc],
iPkcLost);
// Fall through and copy the rest of the block down
}
}
// Shift down - starting at pCurElm
shiftN( &pBlk[uiCurElmOfs], pStack->uiBlkEnd - uiCurElmOfs,
(FLMINT) (0 - iDelElmSize));
pStack->uiBlkEnd -= iDelElmSize;
}
else if (rc == FERR_BT_END_OF_DATA)
{
rc = FERR_OK;
if (pStack->uiCurElm == pStack->uiBlkEnd)
{
// Deleted last element in blk
pStack->uiBlkEnd = uiOldCurElm;
}
else
{
// Need to move the last element marker (LEM) down;
// This should only be used on leaf blocks
pBlk[uiOldCurElm] = BBE_FIRST_FLAG | BBE_LAST_FLAG;
pBlk[uiOldCurElm + BBE_KL] = pBlk[uiOldCurElm + BBE_RL] = 0;
pStack->uiBlkEnd = uiOldCurElm + uiElmOvhd;
}
}
else
{
goto Exit;
}
// Modify the element end offset & restore curElm & prevElm on pStack
UW2FBA( (FLMUINT16) pStack->uiBlkEnd, &pBlk[BH_BLK_END]);
pStack->uiCurElm = uiOldCurElm;
Exit:
return (rc);
}
/****************************************************************************
Desc: Sets the parent element's child block value
****************************************************************************/
void FSSetChildBlkAddr(
FLMBYTE * pElement,
FLMUINT uiBlkAddr,
FLMUINT uiBlkOvhd)
{
FLMBYTE * pChildAddr;
if (uiBlkOvhd == BNE_KEY_START || uiBlkOvhd == BNE_KEY_COUNTS_START)
{
pChildAddr = pElement + BNE_CHILD_BLOCK;
UD2FBA( uiBlkAddr, pChildAddr);
}
else if (uiBlkOvhd == BNE_DATA_OVHD)
{
pChildAddr = pElement + BNE_DATA_CHILD_BLOCK;
UD2FBA( uiBlkAddr, pChildAddr);
}
return;
}