git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@7 0109f412-320b-0410-ab79-c3e0c5ffbbe6
738 lines
22 KiB
C++
738 lines
22 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_p * pStackRV
|
|
)
|
|
{
|
|
RCODE rc;
|
|
BTSK_p 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;
|
|
|
|
/**------------------------------------------------------------
|
|
*** Added 07/15/93
|
|
*** Need to reset keybuf[] for deletes in a while loop like the
|
|
*** data record delete code in fsrecupd.c.
|
|
*** This may be cause of some corruptions while deleting
|
|
*** lots of records.
|
|
***-----------------------------------------------------------*/
|
|
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 & 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 & make that root
|
|
*** EMPTY BLOCK - cannot contain only a LEM - move to previous block
|
|
***
|
|
*** Must not move LASST DRN Marker - there is no need to do this.
|
|
***
|
|
**/
|
|
else if( (FB2UD( &pStack->pBlk[ BH_NEXT_BLK ]) == BT_END)
|
|
&& ( (pStack->uiBlkEnd == BH_OVHD + uiElmOvhd )
|
|
// || ( (pLFile->uiLfType == LF_CONTAINER)
|
|
// && (stack->uiBlkEnd == BH_OVHD + DRN_LAST_MARKER_LEN+ uiElmOvhd)
|
|
// && (stack->uiBlkType == BHT_LEAF)
|
|
) ) //)
|
|
{
|
|
/**
|
|
*** 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 (11/26/91) 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;
|
|
|
|
/**
|
|
*** Date Added: 8/19/93
|
|
*** It is possible to add an element so that a new root block
|
|
*** gets created AND THEN Deleted. I saw this on OS2 when they
|
|
*** got up to 6 levels in the btree. Because of this the ROOT
|
|
*** block in the stack MUST always be pStack[0].
|
|
*** The three lines below do this. Tested on Brad Daw's db.
|
|
**/
|
|
|
|
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--; /* Point 1 element BEFORE stack starts ! */
|
|
*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 & 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_p * pStackRV
|
|
)
|
|
{
|
|
RCODE rc;
|
|
BTSK_p pStack = *pStackRV; /* Stack pointer */
|
|
|
|
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;
|
|
|
|
/* FSBlkBuildPKC( pStack, pStack->pKeyBuf );
|
|
but does not build key from current element Much faster than scanTo() */
|
|
|
|
/* 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_p * pStackRV,
|
|
FLMUINT uiFlags) // FSNLBE_GREATER, *_LESS, CK_COMBINE, POSITION or 0
|
|
{
|
|
RCODE rc;
|
|
BTSK_p 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 callers 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_p 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; /* Element overhead for block */
|
|
FLMINT iDelElmSize; /* Deleted element's size - signed !!*/
|
|
FLMINT iPkcLost; /* Number bytes to expand for next elm*/
|
|
/* MUST be a signed word value */
|
|
|
|
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 & 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 - suggest using shiftN() */
|
|
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;
|
|
}
|