git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@1009 0109f412-320b-0410-ab79-c3e0c5ffbbe6
1958 lines
47 KiB
C++
1958 lines
47 KiB
C++
//-------------------------------------------------------------------------
|
|
// Desc: Key and reference building routines.
|
|
// Tabs: 3
|
|
//
|
|
// Copyright (c) 1990-1992, 1994-2007 Novell, Inc. All Rights Reserved.
|
|
//
|
|
// This library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
// License as published by the Free Software Foundation; version 2.1
|
|
// of the License.
|
|
//
|
|
// This library 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
|
|
// Library Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; 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$
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "flaimsys.h"
|
|
|
|
// Constants for checking to see if numbers are greater than what will fit
|
|
// in 32 bits. the B at the beginning signifies a negative value
|
|
|
|
static const FLMBYTE gv_ucMinInt32 [6] =
|
|
{
|
|
0xB2,0x14,0x74,0x83,0x64,0x8F
|
|
};
|
|
|
|
// Note that the last byte is 0xFF, but it could be 0xF1, 0xF2, etc - it really
|
|
// doesn't matter what is in the lower nibble, because the number terminates
|
|
// when it sees the F in the high nibble.
|
|
|
|
static const FLMBYTE gv_ucMaxUInt32 [6] =
|
|
{
|
|
0x42,0x94,0x96,0x72,0x95,0xFF
|
|
};
|
|
|
|
#define KREF_TBL_SIZE 512
|
|
#define KREF_TBL_THRESHOLD 400
|
|
#define KREF_POOL_BLOCK_SIZE 8192
|
|
#define KREF_TOTAL_BYTES_THRESHOLD ((KREF_POOL_BLOCK_SIZE * 3) - 250)
|
|
|
|
#define KY_SWAP(pKrefTbl, leftP, rightP) \
|
|
pTempKref = pKrefTbl[leftP]; \
|
|
pKrefTbl[leftP] = pKrefTbl[rightP]; \
|
|
pKrefTbl[rightP] = pTempKref
|
|
|
|
FSTATIC FLMINT _KrefCompare(
|
|
FLMUINT * puiQsortFlags,
|
|
KREF_ENTRY * pKreftA,
|
|
KREF_ENTRY * pKreftB);
|
|
|
|
FSTATIC RCODE KYAddUniqueKeys(
|
|
FDB * pDb);
|
|
|
|
FSTATIC RCODE _KrefQuickSort(
|
|
FLMUINT * puiQsortFlags,
|
|
KREF_ENTRY ** pEntryTbl,
|
|
FLMUINT uiLowerBounds,
|
|
FLMUINT uiUpperBounds);
|
|
|
|
FSTATIC RCODE _KrefKillDups(
|
|
FLMUINT * puiQsortFlags,
|
|
KREF_ENTRY ** pKrefTbl,
|
|
FLMUINT * puiKrefTotalRV);
|
|
|
|
FSTATIC RCODE flmProcessIndexedFld(
|
|
FDB * pDb,
|
|
IXD * pUseIxd,
|
|
IFD * pIfdChain,
|
|
void ** ppPathFlds,
|
|
FLMUINT uiLeafFieldLevel,
|
|
FLMUINT uiAction,
|
|
FLMUINT uiContainerNum,
|
|
FLMUINT uiDrn,
|
|
FLMBOOL * pbHadUniqueKeys,
|
|
FlmRecord * pRecord,
|
|
void * pvField);
|
|
|
|
/****************************************************************************
|
|
Desc: Main driver for processing the fields in a record.
|
|
****************************************************************************/
|
|
RCODE flmProcessRecFlds(
|
|
FDB * pDb,
|
|
IXD * pIxd,
|
|
FLMUINT uiContainerNum,
|
|
FLMUINT uiDrn,
|
|
FlmRecord * pRecord,
|
|
FLMUINT uiAction,
|
|
FLMBOOL bPurgedFldsOk,
|
|
FLMBOOL * pbHadUniqueKeys)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
void * pathFlds[GED_MAXLVLNUM + 1];
|
|
FLMUINT uiLeafFieldLevel;
|
|
void * pvField;
|
|
FLMUINT uiDbVersion = pDb->pFile->FileHdr.uiVersionNum;
|
|
|
|
if ((pvField = pRecord->root()) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_ILLEGAL_OP);
|
|
goto Exit;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
FLMUINT uiItemType;
|
|
IFD* pIfdChain;
|
|
FLMUINT uiTagNum = pRecord->getFieldID( pvField);
|
|
FLMUINT uiFldState;
|
|
FLMBOOL bFldEncrypted;
|
|
FLMUINT uiEncFlags = 0;
|
|
FLMUINT uiEncId = 0;
|
|
FLMUINT uiEncState;
|
|
FLMUINT uiFieldType = pRecord->getDataType( pvField);
|
|
|
|
if (RC_BAD( rc = fdictGetField( pDb->pDict, uiTagNum, &uiItemType,
|
|
&pIfdChain, &uiFldState)))
|
|
{
|
|
|
|
// Fill diagnostic error data.
|
|
|
|
pDb->Diag.uiInfoFlags |= (FLM_DIAG_FIELD_NUM | FLM_DIAG_FIELD_TYPE);
|
|
pDb->Diag.uiFieldNum = uiTagNum;
|
|
pDb->Diag.uiFieldType = uiFieldType;
|
|
goto Exit;
|
|
}
|
|
|
|
// Check for encryption.
|
|
|
|
bFldEncrypted = pRecord->isEncryptedField( pvField);
|
|
if (bFldEncrypted)
|
|
{
|
|
|
|
// May still proceed if the field is already encrypted.
|
|
|
|
uiEncFlags = pRecord->getEncFlags( pvField);
|
|
|
|
if (!(uiEncFlags & FLD_HAVE_ENCRYPTED_DATA) &&
|
|
!pDb->pFile->bInLimitedMode)
|
|
{
|
|
uiEncId = pRecord->getEncryptionID( pvField);
|
|
|
|
if (RC_BAD( rc = fdictGetEncInfo( pDb, uiEncId, NULL, &uiEncState)))
|
|
{
|
|
|
|
// Fill diagnostic error data.
|
|
|
|
pDb->Diag.uiInfoFlags |= (FLM_DIAG_FIELD_NUM | FLM_DIAG_ENC_ID);
|
|
pDb->Diag.uiFieldNum = uiTagNum;
|
|
pDb->Diag.uiEncId = uiEncId;
|
|
goto Exit;
|
|
}
|
|
|
|
// Check the state of the Encryption Record.
|
|
|
|
if (uiEncState == ITT_ENC_STATE_PURGE)
|
|
{
|
|
|
|
// EncDef record has been marked as 'purged'. So, user is
|
|
// not allowed to add new fields that are encrypted with
|
|
// this EncDef Id.
|
|
|
|
pDb->Diag.uiInfoFlags |= (FLM_DIAG_FIELD_NUM | FLM_DIAG_ENC_ID);
|
|
pDb->Diag.uiFieldNum = uiTagNum;
|
|
pDb->Diag.uiEncId = uiEncId;
|
|
rc = RC_SET( FERR_PURGED_ENCDEF_FOUND);
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if (!(uiEncFlags & FLD_HAVE_ENCRYPTED_DATA) &&
|
|
pDb->pFile->bInLimitedMode)
|
|
{
|
|
rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
uiLeafFieldLevel = (FLMINT) pRecord->getLevel( pvField);
|
|
pathFlds[uiLeafFieldLevel] = pvField;
|
|
|
|
// Check the field state
|
|
|
|
if (uiFldState == ITT_FLD_STATE_PURGE && bPurgedFldsOk == FALSE)
|
|
{
|
|
pDb->Diag.uiInfoFlags |= FLM_DIAG_FIELD_NUM;
|
|
pDb->Diag.uiFieldNum = uiTagNum;
|
|
rc = RC_SET( FERR_PURGED_FLD_FOUND);
|
|
goto Exit;
|
|
}
|
|
else if ((uiFldState == ITT_FLD_STATE_CHECKING ||
|
|
uiFldState == ITT_FLD_STATE_UNUSED) &&
|
|
!(uiAction & KREF_DEL_KEYS) && !(uiAction & KREF_INDEXING_ONLY))
|
|
{
|
|
// Because a occurance of this field was found update the field's
|
|
// state to be 'active'
|
|
|
|
if (RC_BAD( rc = flmChangeItemState( pDb, uiTagNum,
|
|
ITT_FLD_STATE_ACTIVE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// If this is an encrypted field, see if we need to update the
|
|
// state of the EncDef record too.
|
|
|
|
if (bFldEncrypted)
|
|
{
|
|
if ((uiEncState == ITT_ENC_STATE_CHECKING ||
|
|
uiEncState == ITT_ENC_STATE_UNUSED) &&
|
|
!(uiAction & KREF_DEL_KEYS) && !(uiAction & KREF_INDEXING_ONLY))
|
|
{
|
|
if (RC_BAD( rc = flmChangeItemState( pDb, uiEncId,
|
|
ITT_ENC_STATE_ACTIVE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (uiItemType != uiFieldType && uiTagNum < FLM_DICT_FIELD_NUMS)
|
|
{
|
|
rc = RC_SET( FERR_BAD_FIELD_TYPE);
|
|
pDb->Diag.uiInfoFlags |= (FLM_DIAG_FIELD_NUM | FLM_DIAG_FIELD_TYPE);
|
|
pDb->Diag.uiFieldNum = uiTagNum;
|
|
pDb->Diag.uiFieldType = uiFieldType;
|
|
goto Exit;
|
|
}
|
|
|
|
if (uiFieldType == FLM_BLOB_TYPE)
|
|
{
|
|
if (!(uiAction & KREF_INDEXING_ONLY))
|
|
{
|
|
if (RC_BAD( rc = flmBlobPlaceInTransactionList( pDb,
|
|
((uiAction & KREF_DEL_KEYS) ? BLOB_DELETE_ACTION : BLOB_ADD_ACTION),
|
|
pRecord, pvField)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
else if (uiFieldType == FLM_NUMBER_TYPE)
|
|
{
|
|
|
|
// Make sure if the database version is not at least 4.62 that we
|
|
// don't allow numbers that are too large into the database.
|
|
|
|
if (uiDbVersion < FLM_FILE_FORMAT_VER_4_62)
|
|
{
|
|
const FLMBYTE * pucDataPtr;
|
|
FLMUINT uiDataLength = pRecord->getDataLength( pvField);
|
|
|
|
// All data lengths less than six will be ok. All data lengths
|
|
// greater than six will be bad.
|
|
|
|
if (uiDataLength < 6)
|
|
{
|
|
// For numbers whose data length is less than six, the number
|
|
// is guaranteed to fit in 32 bits.
|
|
}
|
|
else if (uiDataLength > 6)
|
|
{
|
|
|
|
// Any numbers whose data length is greater than six are more
|
|
// than 32 bits.
|
|
|
|
rc = RC_SET( FERR_64BIT_NUMS_NOT_SUPPORTED);
|
|
goto Exit;
|
|
}
|
|
else // uiDataLength == 6
|
|
{
|
|
|
|
// Check for encryption - make sure we have the decrypted data to
|
|
// look at.
|
|
|
|
if (pRecord->isEncryptedField( pvField) &&
|
|
!(pRecord->getEncFlags( pvField) & FLD_HAVE_DECRYPTED_DATA))
|
|
{
|
|
rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE);
|
|
goto Exit;
|
|
}
|
|
pucDataPtr = pRecord->getDataPtr( pvField);
|
|
|
|
// See if the number is negative.
|
|
|
|
if ((*pucDataPtr & 0xF0) == 0xB0)
|
|
{
|
|
// The sixth byte should have an F in either the upper or
|
|
// lower nibble. If it is in the upper nibble, the number
|
|
// will fit in 32 bits.
|
|
|
|
if ((pucDataPtr [5] & 0xF0) == 0xF0)
|
|
{
|
|
// F was in upper nibble, number will fit in 32 bits.
|
|
}
|
|
else
|
|
{
|
|
// The sixth byte should have an F in the lower nibble, so
|
|
// it is ok to compare all six bytes. We are looking for
|
|
// something greater than B2,14,74,83,64,8F
|
|
|
|
if (f_memcmp( pucDataPtr, gv_ucMinInt32, 6) > 0)
|
|
{
|
|
rc = RC_SET( FERR_64BIT_NUMS_NOT_SUPPORTED);
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If the sixth byte is not an F in the high nibble, we
|
|
// have more than a 32 bit value. If it has an F in the
|
|
// high nibble, check the first five bytes to make sure
|
|
// they are not greater than 42,94,96,72,95
|
|
|
|
if ((pucDataPtr [5] & 0xF0) != 0xF0 ||
|
|
f_memcmp( pucDataPtr, gv_ucMaxUInt32, 5) > 0)
|
|
{
|
|
rc = RC_SET( FERR_64BIT_NUMS_NOT_SUPPORTED);
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pIfdChain)
|
|
{
|
|
if (RC_BAD( rc = flmProcessIndexedFld( pDb, pIxd, pIfdChain, pathFlds,
|
|
uiLeafFieldLevel, uiAction, uiContainerNum, uiDrn,
|
|
pbHadUniqueKeys, pRecord, pvField)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if ((pvField = pRecord->next( pvField)) == NULL)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
// Build and add the compound keys to the KREF table
|
|
|
|
if (RC_OK( rc))
|
|
{
|
|
rc = KYBuildCmpKeys( pDb, uiAction, uiContainerNum, uiDrn,
|
|
pbHadUniqueKeys, pRecord);
|
|
}
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: See if a field's path matches the path in the IFD.
|
|
****************************************************************************/
|
|
FLMBOOL flmCheckIfdPath(
|
|
IFD * pIfd,
|
|
FlmRecord * pRecord,
|
|
void ** ppPathFlds,
|
|
FLMUINT uiLeafFieldLevel,
|
|
void * pvLeafField,
|
|
void ** ppvContextField)
|
|
{
|
|
FLMBOOL bMatched = FALSE;
|
|
void * pvContextField;
|
|
FLMINT iParentPos;
|
|
FLMUINT * puiIfdFldPathCToP;
|
|
|
|
// Check the field path to see if field is in context.
|
|
|
|
pvContextField = pvLeafField;
|
|
puiIfdFldPathCToP = &pIfd->pFieldPathCToP[1];
|
|
iParentPos = (FLMINT) uiLeafFieldLevel - 1;
|
|
|
|
while (*puiIfdFldPathCToP && iParentPos >= 0)
|
|
{
|
|
pvContextField = ppPathFlds[iParentPos];
|
|
|
|
// Check for FLM_ANY_FIELD (wild_tag) and skip it.
|
|
|
|
if (*puiIfdFldPathCToP == FLM_ANY_FIELD)
|
|
{
|
|
|
|
// Look at next field in IFD path to see if it matches the
|
|
// current field. If it does, continue from there.
|
|
|
|
if (*(puiIfdFldPathCToP + 1))
|
|
{
|
|
if (pRecord->getFieldID( pvContextField) ==
|
|
*(puiIfdFldPathCToP + 1))
|
|
{
|
|
|
|
// Skip wild card and field that matched.
|
|
|
|
puiIfdFldPathCToP += 2;
|
|
}
|
|
|
|
// Go to next field in path being evaluated no matter what.
|
|
// If it didn't match, we continue looking at the wild card.
|
|
// If it did match, we go to the next field in the path.
|
|
|
|
iParentPos--;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Rest of path is an automatic match - had wildcard at top
|
|
// of IFD path.
|
|
//
|
|
// It's not really necessary to increment this, but it is more
|
|
// efficient because of the comparisons that are done when we
|
|
// exit this loop.
|
|
|
|
puiIfdFldPathCToP++;
|
|
pvContextField = ppPathFlds[0];
|
|
break;
|
|
}
|
|
}
|
|
else if (pRecord->getFieldID( pvContextField) != *puiIfdFldPathCToP)
|
|
{
|
|
|
|
// Field does not match current field in IFD. This jump to Exit
|
|
// will return FALSE. bMatched is FALSE at this point.
|
|
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Go up a level in the record and the IFD path - to parent.
|
|
|
|
iParentPos--;
|
|
puiIfdFldPathCToP++;
|
|
}
|
|
}
|
|
|
|
// If we got to the end of the field path in the IFD, we have a match.
|
|
|
|
if (!(*puiIfdFldPathCToP) ||
|
|
(*puiIfdFldPathCToP == FLM_ANY_FIELD && !(*(puiIfdFldPathCToP + 1))))
|
|
{
|
|
*ppvContextField = pvContextField;
|
|
bMatched = TRUE;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return (bMatched);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Processes a field in a record - indexing, blob, etc.
|
|
****************************************************************************/
|
|
FSTATIC RCODE flmProcessIndexedFld(
|
|
FDB * pDb,
|
|
IXD * pUseIxd,
|
|
IFD * pIfdChain,
|
|
void ** ppPathFlds,
|
|
FLMUINT uiLeafFieldLevel,
|
|
FLMUINT uiAction,
|
|
FLMUINT uiContainerNum,
|
|
FLMUINT uiDrn,
|
|
FLMBOOL * pbHadUniqueKeys,
|
|
FlmRecord * pRecord,
|
|
void * pvField)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
IFD * pIfd;
|
|
IXD * pIxd;
|
|
void * pRootContext;
|
|
const FLMBYTE * pValue;
|
|
const FLMBYTE * pExportValue;
|
|
FLMUINT uiValueLen;
|
|
FLMUINT uiKeyLen;
|
|
FLMBYTE pTmpKeyBuf[MAX_KEY_SIZ];
|
|
|
|
pTmpKeyBuf[0] = '\0';
|
|
|
|
for (pIfd = pIfdChain; pIfd; pIfd = pIfd->pNextInChain)
|
|
{
|
|
if (pUseIxd)
|
|
{
|
|
if (pUseIxd->uiIndexNum == pIfd->uiIndexNum)
|
|
{
|
|
pIxd = pUseIxd;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pIxd = pIfd->pIxd;
|
|
|
|
// If index is offline or on a different container, skip it.
|
|
// NOTE: if pIxd->uiContainerNum is zero, the index is indexing
|
|
// ALL containers.
|
|
|
|
if (pIxd->uiContainerNum)
|
|
{
|
|
if (pIxd->uiContainerNum != uiContainerNum)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (pIxd->uiFlags & IXD_OFFLINE)
|
|
{
|
|
if (uiDrn > pIxd->uiLastDrnIndexed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Else index the key.
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// uiContainerNum == 0, indexing all containers
|
|
|
|
if (pIxd->uiFlags & IXD_OFFLINE)
|
|
{
|
|
if (uiContainerNum > pIxd->uiLastContainerIndexed ||
|
|
uiContainerNum == pIxd->uiLastContainerIndexed &&
|
|
uiDrn > pIxd->uiLastDrnIndexed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Else index the key.
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
// See if field path matches what is defined in the IFD.
|
|
|
|
if (!flmCheckIfdPath( pIfd, pRecord, ppPathFlds, uiLeafFieldLevel,
|
|
pvField, &pRootContext))
|
|
{
|
|
|
|
// Skip this field.
|
|
|
|
continue;
|
|
}
|
|
|
|
// Field passed the path verification. Now output the KEY.
|
|
|
|
if (pIfd->uiFlags & IFD_COMPOUND)
|
|
{
|
|
|
|
// Compound Key.
|
|
|
|
if (RC_BAD( rc = KYCmpKeyAdd2Lst( pDb, pIxd, pIfd, pvField,
|
|
pRootContext)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if (pIfd->uiFlags & IFD_CONTEXT)
|
|
{
|
|
FLMBYTE KeyBuf[4];
|
|
|
|
// Context key (tag number).
|
|
|
|
KeyBuf[0] = KY_CONTEXT_PREFIX;
|
|
f_UINT16ToBigEndian( (FLMUINT16) pRecord->getFieldID(
|
|
pvField), &KeyBuf[1]);
|
|
|
|
if (RC_BAD( rc = KYAddToKrefTbl( pDb, pIxd, uiContainerNum, pIfd,
|
|
uiAction, uiDrn, pbHadUniqueKeys, KeyBuf, KY_CONTEXT_LEN,
|
|
TRUE, FALSE, FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if ((pIfd->uiFlags & IFD_SUBSTRING) &&
|
|
(pRecord->getDataType( pvField) == FLM_TEXT_TYPE))
|
|
{
|
|
FLMBOOL bFirstSubstring = TRUE;
|
|
FLMUINT uiLanguage = pIxd->uiLanguage;
|
|
|
|
// An encrypted field, in limited mode means we use the
|
|
// encrypted data instead.
|
|
|
|
if (pRecord->isEncryptedField( pvField) && pDb->pFile->bInLimitedMode)
|
|
{
|
|
pValue = pRecord->getEncryptionDataPtr( pvField);
|
|
uiValueLen = pRecord->getEncryptedDataLength( pvField);
|
|
|
|
if (RC_BAD( rc = KYAddToKrefTbl( pDb, pIxd, uiContainerNum, pIfd,
|
|
uiAction, uiDrn, pbHadUniqueKeys, pValue, uiValueLen,
|
|
FALSE, bFirstSubstring, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pExportValue = pValue = pRecord->getDataPtr( pvField);
|
|
uiValueLen = pRecord->getDataLength( pvField);
|
|
|
|
// Loop for each word in the text field adding it to the
|
|
// table.
|
|
|
|
while (KYSubstringParse( &pValue, &uiValueLen, pIfd->uiFlags,
|
|
pIfd->uiLimit, (FLMBYTE*) pTmpKeyBuf, &uiKeyLen) == TRUE)
|
|
{
|
|
if (RC_BAD( rc = KYAddToKrefTbl( pDb, pIxd, uiContainerNum,
|
|
pIfd, uiAction, uiDrn, pbHadUniqueKeys,
|
|
(FLMBYTE *) pTmpKeyBuf, uiKeyLen, FALSE,
|
|
bFirstSubstring, FALSE)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ((uiValueLen == 1 &&
|
|
!(uiLanguage >= FLM_FIRST_DBCS_LANG &&
|
|
uiLanguage <= FLM_LAST_DBCS_LANG)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
bFirstSubstring = FALSE;
|
|
}
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
else if ((pIfd->uiFlags & IFD_EACHWORD) &&
|
|
(pRecord->getDataType( pvField) == FLM_TEXT_TYPE))
|
|
{
|
|
|
|
// An encrypted field, in limited mode means we use the
|
|
// encrypted data instead.
|
|
|
|
if (pRecord->isEncryptedField( pvField) && pDb->pFile->bInLimitedMode)
|
|
{
|
|
pValue = pRecord->getEncryptionDataPtr( pvField);
|
|
uiValueLen = pRecord->getEncryptedDataLength( pvField);
|
|
|
|
if (RC_BAD( rc = KYAddToKrefTbl( pDb, pIxd, uiContainerNum, pIfd,
|
|
uiAction, uiDrn, pbHadUniqueKeys, pValue, uiValueLen,
|
|
FALSE, FALSE, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pExportValue = pValue = pRecord->getDataPtr( pvField);
|
|
uiValueLen = pRecord->getDataLength( pvField);
|
|
|
|
// Loop for each word in the text field adding it to the
|
|
// table.
|
|
|
|
while (KYEachWordParse( &pValue, &uiValueLen, pIfd->uiLimit,
|
|
(FLMBYTE *) pTmpKeyBuf, &uiKeyLen) == TRUE)
|
|
{
|
|
if (RC_BAD( rc = KYAddToKrefTbl( pDb, pIxd, uiContainerNum,
|
|
pIfd, uiAction, uiDrn, pbHadUniqueKeys,
|
|
(FLMBYTE*) pTmpKeyBuf, uiKeyLen, FALSE, FALSE, FALSE)))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// Index field content - entire field.
|
|
|
|
FLMBOOL bEncryptedKey = FALSE;
|
|
|
|
// An encrypted field, in limited mode means we use the
|
|
// encrypted data instead.
|
|
|
|
if (pRecord->isEncryptedField( pvField) && pDb->pFile->bInLimitedMode)
|
|
{
|
|
pExportValue = pValue = pRecord->getEncryptionDataPtr( pvField);
|
|
uiValueLen = pRecord->getEncryptedDataLength( pvField);
|
|
bEncryptedKey = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pExportValue = pValue = pRecord->getDataPtr( pvField);
|
|
uiValueLen = pRecord->getDataLength( pvField);
|
|
}
|
|
|
|
if (RC_BAD( rc = KYAddToKrefTbl( pDb, pIxd, uiContainerNum, pIfd,
|
|
uiAction, uiDrn, pbHadUniqueKeys, pExportValue, uiValueLen,
|
|
FALSE, FALSE, bEncryptedKey)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Add an index key to the buffers
|
|
****************************************************************************/
|
|
RCODE KYAddToKrefTbl(
|
|
FDB * pDb,
|
|
IXD * pIxd,
|
|
FLMUINT uiContainerNum,
|
|
IFD * pIfd,
|
|
FLMUINT uiAction,
|
|
FLMUINT uiDrn,
|
|
FLMBOOL * pbHadUniqueKeys,
|
|
const FLMBYTE * pKey,
|
|
FLMUINT uiKeyLen,
|
|
FLMBOOL bAlreadyCollated,
|
|
FLMBOOL bFirstSubstring,
|
|
FLMBOOL bFldIsEncrypted)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
KREF_ENTRY * pKref;
|
|
FLMBYTE * pKrefKey;
|
|
FLMUINT uiKrefKeyLen;
|
|
FLMUINT uiSizeNeeded;
|
|
KREF_CNTRL * pKrefCntrl = &pDb->KrefCntrl;
|
|
|
|
// If the table is FULL, commit the keys or expand the table
|
|
|
|
if (pKrefCntrl->uiCount == pKrefCntrl->uiKrefTblSize)
|
|
{
|
|
FLMUINT uiAllocSize;
|
|
FLMUINT uiOrigKrefTblSize = pKrefCntrl->uiKrefTblSize;
|
|
|
|
if (pKrefCntrl->uiKrefTblSize > 0x8000 / sizeof(KREF_ENTRY *))
|
|
{
|
|
pKrefCntrl->uiKrefTblSize += 4096;
|
|
}
|
|
else
|
|
{
|
|
pKrefCntrl->uiKrefTblSize *= 2;
|
|
}
|
|
|
|
uiAllocSize = pKrefCntrl->uiKrefTblSize * sizeof(KREF_ENTRY *);
|
|
|
|
if (RC_BAD( rc = f_realloc( uiAllocSize, &pKrefCntrl->pKrefTbl)))
|
|
{
|
|
pKrefCntrl->uiKrefTblSize = uiOrigKrefTblSize;
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Get the collated key.
|
|
|
|
if (bAlreadyCollated)
|
|
{
|
|
|
|
// Compound keys are already collated.
|
|
|
|
pKrefKey = (FLMBYTE*) pKey;
|
|
uiKrefKeyLen = uiKeyLen;
|
|
}
|
|
else
|
|
{
|
|
pKrefKey = pKrefCntrl->pKrefKeyBuf;
|
|
uiKrefKeyLen = (pIxd->uiContainerNum)
|
|
? MAX_KEY_SIZ
|
|
: MAX_KEY_SIZ - getIxContainerPartLen( pIxd);
|
|
|
|
if (RC_BAD( rc = KYCollateValue( pKrefKey, &uiKrefKeyLen, pKey, uiKeyLen,
|
|
pIfd->uiFlags, pIfd->uiLimit, NULL, NULL,
|
|
(FLMUINT) ((pIxd->uiLanguage != 0xFFFF)
|
|
? pIxd->uiLanguage
|
|
: pDb->pFile->FileHdr.uiDefaultLanguage),
|
|
FALSE, bFirstSubstring, FALSE, NULL, NULL, bFldIsEncrypted)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// If indexing all containers, add the container number.
|
|
|
|
if (!pIxd->uiContainerNum)
|
|
{
|
|
appendContainerToKey( pIxd, uiContainerNum, pKrefKey, &uiKrefKeyLen);
|
|
}
|
|
|
|
// Allocate memory for the key's KREF and the key itself. We allocate
|
|
// one extra byte so we can NULL terminate the key below. The extra
|
|
// NULL character is to ensure that the compare in the qsort routine
|
|
// will work.
|
|
|
|
uiSizeNeeded = sizeof( KREF_ENTRY) + uiKrefKeyLen + 1;
|
|
|
|
if( RC_BAD( rc = pKrefCntrl->pPool->poolAlloc( uiSizeNeeded,
|
|
(void **)&pKref)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pKrefCntrl->pKrefTbl[pKrefCntrl->uiCount++] = pKref;
|
|
pKrefCntrl->uiTotalBytes += uiSizeNeeded;
|
|
|
|
// Fill in all of the fields in the KREF structure.
|
|
|
|
flmAssert( pIxd->uiIndexNum > 0 && pIxd->uiIndexNum < FLM_UNREGISTERED_TAGS);
|
|
|
|
pKref->ui16IxNum = (FLMUINT16) pIxd->uiIndexNum;
|
|
pKref->uiDrn = uiDrn;
|
|
if (uiAction & KREF_DEL_KEYS)
|
|
{
|
|
pKref->uiFlags = ((uiAction & KREF_MISSING_KEYS_OK)
|
|
? (FLMUINT) (KREF_DELETE_FLAG | KREF_MISSING_OK)
|
|
: (FLMUINT) (KREF_DELETE_FLAG));
|
|
}
|
|
else
|
|
{
|
|
pKref->uiFlags = 0;
|
|
}
|
|
|
|
if (pIxd->uiFlags & IXD_UNIQUE)
|
|
{
|
|
*pbHadUniqueKeys = TRUE;
|
|
pKref->uiFlags |= KREF_UNIQUE_KEY;
|
|
}
|
|
|
|
if (bFldIsEncrypted)
|
|
{
|
|
pKref->uiFlags |= KREF_ENCRYPTED_KEY;
|
|
}
|
|
|
|
pKref->ui16KeyLen = (FLMUINT16) uiKrefKeyLen;
|
|
pKref->uiTrnsSeq = pKrefCntrl->uiTrnsSeqCntr;
|
|
|
|
// Null terminate the key so compare in qsort will work
|
|
|
|
pKrefKey[uiKrefKeyLen++] = '\0';
|
|
|
|
// Copy the key to just after the KREF structure
|
|
|
|
f_memcpy( (FLMBYTE *) (&pKref[1]), pKrefKey, uiKrefKeyLen);
|
|
|
|
Exit:
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Encrypt a field in the pRecord.
|
|
****************************************************************************/
|
|
RCODE flmEncryptField(
|
|
FDICT * pDict,
|
|
FlmRecord * pRecord,
|
|
void * pvField,
|
|
FLMUINT uiEncId,
|
|
F_Pool * pPool)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
F_CCS * pCcs;
|
|
FLMUINT uiEncLength;
|
|
FLMBYTE * pucEncBuffer;
|
|
FLMBYTE * pucDataBuffer = NULL;
|
|
FLMUINT uiCheckLength;
|
|
void * pvMark;
|
|
#ifdef FLM_DEBUG
|
|
FLMBOOL bOk;
|
|
FLMUINT uiLoop;
|
|
#endif
|
|
|
|
pvMark = pPool->poolMark();
|
|
|
|
if (!pRecord->isEncryptedField( pvField))
|
|
{
|
|
flmAssert( 0);
|
|
rc = RC_SET( FERR_FLD_NOT_ENCRYPTED);
|
|
goto Exit;
|
|
}
|
|
|
|
pCcs = (F_CCS *) pDict->pIttTbl[uiEncId].pvItem;
|
|
|
|
flmAssert( pCcs);
|
|
|
|
uiEncLength = pRecord->getEncryptedDataLength( pvField);
|
|
|
|
if( RC_BAD( rc = pPool->poolAlloc( uiEncLength, (void **)&pucDataBuffer)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pucEncBuffer = (FLMBYTE *) pRecord->getEncryptionDataPtr( pvField);
|
|
uiCheckLength = uiEncLength;
|
|
|
|
#ifdef FLM_DEBUG
|
|
|
|
// Preset the buffer to a known value so we can check it after the
|
|
// encryption. It should NOT be the same!
|
|
|
|
f_memset( pucEncBuffer, 'B', uiEncLength);
|
|
#endif
|
|
|
|
// We copy the data into a buffer that is as large as the encrypted
|
|
// data because the encryption algorithm is expecting to get a buffer
|
|
// that does not need to be padded to the nearest 16 byte boundary.
|
|
|
|
f_memcpy( pucDataBuffer, pRecord->getDataPtr( pvField),
|
|
pRecord->getDataLength( pvField));
|
|
|
|
if (RC_BAD( rc = pCcs->encryptToStore( pucDataBuffer, uiEncLength,
|
|
pucEncBuffer, &uiCheckLength)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (uiCheckLength != uiEncLength)
|
|
{
|
|
rc = RC_SET( FERR_DATA_SIZE_MISMATCH);
|
|
goto Exit;
|
|
}
|
|
|
|
#ifdef FLM_DEBUG
|
|
bOk = FALSE;
|
|
for (uiLoop = 0; uiLoop < uiEncLength; uiLoop++)
|
|
{
|
|
if (pucEncBuffer[uiLoop] != 'B')
|
|
{
|
|
bOk = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bOk)
|
|
{
|
|
flmAssert( 0);
|
|
rc = RC_SET( FERR_DATA_ERROR);
|
|
goto Exit;
|
|
}
|
|
#endif
|
|
|
|
pRecord->setEncFlags( pvField,
|
|
FLD_HAVE_DECRYPTED_DATA | FLD_HAVE_ENCRYPTED_DATA);
|
|
|
|
Exit:
|
|
|
|
pPool->poolReset( pvMark);
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Decrypt an encrypted field in the pRecord.
|
|
****************************************************************************/
|
|
RCODE flmDecryptField(
|
|
FDICT * pDict,
|
|
FlmRecord * pRecord,
|
|
void * pvField,
|
|
FLMUINT uiEncId,
|
|
F_Pool * pPool)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
F_CCS * pCcs;
|
|
FLMUINT uiEncLength;
|
|
FLMBYTE * pucEncBuffer = NULL;
|
|
FLMBYTE * pucDataBuffer = NULL;
|
|
FLMUINT uiCheckLength;
|
|
void * pvMark = NULL;
|
|
|
|
pvMark = pPool->poolMark();
|
|
|
|
if (!pRecord->isEncryptedField( pvField))
|
|
{
|
|
flmAssert( 0);
|
|
rc = RC_SET( FERR_FLD_NOT_ENCRYPTED);
|
|
goto Exit;
|
|
}
|
|
|
|
pCcs = (F_CCS*) pDict->pIttTbl[uiEncId].pvItem;
|
|
|
|
flmAssert( pCcs);
|
|
|
|
uiEncLength = pRecord->getEncryptedDataLength( pvField);
|
|
|
|
if( RC_BAD( rc = pPool->poolAlloc( uiEncLength, (void **)&pucDataBuffer)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pucEncBuffer = (FLMBYTE *) pRecord->getEncryptionDataPtr( pvField);
|
|
uiCheckLength = uiEncLength;
|
|
|
|
if (RC_BAD( rc = pCcs->decryptFromStore( pucEncBuffer, uiEncLength,
|
|
pucDataBuffer, &uiCheckLength)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (uiCheckLength != uiEncLength)
|
|
{
|
|
rc = RC_SET( FERR_DATA_SIZE_MISMATCH);
|
|
goto Exit;
|
|
}
|
|
|
|
f_memcpy( (void *)pRecord->getDataPtr( pvField), pucDataBuffer,
|
|
pRecord->getDataLength( pvField));
|
|
|
|
pRecord->setEncFlags( pvField,
|
|
FLD_HAVE_DECRYPTED_DATA | FLD_HAVE_ENCRYPTED_DATA);
|
|
|
|
Exit:
|
|
|
|
pPool->poolReset( pvMark);
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Substring-ize the string in a node.
|
|
****************************************************************************/
|
|
FLMBOOL KYSubstringParse(
|
|
const FLMBYTE ** ppText, // [in][out] points to text
|
|
FLMUINT * puiTextLen, // [in][out] length of text
|
|
FLMUINT uiIfdFlags, // [in] flags
|
|
FLMUINT uiLimitParm, // [in] Max characters
|
|
FLMBYTE * pKeyBuf, // [out] key buffer to fill
|
|
FLMUINT * puiKeyLen) // [out] returns length
|
|
{
|
|
const FLMBYTE * pText = *ppText;
|
|
FLMUINT uiLen = *puiTextLen;
|
|
FLMUINT uiWordLen = 0;
|
|
FLMUINT uiLimit = uiLimitParm ? uiLimitParm : IFD_DEFAULT_SUBSTRING_LIMIT;
|
|
FLMUINT uiFlags = 0;
|
|
FLMUINT uiLeadingSpace = FLM_COMP_NO_WHITESPACE;
|
|
FLMBOOL bIgnoreSpaceDefault = (uiIfdFlags & IFD_NO_SPACE) ? TRUE : FALSE;
|
|
FLMBOOL bIgnoreSpace = TRUE;
|
|
FLMBOOL bIgnoreDash = (uiIfdFlags & IFD_NO_DASH) ? TRUE : FALSE;
|
|
FLMBOOL bMinSpaces = (uiIfdFlags & (IFD_MIN_SPACES | IFD_NO_SPACE)) ? TRUE : FALSE;
|
|
FLMBOOL bNoUnderscore = (uiIfdFlags & IFD_NO_UNDERSCORE) ? TRUE : FALSE;
|
|
FLMBOOL bFirstCharacter = TRUE;
|
|
|
|
// Set uiFlags
|
|
|
|
if (bIgnoreSpaceDefault)
|
|
{
|
|
uiFlags |= FLM_COMP_NO_WHITESPACE;
|
|
}
|
|
|
|
if (bIgnoreDash)
|
|
{
|
|
uiFlags |= FLM_COMP_NO_DASHES;
|
|
}
|
|
|
|
if (bNoUnderscore)
|
|
{
|
|
uiFlags |= FLM_COMP_NO_UNDERSCORES;
|
|
}
|
|
|
|
if (uiIfdFlags & IFD_MIN_SPACES)
|
|
{
|
|
uiFlags |= FLM_COMP_COMPRESS_WHITESPACE;
|
|
}
|
|
|
|
// The limit must return one more than requested in order for the text
|
|
// to collation routine to set the truncated flag.
|
|
|
|
uiLimit++;
|
|
|
|
while (uiLen && uiLimit--)
|
|
{
|
|
FLMBYTE ch = *pText;
|
|
FLMUINT16 ui16WPValue;
|
|
FLMUNICODE ui16UniValue;
|
|
FLMUINT uiCharLen;
|
|
|
|
if ((ch & ASCII_CHAR_MASK) == ASCII_CHAR_CODE)
|
|
{
|
|
if (ch == ASCII_UNDERSCORE && bNoUnderscore)
|
|
{
|
|
ch = ASCII_SPACE;
|
|
}
|
|
|
|
if (ch == ASCII_SPACE && bMinSpaces)
|
|
{
|
|
if (!bIgnoreSpace)
|
|
{
|
|
pKeyBuf[uiWordLen++] = ASCII_SPACE;
|
|
}
|
|
|
|
bIgnoreSpace = TRUE;
|
|
pText++;
|
|
uiLen--;
|
|
continue;
|
|
}
|
|
|
|
ui16WPValue = (FLMUINT16) ch;
|
|
uiCharLen = 1;
|
|
}
|
|
else
|
|
{
|
|
if ((uiCharLen = flmTextGetValue( pText, uiLen, NULL,
|
|
uiFlags | uiLeadingSpace, &ui16WPValue, &ui16UniValue)) == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
flmAssert( uiCharLen <= uiLen);
|
|
}
|
|
|
|
uiLeadingSpace = 0;
|
|
bIgnoreSpace = bIgnoreSpaceDefault;
|
|
uiLen -= uiCharLen;
|
|
while (uiCharLen--)
|
|
{
|
|
pKeyBuf[uiWordLen++] = *pText++;
|
|
}
|
|
|
|
// If on the first word position to start on next character for the
|
|
// next call.
|
|
|
|
if (bFirstCharacter)
|
|
{
|
|
bFirstCharacter = FALSE;
|
|
|
|
// First character - set return value.
|
|
|
|
*ppText = pText;
|
|
*puiTextLen = uiLen;
|
|
}
|
|
}
|
|
|
|
pKeyBuf[uiWordLen] = '\0';
|
|
|
|
// Case of all spaces - the FALSE will trigger indexing is done.
|
|
|
|
*puiKeyLen = (FLMUINT) uiWordLen;
|
|
return ((uiWordLen) ? TRUE : FALSE);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Keyword-ize the information in a node - node is assumed to be a
|
|
TEXT node.
|
|
****************************************************************************/
|
|
FLMBOOL KYEachWordParse(
|
|
const FLMBYTE ** pText,
|
|
FLMUINT * puiTextLen,
|
|
FLMUINT uiLimitParm, // [in] Max characters
|
|
FLMBYTE * pKeyBuf, // [out] Buffer of at least MAX_KEY_SIZ
|
|
FLMUINT * puiKeyLen)
|
|
{
|
|
const FLMBYTE * pKey = NULL;
|
|
const FLMBYTE * pTmpKey;
|
|
FLMUINT uiLimit = uiLimitParm ? uiLimitParm : IFD_DEFAULT_SUBSTRING_LIMIT;
|
|
FLMUINT uiLen;
|
|
FLMUINT uiBytesProcessed = 0;
|
|
FLMBOOL bSkippingDelim = TRUE;
|
|
FLMBOOL bHaveWord = FALSE;
|
|
FLMUINT uiWordLen = 0;
|
|
FLMUINT16 ui16WPValue;
|
|
FLMUNICODE ui16UniValue;
|
|
FLMUINT uiCharLen;
|
|
FLMUINT uiType;
|
|
|
|
uiLen = *puiTextLen;
|
|
pTmpKey = *pText;
|
|
|
|
while ((uiBytesProcessed < uiLen) && (!bHaveWord) && uiLimit)
|
|
{
|
|
uiCharLen = flmTextGetCharType( pTmpKey, uiLen, &ui16WPValue,
|
|
&ui16UniValue, &uiType);
|
|
|
|
// Determine how to handle what we got.
|
|
|
|
if (bSkippingDelim)
|
|
{
|
|
|
|
// If we were skipping delimiters, and we run into a
|
|
// non-delimiter character, set the bSkippingDelim flag to FALSE
|
|
// to indicate the beginning of a word.
|
|
|
|
if (uiType & SDWD_CHR)
|
|
{
|
|
pKey = pTmpKey;
|
|
uiWordLen = uiCharLen;
|
|
bSkippingDelim = FALSE;
|
|
uiLimit--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// If we were NOT skipping delimiters, and we run into a
|
|
// delimiter output the word.
|
|
|
|
if (uiType & (DELI_CHR | WDJN_CHR))
|
|
{
|
|
bHaveWord = TRUE;
|
|
}
|
|
else
|
|
{
|
|
uiWordLen += uiCharLen;
|
|
uiLimit--;
|
|
}
|
|
}
|
|
|
|
// Increment str to skip past what we are pointing at.
|
|
|
|
pTmpKey += uiCharLen;
|
|
uiBytesProcessed += uiCharLen;
|
|
}
|
|
|
|
*pText = pTmpKey;
|
|
*puiTextLen -= uiBytesProcessed;
|
|
|
|
// Return the word, if any.
|
|
|
|
if (uiWordLen)
|
|
{
|
|
*puiKeyLen = uiWordLen;
|
|
f_memcpy( pKeyBuf, pKey, uiWordLen);
|
|
}
|
|
|
|
return ((uiWordLen) ? TRUE : FALSE);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Setup routine for the KREF_CNTRL structure for record updates.
|
|
Will check to see if all structures, buffers and memory pools
|
|
need to be allocated: Kref key buffer, CDL table, KrefTbl and pool.
|
|
The goal is to have only one allocation for most small transactions.
|
|
As of Nov 96, each DB will have its own KREF_CNTRL struture so the
|
|
session temp pool does not have to be used. This means that the
|
|
CDL and cmpKeys arrays do not have to be allocated for each
|
|
record operation (like we did in the session pool).
|
|
****************************************************************************/
|
|
RCODE KrefCntrlCheck(
|
|
FDB * pDb)
|
|
{
|
|
RCODE rc = FERR_OK; // Set for cleaner code.
|
|
KREF_CNTRL * pKrefCntrl;
|
|
|
|
pKrefCntrl = &pDb->KrefCntrl;
|
|
|
|
/* Check if we need to flush between the records and not during
|
|
the processing of a record. This simplifies how we reuse the memory.
|
|
*/
|
|
|
|
if( pKrefCntrl->bKrefSetup)
|
|
{
|
|
if( (pKrefCntrl->uiCount >= KREF_TBL_THRESHOLD)
|
|
|| (pKrefCntrl->uiTotalBytes >= KREF_TOTAL_BYTES_THRESHOLD))
|
|
|
|
{
|
|
if( RC_BAD( rc = KYKeysCommit( pDb, FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FLMUINT uiKrefTblSize = KREF_TBL_SIZE * sizeof(KREF_ENTRY *);
|
|
FLMUINT uiCDLSize = pDb->pDict->uiIfdCnt * sizeof( CDL *);
|
|
FLMUINT uiIxdSize = pDb->pDict->uiIxdCnt;
|
|
FLMUINT uiKeyBufSize = MAX_KEY_SIZ + 8;
|
|
|
|
f_memset( pKrefCntrl, 0, sizeof( KREF_CNTRL));
|
|
pKrefCntrl->bKrefSetup = TRUE;
|
|
if (pDb->uiTransType == FLM_UPDATE_TRANS)
|
|
{
|
|
pKrefCntrl->pPool = &pDb->pFile->krefPool;
|
|
pKrefCntrl->bReusePool = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pKrefCntrl->pPool = &pDb->tmpKrefPool;
|
|
pKrefCntrl->bReusePool = FALSE;
|
|
}
|
|
|
|
if (pKrefCntrl->bReusePool)
|
|
{
|
|
pKrefCntrl->pPool->poolReset();
|
|
}
|
|
else
|
|
{
|
|
pKrefCntrl->pPool->poolInit( KREF_POOL_BLOCK_SIZE);
|
|
}
|
|
|
|
if( RC_BAD( rc = f_alloc( uiKrefTblSize,
|
|
&pKrefCntrl->pKrefTbl))
|
|
|| (uiCDLSize && RC_BAD( rc = f_calloc( uiCDLSize,
|
|
&pKrefCntrl->ppCdlTbl)))
|
|
|| (uiIxdSize && RC_BAD( rc = f_calloc( uiIxdSize,
|
|
&pKrefCntrl->pIxHasCmpKeys)))
|
|
|| RC_BAD( rc = f_calloc( uiKeyBufSize,
|
|
&pKrefCntrl->pKrefKeyBuf)))
|
|
{
|
|
KrefCntrlFree( pDb);
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
pKrefCntrl->uiKrefTblSize = KREF_TBL_SIZE;
|
|
}
|
|
|
|
pKrefCntrl->pReset = pKrefCntrl->pPool->poolMark();
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Resets or frees the memory associated with the KREF.
|
|
****************************************************************************/
|
|
void KrefCntrlFree(
|
|
FDB * pDb)
|
|
{
|
|
KREF_CNTRL * pKrefCntrl = &pDb->KrefCntrl;
|
|
|
|
if( pKrefCntrl->bKrefSetup)
|
|
{
|
|
if (pKrefCntrl->bReusePool)
|
|
{
|
|
pKrefCntrl->pPool->poolReset();
|
|
}
|
|
else
|
|
{
|
|
pKrefCntrl->pPool->poolFree();
|
|
}
|
|
|
|
if( pKrefCntrl->pKrefTbl)
|
|
{
|
|
f_free( &pKrefCntrl->pKrefTbl);
|
|
}
|
|
|
|
if( pKrefCntrl->ppCdlTbl)
|
|
{
|
|
f_free( &pKrefCntrl->ppCdlTbl);
|
|
}
|
|
|
|
if( pKrefCntrl->pIxHasCmpKeys)
|
|
{
|
|
f_free( &pKrefCntrl->pIxHasCmpKeys);
|
|
}
|
|
|
|
if( pKrefCntrl->pKrefKeyBuf)
|
|
{
|
|
f_free( &pKrefCntrl->pKrefKeyBuf);
|
|
}
|
|
|
|
// Just set everyone back to zero.
|
|
|
|
f_memset( pKrefCntrl, 0, sizeof(KREF_CNTRL));
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Checks if the current database has any UNIQUE indexes that need to
|
|
checked. Also does duplicate processing for the record.
|
|
****************************************************************************/
|
|
RCODE KYProcessDupKeys(
|
|
FDB * pDb,
|
|
FLMBOOL bHadUniqueKeys)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
KREF_CNTRL * pKrefCntrl = &pDb->KrefCntrl;
|
|
FLMUINT uiCurRecKrefCnt;
|
|
|
|
pKrefCntrl->uiTrnsSeqCntr++;
|
|
|
|
// Sort and remove duplicates from the list of this record.
|
|
|
|
uiCurRecKrefCnt = pKrefCntrl->uiCount - pKrefCntrl->uiLastRecEnd;
|
|
|
|
if (uiCurRecKrefCnt > 1)
|
|
{
|
|
FLMUINT uiSortFlags = KY_DUP_CHK_SRT;
|
|
|
|
if (RC_BAD( rc = _KrefQuickSort( &uiSortFlags,
|
|
&pKrefCntrl->pKrefTbl[pKrefCntrl->uiLastRecEnd], 0,
|
|
uiCurRecKrefCnt - 1)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Found any duplicates?
|
|
|
|
if (uiSortFlags & KY_DUPS_FOUND)
|
|
{
|
|
if (RC_BAD( rc = _KrefKillDups( &uiSortFlags,
|
|
&pKrefCntrl->pKrefTbl[pKrefCntrl->uiLastRecEnd],
|
|
&uiCurRecKrefCnt)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pKrefCntrl->uiCount = pKrefCntrl->uiLastRecEnd + uiCurRecKrefCnt;
|
|
}
|
|
}
|
|
|
|
if (bHadUniqueKeys)
|
|
{
|
|
|
|
// Now check the keys for uniquness in table, and database.
|
|
|
|
if (RC_BAD( rc = KYAddUniqueKeys( pDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Remove anything that was put into the KREF table by the current
|
|
record update operation.
|
|
****************************************************************************/
|
|
void KYAbortCurrentRecord(
|
|
FDB * pDb)
|
|
{
|
|
flmAssert( pDb->KrefCntrl.bKrefSetup);
|
|
|
|
// Reset the CDL and pIxHasCmpKeys tables
|
|
|
|
if (pDb->pDict->uiIfdCnt)
|
|
{
|
|
f_memset( pDb->KrefCntrl.ppCdlTbl, 0,
|
|
pDb->pDict->uiIfdCnt * sizeof(CDL *));
|
|
}
|
|
|
|
if (pDb->pDict->uiIxdCnt)
|
|
{
|
|
f_memset( pDb->KrefCntrl.pIxHasCmpKeys, 0, pDb->pDict->uiIxdCnt);
|
|
}
|
|
|
|
pDb->KrefCntrl.uiCount = pDb->KrefCntrl.uiLastRecEnd;
|
|
pDb->KrefCntrl.pPool->poolReset( pDb->KrefCntrl.pReset);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Commit (write out) all reference lists from the current pDb. Will
|
|
take care of optimially freeing or resetting memory.
|
|
****************************************************************************/
|
|
RCODE KYKeysCommit(
|
|
FDB * pDb,
|
|
FLMBOOL bCommittingTrans)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
KREF_CNTRL * pKrefCntrl = &pDb->KrefCntrl;
|
|
|
|
// If KrefCntrl has not been initialized, there is no work to do.
|
|
|
|
if (pKrefCntrl->bKrefSetup)
|
|
{
|
|
LFILE * pLFile = NULL;
|
|
FLMUINT uiTotal = pKrefCntrl->uiLastRecEnd;
|
|
KREF_ENTRY * pKref;
|
|
KREF_ENTRY ** pKrefTbl = pKrefCntrl->pKrefTbl;
|
|
FLMUINT uiKrefNum;
|
|
FLMUINT uiLastIxNum;
|
|
|
|
// We should not have reached this point if bAbortTrans is TRUE
|
|
|
|
flmAssert( RC_OK( pDb->AbortRc));
|
|
|
|
// uiTotal and uiLastRecEnd must be the same at this point. If not,
|
|
// we have a bug.
|
|
|
|
flmAssert( uiTotal == pKrefCntrl->uiLastRecEnd);
|
|
|
|
// Sort the KREF table, if it contains more than one record and
|
|
// key. This will sort all keys from the same index the same.
|
|
|
|
if ((uiTotal > 1) && (pKrefCntrl->uiTrnsSeqCntr > 1))
|
|
{
|
|
FLMUINT uiQsortFlags = KY_FINAL_SRT;
|
|
|
|
if (RC_BAD( rc = _KrefQuickSort( &uiQsortFlags,
|
|
pKrefTbl, 0, uiTotal - 1)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
uiLastIxNum = 0;
|
|
|
|
// Loop through the KREF table outputting all keys
|
|
|
|
for (uiKrefNum = 0; uiKrefNum < uiTotal; uiKrefNum++)
|
|
{
|
|
pKref = pKrefTbl[uiKrefNum];
|
|
|
|
// See if the LFILE changed
|
|
|
|
flmAssert( pKref->ui16IxNum > 0 &&
|
|
pKref->ui16IxNum < FLM_UNREGISTERED_TAGS);
|
|
|
|
if (pKref->ui16IxNum != uiLastIxNum)
|
|
{
|
|
uiLastIxNum = pKref->ui16IxNum;
|
|
if (RC_BAD( rc = fdictGetIndex( pDb->pDict,
|
|
pDb->pFile->bInLimitedMode, uiLastIxNum, &pLFile, NULL,
|
|
TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Flush the key to the index
|
|
|
|
if (RC_BAD( rc = FSRefUpdate( pDb, pLFile, pKref)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (bCommittingTrans)
|
|
{
|
|
KrefCntrlFree( pDb);
|
|
}
|
|
else
|
|
{
|
|
|
|
// Empty the table out so we can add more keys in this trans.
|
|
|
|
pKrefCntrl->pPool->poolReset();
|
|
pKrefCntrl->uiCount = 0;
|
|
pKrefCntrl->uiTotalBytes = 0;
|
|
pKrefCntrl->uiLastRecEnd = 0;
|
|
pKrefCntrl->uiTrnsSeqCntr = 0;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Adds all unique key values. Backs out on any unique error so that
|
|
the transaction may continue.
|
|
Note: All duplicates have been removed as well as matching keys.
|
|
****************************************************************************/
|
|
FSTATIC RCODE KYAddUniqueKeys(
|
|
FDB * pDb)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
KREF_CNTRL * pKrefCntrl = &pDb->KrefCntrl;
|
|
KREF_ENTRY ** pKrefTbl = pKrefCntrl->pKrefTbl;
|
|
KREF_ENTRY * pKref;
|
|
FLMUINT uiCurKrefNum;
|
|
FLMUINT uiPrevKrefNum;
|
|
FLMUINT uiTargetCount;
|
|
FLMUINT uiLastIxNum;
|
|
LFILE * pLFile;
|
|
FLMBOOL bUniqueErrorHit = FALSE;
|
|
|
|
// Unique indexes can't be built in the background
|
|
|
|
flmAssert( !(pDb->uiFlags & FDB_BACKGROUND_INDEXING));
|
|
|
|
// Start at the first key for this current record checking for all
|
|
// keys that belong to a unique index. We must keep all keys around
|
|
// until the last key is added/delete so that we can back out all of
|
|
// the changes on a unique error.
|
|
|
|
for (uiCurKrefNum = pKrefCntrl->uiLastRecEnd, uiLastIxNum = 0,
|
|
uiTargetCount = pKrefCntrl->uiCount; uiCurKrefNum < uiTargetCount;)
|
|
{
|
|
pKref = pKrefTbl[uiCurKrefNum];
|
|
|
|
if (pKref->uiFlags & KREF_UNIQUE_KEY)
|
|
{
|
|
flmAssert( pKref->ui16IxNum > 0 &&
|
|
pKref->ui16IxNum < FLM_UNREGISTERED_TAGS);
|
|
|
|
if (pKref->ui16IxNum != uiLastIxNum)
|
|
{
|
|
uiLastIxNum = pKref->ui16IxNum;
|
|
if (RC_BAD( rc = fdictGetIndex( pDb->pDict,
|
|
pDb->pFile->bInLimitedMode, uiLastIxNum, &pLFile, NULL)))
|
|
{
|
|
|
|
// Return the index offline error - should not happen
|
|
|
|
flmAssert( rc != FERR_INDEX_OFFLINE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Flush the key to the index.
|
|
|
|
if (RC_BAD( rc = FSRefUpdate( pDb, pLFile, pKref)))
|
|
{
|
|
pDb->Diag.uiInfoFlags |= FLM_DIAG_INDEX_NUM;
|
|
pDb->Diag.uiIndexNum = pKref->ui16IxNum;
|
|
|
|
// Check only for FERR_NOT_UNIQUE
|
|
|
|
if (rc != FERR_NOT_UNIQUE)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
bUniqueErrorHit = TRUE;
|
|
|
|
// Cycle through again backing out all keys.
|
|
|
|
uiTargetCount = uiCurKrefNum;
|
|
uiCurKrefNum = pKrefCntrl->uiLastRecEnd;
|
|
|
|
// Make sure uiCurKrefNum is NOT incremented at the top of
|
|
// loop.
|
|
|
|
continue;
|
|
}
|
|
|
|
// Toggle the delete flag so on unique error we can back out.
|
|
// This sets the ADD to DELETE and the DELETE to ADD (0)
|
|
|
|
pKref->uiFlags ^= KREF_DELETE_FLAG;
|
|
}
|
|
|
|
uiCurKrefNum++;
|
|
}
|
|
|
|
if (bUniqueErrorHit)
|
|
{
|
|
rc = RC_SET( FERR_NOT_UNIQUE);
|
|
pKrefCntrl->uiCount = pKrefCntrl->uiLastRecEnd;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Move every key down removing the processed keys.
|
|
|
|
for (uiCurKrefNum = uiPrevKrefNum = pKrefCntrl->uiLastRecEnd,
|
|
uiTargetCount = pKrefCntrl->uiCount;
|
|
uiCurKrefNum < uiTargetCount; uiCurKrefNum++)
|
|
{
|
|
pKref = pKrefTbl[uiCurKrefNum];
|
|
|
|
if (!(pKref->uiFlags & KREF_UNIQUE_KEY))
|
|
{
|
|
pKrefTbl[uiPrevKrefNum++] = pKrefTbl[uiCurKrefNum];
|
|
}
|
|
}
|
|
|
|
pKrefCntrl->uiCount = uiPrevKrefNum;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Compare function used to compare two keys. The compare is different
|
|
depending on the sort pass this is on.
|
|
****************************************************************************/
|
|
FSTATIC FLMINT _KrefCompare(
|
|
FLMUINT * puiQsortFlags,
|
|
KREF_ENTRY * pKrefA,
|
|
KREF_ENTRY * pKrefB)
|
|
{
|
|
FLMUINT uiMinLen;
|
|
FLMINT iCompare;
|
|
|
|
// Compare (SORT1) #1, (SORT2) #2 - Index Number.
|
|
|
|
if ((iCompare = ((FLMINT) pKrefA->ui16IxNum) -
|
|
((FLMINT) pKrefB->ui16IxNum)) != 0)
|
|
{
|
|
return (iCompare);
|
|
}
|
|
|
|
// Compare (SORT1) #2, (SORT2) #3: KEY - including NULL character at
|
|
// end.
|
|
|
|
// Comparing the NULL character advoids checking the key length.
|
|
|
|
// VISIT: There could be a BUG where key length should be checked, but
|
|
// it has to do with not storing all compound key pieces in the key.
|
|
|
|
uiMinLen = f_min( pKrefA->ui16KeyLen, pKrefB->ui16KeyLen) + 1;
|
|
if ((iCompare = f_memcmp( &pKrefA[1], &pKrefB[1], uiMinLen)) == 0)
|
|
{
|
|
if (*puiQsortFlags & KY_FINAL_SRT)
|
|
{
|
|
|
|
// Compare (SORT2) The DRN so we load by low DRN to high DRN.
|
|
|
|
if (pKrefA->uiDrn < pKrefB->uiDrn)
|
|
{
|
|
return (-1);
|
|
}
|
|
else if (pKrefA->uiDrn > pKrefB->uiDrn)
|
|
{
|
|
return (1);
|
|
}
|
|
|
|
// Compare (SORT2) Sequence number, so operations occur in
|
|
// correct order. - this will ALWAYS set iCompare to -1 or 1. It
|
|
// is only possible to have different operations here like ADD -
|
|
// DELETE - ADD - DELETE when sorted by uiTrnsSeq. This is why we
|
|
// will set KY_DUPS_FOUND to get rid of duplicates.
|
|
|
|
iCompare = ((FLMINT) pKrefA->uiTrnsSeq) - ((FLMINT) pKrefB->uiTrnsSeq);
|
|
}
|
|
else
|
|
{
|
|
|
|
// Compare (SORT1) Operation Flag, Delete or Add.
|
|
|
|
*puiQsortFlags |= KY_DUPS_FOUND;
|
|
|
|
// Sort so the delete elements are first.
|
|
|
|
if ((iCompare = ((FLMINT) (pKrefB->uiFlags & KREF_DELETE_FLAG)) -
|
|
((FLMINT) (pKrefA->uiFlags & KREF_DELETE_FLAG))) == 0)
|
|
{
|
|
|
|
// Exact duplicate - will remove later
|
|
|
|
pKrefA->uiFlags |= KREF_EQUAL_FLAG;
|
|
pKrefB->uiFlags |= KREF_EQUAL_FLAG;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Data is same but different operation, (delete then an add).
|
|
|
|
pKrefA->uiFlags |= KREF_IGNORE_FLAG;
|
|
pKrefB->uiFlags |= KREF_IGNORE_FLAG;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (iCompare);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Quick sort an array of KREF_ENTRY * values.
|
|
****************************************************************************/
|
|
FSTATIC RCODE _KrefQuickSort(
|
|
FLMUINT * puiQsortFlags,
|
|
KREF_ENTRY ** pEntryTbl,
|
|
FLMUINT uiLowerBounds,
|
|
FLMUINT uiUpperBounds)
|
|
{
|
|
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 (;;)
|
|
{
|
|
while( (uiLBPos == uiMIDPos) || ((iCompare = _KrefCompare( puiQsortFlags,
|
|
pEntryTbl[uiLBPos], pCurEntry)) < 0))
|
|
{
|
|
if (uiLBPos >= uiUpperBounds)
|
|
{
|
|
break;
|
|
}
|
|
|
|
uiLBPos++;
|
|
}
|
|
|
|
while ((uiUBPos == uiMIDPos) || (((iCompare = _KrefCompare(
|
|
puiQsortFlags, pCurEntry, pEntryTbl[uiUBPos])) < 0)))
|
|
{
|
|
if (!uiUBPos)
|
|
{
|
|
break;
|
|
}
|
|
|
|
uiUBPos--;
|
|
}
|
|
|
|
if (uiLBPos < uiUBPos)
|
|
{
|
|
|
|
// Interchange [uiLBPos] with [uiUBPos].
|
|
|
|
KY_SWAP( pEntryTbl, uiLBPos, uiUBPos);
|
|
uiLBPos++;
|
|
uiUBPos--;
|
|
}
|
|
else
|
|
{
|
|
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
|
|
: 0;
|
|
|
|
uiRightItems = (uiMIDPos + 1 < uiUpperBounds)
|
|
? uiUpperBounds - uiMIDPos
|
|
: 0;
|
|
|
|
if (uiLeftItems < uiRightItems)
|
|
{
|
|
|
|
// Recurse on the LEFT side and goto the top on the RIGHT side.
|
|
|
|
if (uiLeftItems)
|
|
{
|
|
(void) _KrefQuickSort( puiQsortFlags, pEntryTbl, uiLowerBounds,
|
|
uiMIDPos - 1);
|
|
}
|
|
|
|
uiLowerBounds = uiMIDPos + 1;
|
|
goto Iterate_Larger_Half;
|
|
}
|
|
else if (uiLeftItems)
|
|
{
|
|
|
|
// Recurse on the RIGHT side and goto the top for the LEFT side.
|
|
|
|
if (uiRightItems)
|
|
{
|
|
(void) _KrefQuickSort( puiQsortFlags, pEntryTbl, uiMIDPos + 1,
|
|
uiUpperBounds);
|
|
}
|
|
|
|
uiUpperBounds = uiMIDPos - 1;
|
|
goto Iterate_Larger_Half;
|
|
}
|
|
|
|
return (FERR_OK);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Kill all duplicate references out of the kref list
|
|
Note: This will ONLY work if EVERY kref has been compared to its neighbor.
|
|
We may have to compare every neighbor again if the new quick
|
|
sort doesn't work.
|
|
****************************************************************************/
|
|
FSTATIC RCODE _KrefKillDups(
|
|
FLMUINT * puiQsortFlags,
|
|
KREF_ENTRY ** pKrefTbl,
|
|
FLMUINT* puiKrefTotalRV)
|
|
{
|
|
FLMUINT uiTotal = (*puiKrefTotalRV);
|
|
FLMUINT uiCurKrefNum;
|
|
KREF_ENTRY * pCurKref;
|
|
FLMUINT uiLastUniqueKrefNum = 0;
|
|
|
|
for (uiCurKrefNum = 1; uiCurKrefNum < uiTotal; uiCurKrefNum++)
|
|
{
|
|
pCurKref = pKrefTbl[uiCurKrefNum];
|
|
|
|
// If the current KREF equals the last unique one, we can remove it
|
|
// from the list by skipping the current entry. To check if they are
|
|
// equal, first look at the KREF_EQUAL_FLAGs on both of them. If
|
|
// both KREFs have this flag set, we still have to call the compare
|
|
// routine. The flags could have been set for two pairs of different
|
|
// keys - such as A, A, B, B. In this sequence of keys, all four
|
|
// KREFs would have the flag set, but the 2nd "A" is not equal to
|
|
// the 1st "B" - thus the need for the call to krefCompare to
|
|
// confirm that the keys are really equal.
|
|
|
|
if ((pKrefTbl[uiLastUniqueKrefNum]->uiFlags & KREF_EQUAL_FLAG) &&
|
|
(pCurKref->uiFlags & KREF_EQUAL_FLAG) &&
|
|
(_KrefCompare( puiQsortFlags,
|
|
pKrefTbl[uiLastUniqueKrefNum], pCurKref) == 0))
|
|
{
|
|
|
|
// If the current KREF had it's ignore flag set, propagate that
|
|
// to the last unique KREF also and remove the current key. This
|
|
// will remove all but the first duplicate key. This is possible
|
|
// because quick sort may not compare every item.
|
|
|
|
if (pCurKref->uiFlags & KREF_IGNORE_FLAG)
|
|
{
|
|
pKrefTbl[uiLastUniqueKrefNum]->uiFlags |= KREF_IGNORE_FLAG;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// Increment to the next slot if we like this kref.
|
|
|
|
if (!(pKrefTbl[uiLastUniqueKrefNum]->uiFlags & KREF_IGNORE_FLAG))
|
|
{
|
|
uiLastUniqueKrefNum++;
|
|
}
|
|
|
|
// Move the item to the current location.
|
|
|
|
pKrefTbl[uiLastUniqueKrefNum] = pCurKref;
|
|
}
|
|
}
|
|
|
|
if (!(pKrefTbl[uiLastUniqueKrefNum]->uiFlags & KREF_IGNORE_FLAG))
|
|
{
|
|
uiLastUniqueKrefNum++;
|
|
}
|
|
|
|
*puiKrefTotalRV = uiLastUniqueKrefNum;
|
|
return (FERR_OK);
|
|
}
|