798 lines
21 KiB
C++
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;
|
|
}
|