//------------------------------------------------------------------------------ // 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$ //------------------------------------------------------------------------------ #include "flaimsys.h" #define KY_SWAP( pKrefTbl, leftP, rightP) \ pTempKref = pKrefTbl [leftP]; \ pKrefTbl [leftP] = pKrefTbl [rightP]; \ pKrefTbl [rightP] = pTempKref FSTATIC RCODE ixKeyGetRowId( F_INDEX * pIndex, const FLMBYTE * pucKey, const FLMBYTE * pucKeyEnd, FLMUINT uiKeyComponent, FLMUINT64 * pui64RowId); FSTATIC RCODE ixKeyGetUTF8( F_Db * pDb, FLMUINT uiTableNum, ICD * pIcd, F_Row * pOldRow, FLMUINT64 ui64RowId, F_DataVector * pSearchKey, FLMUINT uiKeyComponent, F_DynaBuf * pDynaBuf); FSTATIC RCODE ixKeyGetBinary( F_Db * pDb, FLMUINT uiTableNum, ICD * pIcd, F_Row * pOldRow, FLMUINT64 ui64RowId, F_DataVector * pSearchKey, FLMUINT uiKeyComponent, F_DynaBuf * pDynaBuf); FSTATIC RCODE krefQuickSort( F_Db * pDb, F_INDEX * pIndex, KREF_ENTRY ** pEntryTbl, FLMUINT uiLowerBounds, FLMUINT uiUpperBounds); FSTATIC RCODE krefKillDups( F_Db * pDb, F_INDEX * pIndex, KREF_ENTRY ** pKrefTbl, FLMUINT * puiKrefTotal); /*************************************************************************** Desc: Get the row ID from a key. *****************************************************************************/ FSTATIC RCODE ixKeyGetRowId( F_INDEX * pIndex, const FLMBYTE * pucKey, const FLMBYTE * pucKeyEnd, FLMUINT uiKeyComponent, FLMUINT64 * pui64RowId) { RCODE rc = NE_SFLM_OK; FLMUINT uiComponent; FLMUINT uiComponentLen; // Skip past all of the remaining key components so we can get to the // row ID. 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 < pIndex->uiNumKeyComponents) { uiComponentLen = getKeyComponentLength( pucKey); if (uiComponentLen != KEY_HIGH_VALUE && uiComponentLen != KEY_LOW_VALUE) { pucKey += (uiComponentLen + 2); } else { pucKey += 2; } uiComponent++; } // See if there is a row ID in the key. A 0xFF could be present if // we have set a "high" row ID. if (pucKey >= pucKeyEnd || *pucKey == 0xFF) { *pui64RowId = 0; goto Exit; } // At this point, we better have a row ID. if (RC_BAD( rc = f_decodeSEN64( &pucKey, pucKeyEnd, pui64RowId))) { 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 UTF8 value for a particular key component. *****************************************************************************/ FSTATIC RCODE ixKeyGetUTF8( F_Db * pDb, FLMUINT uiTableNum, ICD * pIcd, F_Row * pOldRow, FLMUINT64 ui64RowId, F_DataVector * pSearchKey, FLMUINT uiKeyComponent, F_DynaBuf * pDynaBuf) { RCODE rc = NE_SFLM_OK; FLMUINT uiBufSize; FLMBOOL bIsNull; char * pszDestBuffer; F_Row * pRow = NULL; if (ui64RowId) { if (!pOldRow || pOldRow->getRowId() != ui64RowId) { if( RC_BAD( rc = gv_SFlmSysData.pRowCacheMgr->retrieveRow( pDb, uiTableNum, ui64RowId, &pRow))) { goto Exit; } } else { pRow = pOldRow; } if (RC_BAD( rc = pRow->getUTF8( pDb, pIcd->uiColumnNum, NULL, 0, &bIsNull, NULL, &uiBufSize))) { goto Exit; } if (bIsNull || !uiBufSize) { pDynaBuf->truncateData( 0); } else { if( RC_BAD( rc = pDynaBuf->allocSpace( uiBufSize, (void **)&pszDestBuffer))) { goto Exit; } if (RC_BAD( rc = pRow->getUTF8( pDb, pIcd->uiColumnNum, pszDestBuffer, uiBufSize, &bIsNull, NULL, NULL))) { goto Exit; } } } else { if (RC_BAD( rc = pSearchKey->getUTF8( uiKeyComponent, pDynaBuf))) { goto Exit; } } Exit: if (pRow && pRow != pOldRow) { pRow->ReleaseRow(); } return( rc); } /*************************************************************************** Desc: Get the binary value for a particular key component. *****************************************************************************/ FSTATIC RCODE ixKeyGetBinary( F_Db * pDb, FLMUINT uiTableNum, ICD * pIcd, F_Row * pOldRow, FLMUINT64 ui64RowId, F_DataVector * pSearchKey, FLMUINT uiKeyComponent, F_DynaBuf * pDynaBuf) { RCODE rc = NE_SFLM_OK; FLMUINT uiBufSize; FLMBOOL bIsNull; FLMBYTE * pucDestBuffer; F_Row * pRow = NULL; if (ui64RowId) { if (!pOldRow || pOldRow->getRowId() != ui64RowId) { if( RC_BAD( rc = gv_SFlmSysData.pRowCacheMgr->retrieveRow( pDb, uiTableNum, ui64RowId, &pRow))) { goto Exit; } } else { pRow = pOldRow; } pRow->getDataLen( pDb, pIcd->uiColumnNum, &uiBufSize, &bIsNull); if (bIsNull || !uiBufSize) { pDynaBuf->truncateData( 0); } else { if( RC_BAD( rc = pDynaBuf->allocSpace( uiBufSize, (void **)&pucDestBuffer))) { goto Exit; } if (RC_BAD( rc = pRow->getBinary( pDb, pIcd->uiColumnNum, pucDestBuffer, uiBufSize, NULL, &bIsNull))) { goto Exit; } } } else { if (RC_BAD( rc = pSearchKey->getBinary( uiKeyComponent, pDynaBuf))) { goto Exit; } } Exit: if (pRow && pRow != pOldRow) { pRow->ReleaseRow(); } 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, F_INDEX * pIndex, F_DataVector * pSearchKey, F_Row * pRow1, F_Row * pRow2, FLMBOOL bCompareRowId, const void * pvKey1, FLMUINT uiKeyLen1, const void * pvKey2, FLMUINT uiKeyLen2, FLMINT * piCompare) { RCODE rc = NE_SFLM_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 ui64RowId1; FLMUINT64 ui64RowId2; flmAssert( uiKeyLen1 && uiKeyLen2); // Loop for each compound piece of key uiKeyComponent = 0; pIcd = pIndex->pKeyIcds; 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 the 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; } // Need to get the row that holds the data for the 1st key. if (isSearchKeyComponent( pucKey1)) { flmAssert( pSearchKey); ui64RowId1 = pSearchKey->getRowId(); // The search key better have a row ID or the untruncated // value. flmAssert( ui64RowId1 || !pSearchKey->isRightTruncated( uiKeyComponent)); } else { if (RC_BAD( rc = ixKeyGetRowId( pIndex, pucKey1, pucKeyEnd1, uiKeyComponent, &ui64RowId1))) { goto Exit; } flmAssert( ui64RowId1); } // Get the row that holds the data for the 2nd key. if (isSearchKeyComponent( pucKey2)) { flmAssert( pSearchKey); ui64RowId2 = pSearchKey->getRowId(); // The search key better have a row ID or the untruncated // value. flmAssert( ui64RowId2 || !pSearchKey->isRightTruncated( uiKeyComponent)); } else { if (RC_BAD( rc = ixKeyGetRowId( pIndex, pucKey2, pucKeyEnd2, uiKeyComponent, &ui64RowId2))) { goto Exit; } flmAssert( ui64RowId2); } // If the row IDs are equal, we can skip fetching the data, because // it will be the same. if (ui64RowId1 != ui64RowId2) { FLMBYTE ucDynaBuf1[ 64]; FLMBYTE ucDynaBuf2[ 64]; F_DynaBuf dynaBuf1( ucDynaBuf1, sizeof( ucDynaBuf1)); F_DynaBuf dynaBuf2( ucDynaBuf2, sizeof( ucDynaBuf2)); F_TABLE * pTable = pDb->getDict()->getTable( pIndex->uiTableNum); F_COLUMN * pColumn = pDb->getDict()->getColumn( pTable, pIcd->uiColumnNum); // Better be binary data or text data. switch (pColumn->eDataTyp) { case SFLM_STRING_TYPE: { if (RC_BAD( rc = ixKeyGetUTF8( pDb, pIndex->uiTableNum, pIcd, pRow1, ui64RowId1, pSearchKey, uiKeyComponent, &dynaBuf1))) { goto Exit; } if (RC_BAD( rc = ixKeyGetUTF8( pDb, pIndex->uiTableNum, pIcd, pRow2, ui64RowId2, pSearchKey, uiKeyComponent, &dynaBuf2))) { goto Exit; } if (RC_BAD( rc = f_compareUTF8Strings( dynaBuf1.getBufferPtr(), dynaBuf1.getDataLength(), FALSE, dynaBuf2.getBufferPtr(), dynaBuf2.getDataLength(), FALSE, pIcd->uiCompareRules, pIndex->uiLanguage, piCompare))) { goto Exit; } if (*piCompare < 0) { *piCompare = bSortAscending ? -1 : 1; goto Exit; } else if (*piCompare > 0) { *piCompare = bSortAscending ? 1 : -1; goto Exit; } break; } case SFLM_BINARY_TYPE: { if (RC_BAD( rc = ixKeyGetBinary( pDb, pIndex->uiTableNum, pIcd, pRow1, ui64RowId1, pSearchKey, uiKeyComponent, &dynaBuf1))) { goto Exit; } if (RC_BAD( rc = ixKeyGetBinary( pDb, pIndex->uiTableNum, pIcd, pRow2, ui64RowId2, pSearchKey, uiKeyComponent, &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_SFLM_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); // If there are no more ICDs, we are done with the key // components. if (uiKeyComponent < pIndex->uiNumKeyComponents) { break; } pIcd++; uiKeyComponent++; // 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 row ID, if being requested to. Includes comparing of the // last byte, which is the total number of bytes in the row ID. if (bCompareRowId) { // See if we have a row ID - this may be a search that // passed in only a partial key and there is now ROW id 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 { FLMUINT64 ui64RowId1; FLMUINT64 ui64RowId2; // 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 = f_decodeSEN64( &pucKey1, pucKeyEnd1, &ui64RowId1))) { goto Exit; } if (RC_BAD( rc = f_decodeSEN64( &pucKey2, pucKeyEnd2, &ui64RowId2))) { goto Exit; } if (ui64RowId1 == ui64RowId2) { *piCompare = 0; } else if (ui64RowId1 < ui64RowId2) { *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, F_INDEX * pIndex, KREF_ENTRY * pKrefA, KREF_ENTRY * pKrefB, FLMINT * piCompare) { RCODE rc = NE_SFLM_OK; // Compare index numbers if ((*piCompare = ((FLMINT) pKrefA->ui16IxNum) - ((FLMINT) pKrefB->ui16IxNum)) != 0) { goto Exit; } if (!pIndex || pIndex->uiIndexNum != (FLMUINT)pKrefA->ui16IxNum) { pIndex = pDb->getDict()->getIndex( (FLMUINT)pKrefA->ui16IxNum); } if (RC_BAD( rc = ixKeyCompare( pDb, pIndex, NULL, pKrefA->pRow, pKrefB->pRow, 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, F_INDEX * pIndex, KREF_ENTRY * pKrefA, KREF_ENTRY * pKrefB, FLMINT * piCompare) { RCODE rc = NE_SFLM_OK; if (RC_BAD( rc = krefCompareIxAndKey( pDb, pIndex, 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( F_INDEX * pIndex) { RCODE rc = NE_SFLM_OK; // Sort and remove duplicates if (m_uiKrefCount > 1) { if (RC_BAD( rc = krefQuickSort( this, pIndex, m_pKrefTbl, 0, m_uiKrefCount - 1))) { goto Exit; } if (RC_BAD( rc = krefKillDups( this, pIndex, 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_SFLM_OK; // If the Kref has not been initialized, there is no // work to do. if (m_bKrefSetup) { F_INDEX * pIndex = NULL; 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; } // 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; pIndex = m_pDict->getIndex( uiLastIxNum); } // Flush the key to the index if (m_pKeyColl) { m_pKeyColl->addKey( this, pIndex, pKref); } else { if (RC_BAD(rc = refUpdate( pIndex, pKref, TRUE))) { if (rc != NE_SFLM_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); } return( rc); } /*************************************************************************** Desc: Quick sort an array of KREF_ENTRY * values. ****************************************************************************/ FSTATIC RCODE krefQuickSort( F_Db * pDb, F_INDEX * pIndex, KREF_ENTRY ** pEntryTbl, FLMUINT uiLowerBounds, FLMUINT uiUpperBounds) { RCODE rc = NE_SFLM_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, pIndex, 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, pIndex, 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, pIndex, 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, pIndex, 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, F_INDEX * pIndex, KREF_ENTRY ** pKrefTbl, FLMUINT * puiKrefTotal) { RCODE rc = NE_SFLM_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, pIndex, 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); }