git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@7 0109f412-320b-0410-ab79-c3e0c5ffbbe6
1401 lines
30 KiB
C++
1401 lines
30 KiB
C++
//------------------------------------------------------------------------------
|
|
// Desc: Contains specific q-sort code to sort FLAIM's KREF structures.
|
|
//
|
|
// Tabs: 3
|
|
//
|
|
// Copyright (c) 1990-2000, 2002-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: kyqsort.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "flaimsys.h"
|
|
|
|
#define KY_SWAP( pKrefTbl, leftP, rightP) \
|
|
pTempKref = pKrefTbl [leftP]; \
|
|
pKrefTbl [leftP] = pKrefTbl [rightP]; \
|
|
pKrefTbl [rightP] = pTempKref
|
|
|
|
FSTATIC RCODE ixKeyGetNodeId(
|
|
IXD * pIxd,
|
|
const FLMBYTE * pucKey,
|
|
const FLMBYTE * pucKeyEnd,
|
|
FLMUINT uiKeyComponent,
|
|
FLMUINT64 * pui64NodeId);
|
|
|
|
FSTATIC RCODE ixKeyCompareUnicode(
|
|
ICD * pIcd,
|
|
FLMUINT uiLanguage,
|
|
FLMUNICODE * puzStr1,
|
|
FLMUINT uiByteLen1,
|
|
FLMUNICODE * puzStr2,
|
|
FLMUINT uiByteLen2,
|
|
FLMINT * piCompare);
|
|
|
|
FSTATIC RCODE ixKeyGetUnicode(
|
|
F_Db * pDb,
|
|
ICD * pIcd,
|
|
FLMUINT uiCollection,
|
|
FLMUINT64 ui64NodeId,
|
|
FLMUINT uiKeyComponent,
|
|
F_OldNodeList * pOldNodeList,
|
|
F_DataVector * pSearchKey,
|
|
F_DynaBuf * pDynaBuf);
|
|
|
|
FSTATIC RCODE ixKeyGetBinary(
|
|
F_Db * pDb,
|
|
ICD * pIcd,
|
|
FLMUINT uiCollection,
|
|
FLMUINT64 ui64NodeId,
|
|
FLMUINT uiKeyComponent,
|
|
F_OldNodeList * pOldNodeList,
|
|
F_DataVector * pSearchKey,
|
|
F_DynaBuf * pDynaBuf);
|
|
|
|
FSTATIC RCODE krefQuickSort(
|
|
F_Db * pDb,
|
|
IXD * pIxd,
|
|
KREF_ENTRY ** pEntryTbl,
|
|
FLMUINT uiLowerBounds,
|
|
FLMUINT uiUpperBounds);
|
|
|
|
FSTATIC RCODE krefKillDups(
|
|
F_Db * pDb,
|
|
IXD * pIxd,
|
|
KREF_ENTRY ** pKrefTbl,
|
|
FLMUINT * puiKrefTotal);
|
|
|
|
/***************************************************************************
|
|
Desc: Compares result set entries during the finalization stage to allow
|
|
the result set to be sorted and to remove duplicates.
|
|
*****************************************************************************/
|
|
FSTATIC RCODE ixKeyGetNodeId(
|
|
IXD * pIxd,
|
|
const FLMBYTE * pucKey,
|
|
const FLMBYTE * pucKeyEnd,
|
|
FLMUINT uiKeyComponent,
|
|
FLMUINT64 * pui64NodeId)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
FLMUINT uiComponent;
|
|
FLMUINT uiComponentLen;
|
|
|
|
// Skip past all of the remaining key components so we can get to the
|
|
// node ID list. We are currently positioned on the key component
|
|
// specified in uiKeyComponent. NOTE: uiKeyComponent is zero-based,
|
|
// 0=1st component, 1=2nd component, etc.
|
|
|
|
uiComponent = uiKeyComponent;
|
|
while (pucKey < pucKeyEnd && uiComponent < pIxd->uiNumKeyComponents)
|
|
{
|
|
uiComponentLen = getKeyComponentLength( pucKey);
|
|
if (uiComponentLen != KEY_HIGH_VALUE && uiComponentLen != KEY_LOW_VALUE)
|
|
{
|
|
pucKey += (uiComponentLen + 2);
|
|
}
|
|
else
|
|
{
|
|
pucKey += 2;
|
|
}
|
|
uiComponent++;
|
|
}
|
|
|
|
// See if there are node IDs in the key. A 0xFF could be present if
|
|
// we have set a "high" node ID.
|
|
|
|
if (pucKey >= pucKeyEnd || *pucKey == 0xFF)
|
|
{
|
|
*pui64NodeId = 0;
|
|
goto Exit;
|
|
}
|
|
|
|
// At this point, we better have all of the node ids, including
|
|
// document ID.
|
|
|
|
// Skip past the document ID
|
|
|
|
if (RC_BAD( rc = flmDecodeSEN64( &pucKey, pucKeyEnd, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (pucKey >= pucKeyEnd)
|
|
{
|
|
*pui64NodeId = 0;
|
|
goto Exit;
|
|
}
|
|
|
|
// Skip all of the node ids up to the one we want
|
|
|
|
for (uiComponent = 0; uiComponent < uiKeyComponent; uiComponent++)
|
|
{
|
|
// Skip the component node ID - passing a NULL for the last
|
|
// parameter is cheaper than getting it out.
|
|
|
|
if (RC_BAD( rc = flmDecodeSEN64( &pucKey, pucKeyEnd, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (pucKey >= pucKeyEnd)
|
|
{
|
|
*pui64NodeId = 0;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Should now be positioned on the one we want, extract it.
|
|
|
|
if (RC_BAD( rc = flmDecodeSEN64( &pucKey, pucKeyEnd, pui64NodeId)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: Compares result set entries during the finalization stage to allow
|
|
the result set to be sorted and to remove duplicates.
|
|
*****************************************************************************/
|
|
FSTATIC RCODE ixKeyCompareUnicode(
|
|
ICD * pIcd,
|
|
FLMUINT uiLanguage,
|
|
FLMUNICODE * puzStr1,
|
|
FLMUINT uiByteLen1,
|
|
FLMUNICODE * puzStr2,
|
|
FLMUINT uiByteLen2,
|
|
FLMINT * piCompare)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
F_BufferIStream bufferLStream;
|
|
F_BufferIStream bufferRStream;
|
|
F_CollIStream lStream;
|
|
F_CollIStream rStream;
|
|
|
|
if (RC_BAD( rc = bufferLStream.open( (FLMBYTE *)puzStr1, uiByteLen1)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = bufferRStream.open( (FLMBYTE *)puzStr2, uiByteLen2)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = lStream.open( &bufferLStream, TRUE, uiLanguage,
|
|
pIcd->uiCompareRules, FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = rStream.open( &bufferRStream, TRUE, uiLanguage,
|
|
pIcd->uiCompareRules, FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = fqCompareCollStreams( &lStream, &rStream, FALSE,
|
|
uiLanguage, piCompare)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: Do binary comparison.
|
|
*****************************************************************************/
|
|
FINLINE FLMINT ixKeyCompareBinary(
|
|
const void * pvData1,
|
|
FLMUINT uiLen1,
|
|
const void * pvData2,
|
|
FLMUINT uiLen2,
|
|
FLMBOOL bSortAscending)
|
|
{
|
|
FLMINT iCompare;
|
|
|
|
if (uiLen1 > uiLen2)
|
|
{
|
|
if ((iCompare = f_memcmp( pvData1, pvData2, uiLen2)) >= 0)
|
|
{
|
|
return( bSortAscending ? 1 : -1);
|
|
}
|
|
else
|
|
{
|
|
return( bSortAscending ? -1 : 1);
|
|
}
|
|
}
|
|
else if (uiLen1 < uiLen2)
|
|
{
|
|
if ((iCompare = f_memcmp( pvData1, pvData2, uiLen1)) <= 0)
|
|
{
|
|
return( bSortAscending ? -1 : 1);
|
|
}
|
|
else
|
|
{
|
|
return( bSortAscending ? 1 : -1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((iCompare = f_memcmp( pvData1, pvData2, uiLen1)) != 0)
|
|
{
|
|
if (iCompare < 0)
|
|
{
|
|
return( bSortAscending ? -1 : 1);
|
|
}
|
|
else
|
|
{
|
|
return( bSortAscending ? 1 : -1);
|
|
}
|
|
}
|
|
}
|
|
|
|
return( 0);
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: Get the unicode value for a particular key component.
|
|
*****************************************************************************/
|
|
FSTATIC RCODE ixKeyGetUnicode(
|
|
F_Db * pDb,
|
|
ICD * pIcd,
|
|
FLMUINT uiCollection,
|
|
FLMUINT64 ui64NodeId,
|
|
FLMUINT uiKeyComponent,
|
|
F_OldNodeList * pOldNodeList,
|
|
F_DataVector * pSearchKey,
|
|
F_DynaBuf * pDynaBuf)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
IF_DOMNode * pNode = NULL;
|
|
IF_PosIStream * pIStream = NULL;
|
|
eDomNodeType eNodeType;
|
|
|
|
if (ui64NodeId)
|
|
{
|
|
eNodeType = (pIcd->uiFlags & ICD_IS_ATTRIBUTE)
|
|
? ATTRIBUTE_NODE
|
|
: ELEMENT_NODE;
|
|
|
|
// If there is an old-node list, first see if we can get the data
|
|
// from there. If it is not in there, see if we can get it from
|
|
// the database.
|
|
|
|
if (pOldNodeList)
|
|
{
|
|
FLMBYTE * pucData;
|
|
FLMUINT uiDataLen;
|
|
FLMUINT uiDummy;
|
|
void * pvBuffer;
|
|
|
|
if( pOldNodeList->findNodeInList( eNodeType, uiCollection,
|
|
ui64NodeId, pIcd->uiDictNum, &pucData, &uiDataLen, &uiDummy))
|
|
{
|
|
|
|
// Allocate the space needed.
|
|
|
|
if (RC_BAD( rc = pDynaBuf->allocSpace( uiDataLen, &pvBuffer)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_memcpy( pvBuffer, pucData, uiDataLen);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if( eNodeType == ATTRIBUTE_NODE)
|
|
{
|
|
if( RC_BAD( rc = pDb->getAttribute( uiCollection, ui64NodeId,
|
|
pIcd->uiDictNum, &pNode)))
|
|
{
|
|
flmAssert( rc != NE_XFLM_DOM_NODE_NOT_FOUND);
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NodeId, &pNode)))
|
|
{
|
|
flmAssert( rc != NE_XFLM_DOM_NODE_NOT_FOUND);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (RC_BAD( rc = pNode->getUnicode( (IF_Db *)pDb, pDynaBuf)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (RC_BAD( rc = pSearchKey->getUnicode( uiKeyComponent, pDynaBuf)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (pIStream)
|
|
{
|
|
pIStream->Release();
|
|
}
|
|
|
|
if (pNode)
|
|
{
|
|
pNode->Release();
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: Get the binary value for a particular key component.
|
|
*****************************************************************************/
|
|
FSTATIC RCODE ixKeyGetBinary(
|
|
F_Db * pDb,
|
|
ICD * pIcd,
|
|
FLMUINT uiCollection,
|
|
FLMUINT64 ui64NodeId,
|
|
FLMUINT uiKeyComponent,
|
|
F_OldNodeList * pOldNodeList,
|
|
F_DataVector * pSearchKey,
|
|
F_DynaBuf * pDynaBuf)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
IF_DOMNode * pNode = NULL;
|
|
IF_PosIStream * pIStream = NULL;
|
|
eDomNodeType eNodeType;
|
|
|
|
if (ui64NodeId)
|
|
{
|
|
eNodeType = (pIcd->uiFlags & ICD_IS_ATTRIBUTE)
|
|
? ATTRIBUTE_NODE
|
|
: ELEMENT_NODE;
|
|
|
|
// If there is an old-node list, first see if we can get the data
|
|
// from there. If it is not in there, see if we can get it from
|
|
// the database.
|
|
|
|
if (pOldNodeList)
|
|
{
|
|
FLMBYTE * pucData;
|
|
FLMUINT uiDataLen;
|
|
FLMUINT uiDummy;
|
|
void * pvBuffer;
|
|
|
|
if( pOldNodeList->findNodeInList( eNodeType, uiCollection,
|
|
ui64NodeId, pIcd->uiDictNum, &pucData, &uiDataLen, &uiDummy))
|
|
{
|
|
|
|
// Allocate the space needed.
|
|
|
|
if (RC_BAD( rc = pDynaBuf->allocSpace( uiDataLen, &pvBuffer)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_memcpy( pvBuffer, pucData, uiDataLen);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if( eNodeType == ATTRIBUTE_NODE)
|
|
{
|
|
if( RC_BAD( rc = pDb->getAttribute( uiCollection, ui64NodeId,
|
|
pIcd->uiDictNum, &pNode)))
|
|
{
|
|
flmAssert( rc != NE_XFLM_DOM_NODE_NOT_FOUND);
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NodeId, &pNode)))
|
|
{
|
|
flmAssert( rc != NE_XFLM_DOM_NODE_NOT_FOUND);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if( RC_BAD( rc = pNode->getBinary( (IF_Db *)pDb, pDynaBuf)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (RC_BAD( rc = pSearchKey->getBinary( uiKeyComponent, pDynaBuf)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (pIStream)
|
|
{
|
|
pIStream->Release();
|
|
}
|
|
|
|
if (pNode)
|
|
{
|
|
pNode->Release();
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: Compares result set entries during the finalization stage to allow
|
|
the result set to be sorted and to remove duplicates.
|
|
*****************************************************************************/
|
|
RCODE ixKeyCompare(
|
|
F_Db * pDb,
|
|
IXD * pIxd,
|
|
F_DataVector * pSearchKey,
|
|
F_OldNodeList * pOldNodeList1,
|
|
F_OldNodeList * pOldNodeList2,
|
|
FLMBOOL bCompareDocId,
|
|
FLMBOOL bCompareNodeIds,
|
|
const void * pvKey1,
|
|
FLMUINT uiKeyLen1,
|
|
const void * pvKey2,
|
|
FLMUINT uiKeyLen2,
|
|
FLMINT * piCompare)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
FLMUINT uiKeyComponent;
|
|
ICD * pIcd;
|
|
FLMUINT uiComponentLen1;
|
|
FLMUINT uiComponentLen2;
|
|
FLMBOOL bTruncated1;
|
|
FLMBOOL bTruncated2;
|
|
const FLMBYTE * pucKey1 = (const FLMBYTE *)pvKey1;
|
|
const FLMBYTE * pucKey2 = (const FLMBYTE *)pvKey2;
|
|
const FLMBYTE * pucKeyEnd1 = pucKey1 + uiKeyLen1;
|
|
const FLMBYTE * pucKeyEnd2 = pucKey2 + uiKeyLen2;
|
|
FLMBOOL bSortAscending;
|
|
FLMBOOL bSortMissingHigh;
|
|
FLMUINT64 ui64NodeId1;
|
|
FLMUINT64 ui64NodeId2;
|
|
|
|
flmAssert( uiKeyLen1 && uiKeyLen2);
|
|
|
|
// Loop for each compound piece of key
|
|
|
|
uiKeyComponent = 0;
|
|
pIcd = pIxd->pFirstKey;
|
|
for (;;)
|
|
{
|
|
bSortAscending = (pIcd->uiFlags & ICD_DESCENDING) ? FALSE : TRUE;
|
|
bSortMissingHigh = (pIcd->uiFlags & ICD_MISSING_HIGH) ? TRUE : FALSE;
|
|
uiComponentLen1 = getKeyComponentLength( pucKey1);
|
|
uiComponentLen2 = getKeyComponentLength( pucKey2);
|
|
|
|
// See if either component is a "high" key
|
|
// NOTE: KEY_HIGH_VALUE always sorts highest, regardless of
|
|
// ascending or descending. It is never actually stored. It is
|
|
// only passed in for searching.
|
|
|
|
if (uiComponentLen1 == KEY_HIGH_VALUE)
|
|
{
|
|
if (uiComponentLen2 == KEY_HIGH_VALUE)
|
|
{
|
|
uiComponentLen1 = uiComponentLen2 = 0;
|
|
goto Test_Exclusive;
|
|
}
|
|
else
|
|
{
|
|
*piCompare = 1;
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if (uiComponentLen2 == KEY_HIGH_VALUE)
|
|
{
|
|
*piCompare = -1;
|
|
goto Exit;
|
|
}
|
|
|
|
// See if either component is a "low" key
|
|
// NOTE: KEY_LOW_VALUE always sorts lowest, regardless of
|
|
// ascending or descending. It is never actually stored. It is
|
|
// only passed in for searching.
|
|
|
|
if (uiComponentLen1 == KEY_LOW_VALUE)
|
|
{
|
|
if (uiComponentLen2 == KEY_LOW_VALUE)
|
|
{
|
|
uiComponentLen1 = uiComponentLen2 = 0;
|
|
goto Test_Exclusive;
|
|
}
|
|
else
|
|
{
|
|
*piCompare = -1;
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if (uiComponentLen2 == KEY_LOW_VALUE)
|
|
{
|
|
*piCompare = 1;
|
|
goto Exit;
|
|
}
|
|
|
|
// See if either component is missing. Need to apply the rules for
|
|
// sorting missing components in that case.
|
|
|
|
if (!uiComponentLen1)
|
|
{
|
|
if (uiComponentLen2)
|
|
{
|
|
if (bSortMissingHigh)
|
|
{
|
|
*piCompare = bSortAscending ? 1 : -1;
|
|
}
|
|
else
|
|
{
|
|
*piCompare = bSortAscending ? -1 : 1;
|
|
}
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
goto Test_Exclusive;
|
|
}
|
|
}
|
|
else if (!uiComponentLen2)
|
|
{
|
|
if (bSortMissingHigh)
|
|
{
|
|
*piCompare = bSortAscending ? -1 : 1;
|
|
}
|
|
else
|
|
{
|
|
*piCompare = bSortAscending ? 1 : -1;
|
|
}
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Component length must not exceed remaining length of key.
|
|
|
|
flmAssert( pucKey1 + 2 + uiComponentLen1 <= pucKeyEnd1 &&
|
|
pucKey2 + 2 + uiComponentLen2 <= pucKeyEnd2);
|
|
|
|
if ((*piCompare = ixKeyCompareBinary( pucKey1 + 2, uiComponentLen1,
|
|
pucKey2 + 2, uiComponentLen2, bSortAscending)) != 0)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Data is equal, see if one or ther other is truncated.
|
|
|
|
bTruncated1 = isKeyComponentTruncated( pucKey1);
|
|
bTruncated2 = isKeyComponentTruncated( pucKey2);
|
|
|
|
if (bTruncated1 || bTruncated2)
|
|
{
|
|
if (!bTruncated2)
|
|
{
|
|
*piCompare = bSortAscending ? 1 : -1;
|
|
goto Exit;
|
|
}
|
|
else if (!bTruncated1)
|
|
{
|
|
*piCompare = bSortAscending ? -1 : 1;
|
|
goto Exit;
|
|
}
|
|
|
|
if (isSearchKeyComponent( pucKey1))
|
|
{
|
|
flmAssert( pSearchKey);
|
|
ui64NodeId1 = pSearchKey->getID( uiKeyComponent);
|
|
|
|
// The search key better have a node ID or the untruncated
|
|
// value.
|
|
|
|
flmAssert( ui64NodeId1 ||
|
|
!pSearchKey->isRightTruncated( uiKeyComponent));
|
|
}
|
|
else
|
|
{
|
|
|
|
// Need to read the data from the nodes and do a comparison.
|
|
// Get each node ID.
|
|
|
|
if (RC_BAD( rc = ixKeyGetNodeId( pIxd, pucKey1, pucKeyEnd1,
|
|
uiKeyComponent, &ui64NodeId1)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
flmAssert( ui64NodeId1);
|
|
}
|
|
|
|
if (isSearchKeyComponent( pucKey2))
|
|
{
|
|
flmAssert( pSearchKey);
|
|
ui64NodeId2 = pSearchKey->getID( uiKeyComponent);
|
|
|
|
// The search key better have a node ID or the untruncated
|
|
// value.
|
|
|
|
flmAssert( ui64NodeId2 ||
|
|
!pSearchKey->isRightTruncated( uiKeyComponent));
|
|
}
|
|
else
|
|
{
|
|
// Need to read the data from the nodes and do a comparison.
|
|
// Get each node ID.
|
|
|
|
if (RC_BAD( rc = ixKeyGetNodeId( pIxd, pucKey2, pucKeyEnd2,
|
|
uiKeyComponent, &ui64NodeId2)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
flmAssert( ui64NodeId2);
|
|
}
|
|
|
|
// If the node IDs are equal, we can skip fetching the data, because
|
|
// it will be the same.
|
|
|
|
if (ui64NodeId1 != ui64NodeId2)
|
|
{
|
|
FLMBYTE ucDynaBuf1[ 64];
|
|
FLMBYTE ucDynaBuf2[ 64];
|
|
F_DynaBuf dynaBuf1( ucDynaBuf1, sizeof( ucDynaBuf1));
|
|
F_DynaBuf dynaBuf2( ucDynaBuf2, sizeof( ucDynaBuf2));
|
|
|
|
// Better be binary data or text data.
|
|
|
|
switch (icdGetDataType( pIcd))
|
|
{
|
|
case XFLM_TEXT_TYPE:
|
|
{
|
|
if (RC_BAD( rc = ixKeyGetUnicode( pDb, pIcd,
|
|
pIxd->uiCollectionNum,
|
|
ui64NodeId1, uiKeyComponent,
|
|
pOldNodeList1, pSearchKey, &dynaBuf1)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (RC_BAD( rc = ixKeyGetUnicode( pDb, pIcd,
|
|
pIxd->uiCollectionNum,
|
|
ui64NodeId2, uiKeyComponent,
|
|
pOldNodeList2, pSearchKey, &dynaBuf2)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (RC_BAD( rc = ixKeyCompareUnicode( pIcd,
|
|
pIxd->uiLanguage,
|
|
dynaBuf1.getUnicodePtr(),
|
|
dynaBuf1.getDataLength(),
|
|
dynaBuf2.getUnicodePtr(),
|
|
dynaBuf2.getDataLength(), piCompare)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (*piCompare < 0)
|
|
{
|
|
*piCompare = bSortAscending ? -1 : 1;
|
|
goto Exit;
|
|
}
|
|
else if (*piCompare > 0)
|
|
{
|
|
*piCompare = bSortAscending ? 1 : -1;
|
|
goto Exit;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case XFLM_BINARY_TYPE:
|
|
{
|
|
if (RC_BAD( rc = ixKeyGetBinary( pDb, pIcd,
|
|
pIxd->uiCollectionNum,
|
|
ui64NodeId1, uiKeyComponent,
|
|
pOldNodeList1, pSearchKey, &dynaBuf1)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = ixKeyGetBinary( pDb, pIcd,
|
|
pIxd->uiCollectionNum, ui64NodeId2, uiKeyComponent,
|
|
pOldNodeList2, pSearchKey, &dynaBuf2)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if ((*piCompare = ixKeyCompareBinary(
|
|
dynaBuf1.getBufferPtr(),
|
|
dynaBuf1.getDataLength(),
|
|
dynaBuf2.getBufferPtr(),
|
|
dynaBuf2.getDataLength(),
|
|
bSortAscending)) != 0)
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Test_Exclusive:
|
|
|
|
// See if either component is exclusive - everything else is
|
|
// equal up to this point.
|
|
|
|
if (isKeyComponentLTExclusive( pucKey1))
|
|
{
|
|
if (!isKeyComponentLTExclusive( pucKey2))
|
|
{
|
|
*piCompare = bSortAscending ? -1 : 1;
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if (isKeyComponentGTExclusive( pucKey1))
|
|
{
|
|
if (!isKeyComponentGTExclusive( pucKey2))
|
|
{
|
|
*piCompare = bSortAscending ? 1 : -1;
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if (isKeyComponentLTExclusive( pucKey2))
|
|
{
|
|
*piCompare = bSortAscending ? 1 : -1;
|
|
goto Exit;
|
|
}
|
|
else if (isKeyComponentGTExclusive( pucKey2))
|
|
{
|
|
*piCompare = bSortAscending ? -1 : 1;
|
|
goto Exit;
|
|
}
|
|
|
|
// Position to the end of this component
|
|
|
|
pucKey1 += (2 + uiComponentLen1);
|
|
pucKey2 += (2 + uiComponentLen2);
|
|
|
|
pIcd = pIcd->pNextKeyComponent;
|
|
uiKeyComponent++;
|
|
|
|
// If there are no more ICDs, we are done with the key
|
|
// components.
|
|
|
|
if (!pIcd)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// See if we are out of key components - this may be a search that
|
|
// passed in only a partial key.
|
|
|
|
if (pucKey1 >= pucKeyEnd1)
|
|
{
|
|
*piCompare = (pucKey2 >= pucKeyEnd2) ? 0 : -1;
|
|
goto Exit;
|
|
}
|
|
else if (pucKey2 >= pucKeyEnd2)
|
|
{
|
|
*piCompare = 1;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Compare the node ID list, if being requested to. Includes comparing of the
|
|
// last byte, which is the total number of bytes in the node ID list.
|
|
|
|
if (bCompareDocId || bCompareNodeIds)
|
|
{
|
|
|
|
// See if we have a node IDs - this may be a search that
|
|
// passed in only a partial key and there are no NODE ids on it.
|
|
|
|
if (pucKey1 >= pucKeyEnd1)
|
|
{
|
|
*piCompare = (pucKey2 >= pucKeyEnd2) ? 0 : -1;
|
|
goto Exit;
|
|
}
|
|
else if (pucKey2 >= pucKeyEnd2)
|
|
{
|
|
*piCompare = 1;
|
|
goto Exit;
|
|
}
|
|
|
|
// See if either one has an ID buffer of "high"
|
|
|
|
if (*pucKey1 == 0xFF)
|
|
{
|
|
|
|
// Key1 has a "high" set of node IDs, see what key2 has.
|
|
|
|
*piCompare = (*pucKey2 == 0xFF) ? 0 : 1;
|
|
goto Exit;
|
|
}
|
|
else if (*pucKey2 == 0xFF)
|
|
{
|
|
// Key2 has a "high" set of node IDs, key1 does not.
|
|
|
|
*piCompare = -1;
|
|
goto Exit;
|
|
}
|
|
else if (bCompareNodeIds)
|
|
{
|
|
FLMUINT uiNodeIDLen1 = (FLMUINT)(pucKeyEnd1 - pucKey1);
|
|
FLMUINT uiNodeIDLen2 = (FLMUINT)(pucKeyEnd2 - pucKey2);
|
|
|
|
// We will always compare doc ID if we are also comparing
|
|
// node ID. Hence, although it is probably not entirely
|
|
// necessary, we require the caller to also set bCompareDocId
|
|
// to TRUE, just so that he doesn't think it is possible to
|
|
// compare the node ids without comparing the document id.
|
|
|
|
flmAssert( bCompareDocId);
|
|
|
|
*piCompare = ixKeyCompareBinary( pucKey1, uiNodeIDLen1,
|
|
pucKey2, uiNodeIDLen2, TRUE);
|
|
}
|
|
else
|
|
{
|
|
FLMUINT64 ui64DocId1;
|
|
FLMUINT64 ui64DocId2;
|
|
|
|
// Get the document ID and compare it, and only it.
|
|
// At this point, both keys should be positioned to
|
|
// get the document ID.
|
|
|
|
if (RC_BAD( rc = flmDecodeSEN64( &pucKey1, pucKeyEnd1, &ui64DocId1)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (RC_BAD( rc = flmDecodeSEN64( &pucKey2, pucKeyEnd2, &ui64DocId2)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (ui64DocId1 == ui64DocId2)
|
|
{
|
|
*piCompare = 0;
|
|
}
|
|
else if (ui64DocId1 < ui64DocId2)
|
|
{
|
|
*piCompare = -1;
|
|
}
|
|
else
|
|
{
|
|
*piCompare = 1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*piCompare = 0;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Compare function used to compare index number and key
|
|
****************************************************************************/
|
|
FINLINE RCODE krefCompareIxAndKey(
|
|
F_Db * pDb,
|
|
IXD * pIxd,
|
|
KREF_ENTRY * pKrefA,
|
|
KREF_ENTRY * pKrefB,
|
|
FLMINT * piCompare)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
|
|
// Compare index numbers
|
|
|
|
if ((*piCompare = ((FLMINT) pKrefA->ui16IxNum) -
|
|
((FLMINT) pKrefB->ui16IxNum)) != 0)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (!pIxd || pIxd->uiIndexNum != (FLMUINT)pKrefA->ui16IxNum)
|
|
{
|
|
if (RC_BAD( rc = pDb->getDict()->getIndex( (FLMUINT)pKrefA->ui16IxNum,
|
|
NULL, &pIxd, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (RC_BAD( rc = ixKeyCompare( pDb, pIxd, NULL,
|
|
pKrefA->bDelete ? pDb->getOldNodeList() : NULL,
|
|
pKrefB->bDelete ? pDb->getOldNodeList() : NULL,
|
|
TRUE, TRUE,
|
|
&pKrefA [1], (FLMUINT)pKrefA->ui16KeyLen,
|
|
&pKrefB [1], (FLMUINT)pKrefB->ui16KeyLen, piCompare)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Compare function used to compare key data
|
|
****************************************************************************/
|
|
FINLINE FLMBOOL krefIsKeyDataEqual(
|
|
KREF_ENTRY * pKrefA,
|
|
KREF_ENTRY * pKrefB)
|
|
{
|
|
if( pKrefA->uiDataLen != pKrefB->uiDataLen)
|
|
{
|
|
return( FALSE);
|
|
}
|
|
|
|
if( pKrefA->uiDataLen)
|
|
{
|
|
if( f_memcmp( (FLMBYTE *)(&pKrefA [1]) +
|
|
pKrefA->ui16KeyLen + 1,
|
|
(FLMBYTE *)(&pKrefB [1]) +
|
|
pKrefB->ui16KeyLen + 1,
|
|
pKrefA->uiDataLen) != 0)
|
|
{
|
|
return( FALSE);
|
|
}
|
|
}
|
|
|
|
return( TRUE);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Compare function used to compare two keys.
|
|
****************************************************************************/
|
|
FINLINE RCODE krefSortCompare(
|
|
F_Db * pDb,
|
|
IXD * pIxd,
|
|
KREF_ENTRY * pKrefA,
|
|
KREF_ENTRY * pKrefB,
|
|
FLMINT * piCompare)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
|
|
if (RC_BAD( rc = krefCompareIxAndKey( pDb, pIxd, pKrefA, pKrefB, piCompare)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (*piCompare == 0)
|
|
{
|
|
*piCompare = (pKrefA->uiSequence < pKrefB->uiSequence) ? -1 : 1;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Checks if the current database has any UNIQUE indexes that need
|
|
to checked. Also does duplicate processing for the record.
|
|
****************************************************************************/
|
|
RCODE F_Db::processDupKeys(
|
|
IXD * pIxd)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
|
|
// Sort and remove duplicates
|
|
|
|
if (m_uiKrefCount > 1)
|
|
{
|
|
if (RC_BAD( rc = krefQuickSort( this, pIxd, m_pKrefTbl,
|
|
0, m_uiKrefCount - 1)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (RC_BAD( rc = krefKillDups( this, pIxd,
|
|
m_pKrefTbl, &m_uiKrefCount)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Commit (write out) all keys that have built up in the KREF table.
|
|
****************************************************************************/
|
|
RCODE F_Db::keysCommit(
|
|
FLMBOOL bCommittingTrans,
|
|
FLMBOOL bSortKeys)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
FLMUINT uiRflToken = 0;
|
|
|
|
// If the Kref has not been initialized, there is no
|
|
// work to do.
|
|
|
|
if (m_bKrefSetup)
|
|
{
|
|
LFILE * pLFile = NULL;
|
|
IXD * pIxd;
|
|
FLMUINT uiTotal = m_uiKrefCount;
|
|
KREF_ENTRY * pKref;
|
|
KREF_ENTRY ** pKrefTbl = m_pKrefTbl;
|
|
FLMUINT uiKrefNum;
|
|
FLMUINT uiLastIxNum;
|
|
|
|
// We should not have reached this point if bAbortTrans is TRUE
|
|
|
|
if( RC_BAD( m_AbortRc))
|
|
{
|
|
rc = RC_SET_AND_ASSERT( m_AbortRc);
|
|
goto Exit;
|
|
}
|
|
|
|
// Sort the KREF table, if it contains more than one key.
|
|
// This will sort all keys from the same index the same.
|
|
|
|
if (uiTotal > 1 && bSortKeys)
|
|
{
|
|
processDupKeys( NULL);
|
|
uiTotal = m_uiKrefCount;
|
|
}
|
|
|
|
// Disable RFL logging
|
|
|
|
if( uiTotal)
|
|
{
|
|
m_pDatabase->m_pRfl->disableLogging( &uiRflToken);
|
|
}
|
|
|
|
// Loop through the KREF table outputting all keys
|
|
|
|
uiLastIxNum = 0;
|
|
for (uiKrefNum = 0; uiKrefNum < uiTotal; uiKrefNum++)
|
|
{
|
|
pKref = pKrefTbl [uiKrefNum];
|
|
|
|
// See if the LFILE changed
|
|
|
|
flmAssert( pKref->ui16IxNum);
|
|
|
|
if (pKref->ui16IxNum != uiLastIxNum)
|
|
{
|
|
uiLastIxNum = pKref->ui16IxNum;
|
|
if (RC_BAD( rc = m_pDict->getIndex( uiLastIxNum,
|
|
&pLFile, &pIxd, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Flush the key to the index
|
|
if (m_pKeyColl)
|
|
{
|
|
m_pKeyColl->addKey( this, pIxd, pKref);
|
|
}
|
|
else
|
|
{
|
|
if (RC_BAD(rc = refUpdate( pLFile, pIxd, pKref, TRUE)))
|
|
{
|
|
if (rc != NE_XFLM_NOT_UNIQUE)
|
|
{
|
|
RC_UNEXPECTED_ASSERT( rc);
|
|
}
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bCommittingTrans)
|
|
{
|
|
krefCntrlFree();
|
|
}
|
|
else
|
|
{
|
|
// Empty the table out so we can add more keys in this trans.
|
|
|
|
m_pKrefPool->poolReset( NULL, TRUE);
|
|
m_uiKrefCount = 0;
|
|
m_uiTotalKrefBytes = 0;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if( RC_BAD( rc))
|
|
{
|
|
setMustAbortTrans( rc);
|
|
}
|
|
|
|
if( uiRflToken)
|
|
{
|
|
m_pDatabase->m_pRfl->enableLogging( &uiRflToken);
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: Quick sort an array of KREF_ENTRY * values.
|
|
****************************************************************************/
|
|
FSTATIC RCODE krefQuickSort(
|
|
F_Db * pDb,
|
|
IXD * pIxd,
|
|
KREF_ENTRY ** pEntryTbl,
|
|
FLMUINT uiLowerBounds,
|
|
FLMUINT uiUpperBounds)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
FLMUINT uiLBPos;
|
|
FLMUINT uiUBPos;
|
|
FLMUINT uiMIDPos;
|
|
FLMUINT uiLeftItems;
|
|
FLMUINT uiRightItems;
|
|
KREF_ENTRY * pCurEntry;
|
|
KREF_ENTRY * pTempKref;
|
|
FLMINT iCompare;
|
|
|
|
Iterate_Larger_Half:
|
|
|
|
uiUBPos = uiUpperBounds;
|
|
uiLBPos = uiLowerBounds;
|
|
uiMIDPos = (uiUpperBounds + uiLowerBounds + 1) / 2;
|
|
pCurEntry = pEntryTbl[ uiMIDPos ];
|
|
for( ;;)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (uiLBPos != uiMIDPos)
|
|
{
|
|
if (RC_BAD( rc = krefSortCompare( pDb, pIxd,
|
|
pEntryTbl[ uiLBPos], pCurEntry, &iCompare)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (iCompare >= 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (uiLBPos >= uiUpperBounds)
|
|
{
|
|
break;
|
|
}
|
|
uiLBPos++;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
if (uiUBPos != uiMIDPos)
|
|
{
|
|
if (RC_BAD( rc = krefSortCompare( pDb, pIxd, pCurEntry,
|
|
pEntryTbl[ uiUBPos], &iCompare)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (iCompare >= 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (!uiUBPos)
|
|
{
|
|
break;
|
|
}
|
|
uiUBPos--;
|
|
}
|
|
|
|
if (uiLBPos < uiUBPos) // Interchange and continue loop.
|
|
{
|
|
|
|
// Interchange [uiLBPos] with [uiUBPos].
|
|
|
|
KY_SWAP( pEntryTbl, uiLBPos, uiUBPos );
|
|
uiLBPos++; // Scan from left to right.
|
|
uiUBPos--; // Scan from right to left.
|
|
}
|
|
else // Past each other - done
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check for swap( LB, MID ) - cases 3 and 4
|
|
|
|
if (uiLBPos < uiMIDPos)
|
|
{
|
|
|
|
// Interchange [uiLBPos] with [uiMIDPos]
|
|
|
|
KY_SWAP( pEntryTbl, uiMIDPos, uiLBPos );
|
|
uiMIDPos = uiLBPos;
|
|
}
|
|
else if (uiMIDPos < uiUBPos)
|
|
{
|
|
|
|
// Interchange [uUBPos] with [uiMIDPos]
|
|
|
|
KY_SWAP( pEntryTbl, uiMIDPos, uiUBPos );
|
|
uiMIDPos = uiUBPos;
|
|
}
|
|
|
|
// Check the left piece.
|
|
|
|
uiLeftItems = (uiLowerBounds + 1 < uiMIDPos )
|
|
? uiMIDPos - uiLowerBounds // 2 or more
|
|
: 0;
|
|
uiRightItems = (uiMIDPos + 1 < uiUpperBounds )
|
|
? uiUpperBounds - uiMIDPos // 2 or more
|
|
: 0;
|
|
|
|
if (uiLeftItems < uiRightItems)
|
|
{
|
|
|
|
// Recurse on the LEFT side and goto the top on the RIGHT side.
|
|
|
|
if (uiLeftItems)
|
|
{
|
|
if (RC_BAD( rc = krefQuickSort( pDb, pIxd, pEntryTbl,
|
|
uiLowerBounds, uiMIDPos - 1)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
uiLowerBounds = uiMIDPos + 1;
|
|
goto Iterate_Larger_Half;
|
|
}
|
|
else if (uiLeftItems) // Compute a truth table to figure out this check.
|
|
{
|
|
|
|
// Recurse on the RIGHT side and goto the top for the LEFT side.
|
|
|
|
if (uiRightItems)
|
|
{
|
|
if (RC_BAD( rc = krefQuickSort( pDb, pIxd, pEntryTbl,
|
|
uiMIDPos + 1, uiUpperBounds)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
uiUpperBounds = uiMIDPos - 1;
|
|
goto Iterate_Larger_Half;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Kill all duplicate keys in the KREF table.
|
|
****************************************************************************/
|
|
FSTATIC RCODE krefKillDups(
|
|
F_Db * pDb,
|
|
IXD * pIxd,
|
|
KREF_ENTRY ** pKrefTbl,
|
|
FLMUINT * puiKrefTotal)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
FLMUINT uiCurKref = 0;
|
|
FLMUINT uiLastKref = *puiKrefTotal;
|
|
FLMUINT uiFirstForKey;
|
|
FLMUINT uiLastForKey;
|
|
FLMUINT uiNewPosOffset = 0;
|
|
FLMINT iCompare;
|
|
|
|
while( uiCurKref < uiLastKref)
|
|
{
|
|
uiFirstForKey = uiLastForKey = uiCurKref;
|
|
uiCurKref = uiFirstForKey + 1;
|
|
|
|
while( uiCurKref < uiLastKref)
|
|
{
|
|
if (RC_BAD( rc = krefCompareIxAndKey( pDb, pIxd, pKrefTbl[ uiFirstForKey],
|
|
pKrefTbl[ uiCurKref], &iCompare)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (iCompare)
|
|
{
|
|
break;
|
|
}
|
|
|
|
uiLastForKey = uiCurKref++;
|
|
}
|
|
|
|
if( uiFirstForKey == uiLastForKey)
|
|
{
|
|
pKrefTbl[ uiNewPosOffset++] = pKrefTbl[ uiFirstForKey];
|
|
continue;
|
|
}
|
|
|
|
if( pKrefTbl[ uiFirstForKey]->bDelete)
|
|
{
|
|
if( pKrefTbl[ uiLastForKey]->bDelete)
|
|
{
|
|
pKrefTbl[ uiNewPosOffset++] = pKrefTbl[ uiFirstForKey];
|
|
}
|
|
else
|
|
{
|
|
TestCancel:
|
|
// See if the operations cancel each other. If they don't, we
|
|
// need to keep both operations
|
|
|
|
if( !krefIsKeyDataEqual( pKrefTbl[ uiFirstForKey], pKrefTbl[ uiLastForKey]))
|
|
{
|
|
pKrefTbl[ uiNewPosOffset++] = pKrefTbl[ uiFirstForKey];
|
|
pKrefTbl[ uiNewPosOffset++] = pKrefTbl[ uiLastForKey];
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( pKrefTbl[ uiLastForKey]->bDelete)
|
|
{
|
|
goto TestCancel;
|
|
}
|
|
else
|
|
{
|
|
pKrefTbl[ uiNewPosOffset++] = pKrefTbl[ uiLastForKey];
|
|
}
|
|
}
|
|
}
|
|
|
|
*puiKrefTotal = uiNewPosOffset;
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|