Files
mars-flaim/xflaim/src/kyqsort.cpp
dsandersoremutah c55dab446f Renamed version4 to flaim and version5 to xflaim
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@7 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2006-01-27 21:06:39 +00:00

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);
}