//------------------------------------------------------------------------------ // Desc: Utility to compare two databases for equivalence. // Tabs: 3 // // Copyright (c) 2003-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" #include "sharutil.h" #include "dbdiff.h" /**************************************************************************** Name: compareNodes Desc: method to compare two F_DOMNodes. ****************************************************************************/ RCODE F_DbDiff::compareNodes( FLMBYTE * pszCompareInfo, //info about the records for output F_DOMNode * pNode1, F_DOMNode * pNode2, DBDIFF_CALLBACK outputCallback, void * pvData) { RCODE rc = NE_XFLM_OK; FlmStringAcc acc; char szErrBuff[ 100]; if (pNode1->compareNode( pNode2, m_pDb1, m_pDb2, &szErrBuff[0], sizeof( szErrBuff)) != 0) { acc.printf( "ERROR: {%s} %s\n", pszCompareInfo, szErrBuff); outputCallback( (char*)acc.getTEXT(), pvData); rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } Exit: return rc; } /**************************************************************************** Name: compareCollections Desc: compare two databases' Collection ****************************************************************************/ RCODE F_DbDiff::compareCollections( FLMUINT uiCollection, DBDIFF_CALLBACK outputCallback, void * pvData) { FLMUINT64 ui64NodeId1 = 0; FLMUINT64 ui64NodeId2 = 0; F_DOMNode * pNode1 = NULL; F_DOMNode * pNode2 = NULL; FLMBOOL bScanFinished1 = FALSE; FLMBOOL bScanFinished2 = FALSE; FlmStringAcc acc; RCODE rc = NE_XFLM_OK; if ( uiCollection == XFLM_MAINT_COLLECTION) { // Ignore any differences in this collection goto Exit; } while ( (!bScanFinished1) && (!bScanFinished2)) { if ( RC_BAD( rc = m_pDb1->getNextNode( uiCollection, ui64NodeId1, &pNode1))) { if ( rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; bScanFinished1 = TRUE; } else { goto Exit; } } else { if( RC_BAD( rc = pNode1->getNodeId( m_pDb1, &ui64NodeId1))) { goto Exit; } } if ( RC_BAD( rc = m_pDb2->getNextNode( uiCollection, ui64NodeId2, &pNode2))) { if ( rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; bScanFinished2 = TRUE; } else { goto Exit; } } else { if( RC_BAD( rc = pNode1->getNodeId( m_pDb2, &ui64NodeId2))) { goto Exit; } } if ( bScanFinished1 && bScanFinished2) { break; } //if one of them is finished and not the other one if (bScanFinished1 != bScanFinished2) { FlmStringAcc acc; acc.appendf( "ERROR: database1 and database2 have a different # of nodes in " "Collection %u\n", uiCollection); outputCallback( (char*)acc.getTEXT(), pvData); rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } //if the nodeId's are different, then there's a problem if ( ui64NodeId1 != ui64NodeId2) { FlmStringAcc acc; acc.appendf( "ERROR: database1's nodeId %u ", ui64NodeId1); acc.appendf( " != database2's nodeId %u!\n", ui64NodeId2); acc.appendf( "ERROR: nodeId mismatch in Collection %u\n", uiCollection); outputCallback( (char*)acc.getTEXT(), pvData); rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } if ( !bScanFinished1) //bScanFinished1==bScanFinished2 at this point { acc.printf( "NodeId %u of Collection %u", ui64NodeId1, uiCollection); //compare the two records for accuracy if ( RC_BAD( rc = compareNodes( (FLMBYTE *)acc.getTEXT(), pNode1, pNode2, outputCallback, pvData))) { goto Exit; } } #ifdef FLM_NLM f_yieldCPU(); #else f_sleep( 0); #endif } Exit: if ( pNode1) { pNode1->Release(); } if ( pNode2) { pNode2->Release(); } return rc; } /**************************************************************************** Name: flmDbDiff Desc: compare two databases for logical equivalence. ****************************************************************************/ RCODE F_DbDiff::diff( char * pszDb1, char * pszDb1Password, char * pszDb2, char * pszDb2Password, DBDIFF_CALLBACK outputCallback, void * pvData) { RCODE rc = NE_XFLM_OK; RCODE tmpRc = NE_XFLM_OK; FLMBOOL bTransActive1 = FALSE; FLMBOOL bTransActive2 = FALSE; F_DOMNode * pNode1 = NULL; F_DOMNode * pNode2 = NULL; FlmVector CollectionVec1; FlmVector CollectionVec2; FLMUINT uiCollectionLen1 = 0; FLMUINT uiCollectionLen2 = 0; FLMUINT uiIndexLen1 = 0; FLMUINT uiIndexLen2 = 0; FLMUINT uiLoop; F_COLLECTION * pCollection1; F_COLLECTION * pCollection2; F_Dict * pDict1; F_Dict * pDict2; FLMUINT uiCollectionNum; FLMUINT uiIndexNum; IXD * pIxd; FlmStringAcc acc; FlmVector IndexVec1; FlmVector IndexVec2; //try to unlock any files currently open if ( RC_BAD( rc = dbSystem.closeUnusedFiles(0))) { goto Exit; } //can't compare a db with itself if ( f_strcmp( pszDb1, pszDb2) == 0) { outputCallback( "ERROR: cannot compare a dib with itself!\n", pvData); rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } //open up the database, and all that it entails if( RC_BAD( rc = dbSystem.openDb( pszDb1, NULL, NULL, pszDb1Password, XFLM_DONT_RESUME_THREADS, (IF_Db **)&m_pDb1))) { acc.printf( "ERROR: cannot open dib '"); acc.appendTEXT( pszDb1); acc.appendTEXT( "'!\n"); outputCallback((char*)acc.getTEXT(), pvData); goto Exit; } if( RC_BAD( rc = dbSystem.openDb( pszDb2, NULL, NULL, pszDb2Password, XFLM_DONT_RESUME_THREADS, (IF_Db **)&m_pDb2))) { acc.printf( "ERROR: cannot open dib '"); acc.appendTEXT( pszDb2); acc.appendTEXT( "'!\n"); outputCallback((char*)acc.getTEXT(), pvData); goto Exit; } if( RC_BAD( rc = m_pDb1->transBegin( XFLM_READ_TRANS))) { outputCallback( "ERROR: cannot start trans #1\n", pvData); goto Exit; } bTransActive1 = TRUE; if( RC_BAD( rc = m_pDb2->transBegin( XFLM_READ_TRANS))) { outputCallback( "ERROR: cannot start trans #2\n", pvData); goto Exit; } bTransActive2 = TRUE; if (RC_BAD( rc = m_pDb1->getDictionary( &pDict1))) { outputCallback( "ERROR: retrieving dictionary #1\n", pvData); goto Exit; } if (RC_BAD( rc = m_pDb2->getDictionary( &pDict2))) { outputCallback( "ERROR: retrieving dictionary #2\n", pvData); goto Exit; } //prepare to read Collections from the dictionary if (RC_BAD( rc = pDict1->getCollection( XFLM_DICT_COLLECTION, &pCollection1))) { outputCallback( "ERROR: retrieving dictionary collection #1\n", pvData); goto Exit; } if (RC_BAD( rc = pDict2->getCollection( XFLM_DICT_COLLECTION, &pCollection2))) { outputCallback( "ERROR: retrieving dictionary collection #2\n", pvData); goto Exit; } outputCallback( "Starting dbdiff with databases '", pvData); outputCallback( (char*)pszDb1, pvData); outputCallback( "' and '", pvData); outputCallback( (char*)pszDb2, pvData); outputCallback( "'\n", pvData); outputCallback( "Building index and Collection lists...", pvData); //build up a list of each Collection from the first database for (uiCollectionNum = 0;;) { if ((pCollection1 = pDict1->getNextCollection( uiCollectionNum, TRUE)) == NULL) { break; } uiCollectionNum = pCollection1->lfInfo.uiLfNum; if ( RC_BAD( rc = CollectionVec1.setElementAt( (void*)uiCollectionNum, uiCollectionLen1++))) { goto Exit; } } //build up a list of each Collection from the second database for (uiCollectionNum = 0;;) { if ((pCollection2 = pDict2->getNextCollection( uiCollectionNum, TRUE)) == NULL) { break; } uiCollectionNum = pCollection2->lfInfo.uiLfNum; if ( RC_BAD( rc = CollectionVec2.setElementAt( (void*)uiCollectionNum, uiCollectionLen2++))) { goto Exit; } } //must have same # of Collections to be the same if ( uiCollectionLen1 != uiCollectionLen2) { acc.printf( "ERROR: the two databases have a different number of Collections(" "%u,%u)!\n", uiCollectionLen1, uiCollectionLen2); outputCallback( (char*)acc.getTEXT(), pvData); rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } //must have same # of indexes to be the same //build up a list of each Collection from the first database for (uiIndexNum = 0;;) { if ((pIxd = pDict1->getNextIndex( uiIndexNum, TRUE)) == NULL) { break; } uiIndexNum = pIxd->uiIndexNum; IndexVec1.setElementAt( (void*)uiIndexNum, uiIndexLen1++); } for (uiIndexNum = 0;;) { if ((pIxd = pDict2->getNextIndex( uiIndexNum, TRUE)) == NULL) { break; } uiIndexNum = pIxd->uiIndexNum; IndexVec2.setElementAt( (void*)uiIndexNum, uiIndexLen2++); } if ( uiIndexLen1 != uiIndexLen2) { acc.printf( "ERROR: the two databases have a different number of indexes(" "%u,%u)!\n", uiIndexLen1, uiIndexLen2); outputCallback( (char*)acc.getTEXT(), pvData); rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } outputCallback( "done\n", pvData); outputCallback( "comparing collections\n", pvData); //loop through each Collection and compare the two for ( uiLoop = 0; uiLoop < uiCollectionLen1; uiLoop++) { FLMUINT uiCollection1 = (FLMUINT)CollectionVec1.getElementAt( uiLoop); FLMUINT uiCollection2 = (FLMUINT)CollectionVec2.getElementAt( uiLoop); if ( uiCollection1 != uiCollection2) { acc.printf( "ERROR: database1's Collection (%u) != " "database2's Collection (%u)!\n", uiCollection1, uiCollection2); outputCallback( (char*)acc.getTEXT(), pvData); rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } else { acc.printf( "(%4u of %4u) processing Collection #%u\n", uiLoop+1, uiCollectionLen1, uiCollection1); outputCallback( (char*)acc.getTEXT(), pvData); if ( RC_BAD( rc = compareCollections( uiCollection1, //uiCollection1==uiCollection2 at this point outputCallback, pvData))) { goto Exit; } } } // Compare index keys outputCallback( "done\n", pvData); outputCallback( "comparing indexes\n", pvData); for( uiLoop = 0; uiLoop < uiIndexLen1; uiLoop++) { uiIndexNum = (FLMUINT)IndexVec1.getElementAt( uiLoop); if ( uiIndexNum != (FLMUINT)IndexVec2.getElementAt( uiLoop)) { acc.printf( "ERROR: database1's Index (%u) != " "database2's Index (%u)!\n", uiIndexNum, IndexVec2.getElementAt( uiLoop)); outputCallback( (char*)acc.getTEXT(), pvData); rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } acc.printf( "(%5u of %5u) processing Index #%u\n", uiLoop + 1, uiIndexLen1, uiIndexNum); outputCallback( acc.getTEXT(), pvData); if ( RC_BAD( rc = compareIndexes( uiIndexNum, outputCallback, pvData))) { goto Exit; } } Exit: if (pNode1) { pNode1->Release(); } if (pNode2) { pNode2->Release(); } if ( bTransActive1) { if ( RC_OK( rc)) { rc = m_pDb1->transCommit(); } else { (void)m_pDb1->transAbort(); } } if ( bTransActive2) { if ( RC_OK( rc)) { rc = m_pDb2->transCommit(); } else { (void)m_pDb2->transAbort(); } } //close any files currently open. This is so any smi open can be //done following this in the same process. tmpRc = dbSystem.closeUnusedFiles(0); if ( RC_OK( rc)) { rc = tmpRc; } return rc; } RCODE F_DbDiff::compareIndexes( FLMUINT uiIndexNum, DBDIFF_CALLBACK outputCallback, void * pvData) { RCODE rc = NE_XFLM_OK; F_DataVector searchKey1; F_DataVector searchKey2; FLMBOOL bDataComp; FLMBOOL bKeyComp; FLMUINT uiLoop; FLMUINT uiLoop2; FLMUINT uiDataType; FLMUINT uiDataLen1; FLMUINT uiDataLen2; FLMBYTE * pucVal1 = NULL; FLMBYTE * pucVal2 = NULL; FLMUINT64 ui64Val1 = 0; FLMUINT64 ui64Val2 = 0; for( uiLoop = 0;; uiLoop++) { if ( uiLoop == 0) { if (RC_BAD( rc = m_pDb1->keyRetrieve( uiIndexNum, NULL, XFLM_FIRST, &searchKey1))) { if ( rc == NE_XFLM_EOF_HIT) { // empty index. Make sure the other index is empty too. if (( rc = m_pDb2->keyRetrieve( uiIndexNum, NULL, XFLM_FIRST, &searchKey2)) != NE_XFLM_EOF_HIT) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"key mismatch found.", pvData); goto Exit; } } rc = NE_XFLM_OK; goto Exit; } if (RC_BAD( rc = m_pDb2->keyRetrieve( uiIndexNum, NULL, XFLM_FIRST, &searchKey2))) { outputCallback( (char*)"keyRetrieve failed.", pvData); goto Exit; } if ( searchKey1.getDocumentID() != searchKey2.getDocumentID()) { // Error rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"index document id mismatch.", pvData); goto Exit; } } else { if (RC_BAD( rc = m_pDb1->keyRetrieve( uiIndexNum, &searchKey1, XFLM_EXCL, &searchKey1))) { if ( rc == NE_XFLM_EOF_HIT) { // No more keys. Make sure the other index is at the end too. if (( rc = m_pDb2->keyRetrieve( uiIndexNum, &searchKey2, XFLM_EXCL, &searchKey2)) != NE_XFLM_EOF_HIT) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"key mismatch found.", pvData); goto Exit; } } rc = NE_XFLM_OK; goto Exit; } if (RC_BAD( rc = m_pDb2->keyRetrieve( uiIndexNum, &searchKey2, XFLM_EXCL, &searchKey2))) { outputCallback( (char*)"keyRetrieve failed.", pvData); goto Exit; } } for( uiLoop2 = 0;; uiLoop2++) { if ( ( bDataComp = searchKey1.isDataComponent(uiLoop2)) != searchKey2.isDataComponent(uiLoop2)) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"isDataComponent mismatch.", pvData); goto Exit; } if ( ( bKeyComp = searchKey1.isKeyComponent(uiLoop2)) != searchKey2.isKeyComponent(uiLoop2)) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"isKeyComponent mismatch.", pvData); goto Exit; } if ( !bDataComp && !bKeyComp) { // No more components to check break; } if ( searchKey1.isRightTruncated( uiLoop2) != searchKey2.isRightTruncated( uiLoop2)) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"isRightTruncated mismatch.", pvData); goto Exit; } if ( searchKey1.isLeftTruncated( uiLoop2) != searchKey2.isLeftTruncated( uiLoop2)) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"isLeftTruncated mismatch.", pvData); goto Exit; } if ( searchKey1.getID(uiLoop2) != searchKey2.getID(uiLoop2)) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"node id mismatch.", pvData); goto Exit; } if ( searchKey1.getNameId(uiLoop2) != searchKey2.getNameId(uiLoop2)) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"name id mismatch.", pvData); goto Exit; } if ( searchKey1.isAttr(uiLoop2) != searchKey2.isAttr(uiLoop2)) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"isAttr mismatch.", pvData); goto Exit; } if ( ( uiDataType = searchKey1.getDataType(uiLoop2)) != searchKey2.getDataType(uiLoop2)) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"data type mismatch.", pvData); goto Exit; } if ( ( uiDataLen1 = searchKey1.getDataLength(uiLoop2)) != ( uiDataLen2 = searchKey2.getDataLength(uiLoop2))) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"data length mismatch.", pvData); goto Exit; } if ( uiDataType == XFLM_TEXT_TYPE || uiDataType == XFLM_BINARY_TYPE) { if ( pucVal1) { f_free( &pucVal1); } if ( RC_BAD( rc = f_alloc( uiDataLen1, &pucVal1))) { goto Exit; } if ( pucVal2) { f_free( &pucVal2); } if ( RC_BAD( rc = f_alloc( uiDataLen2, &pucVal2))) { goto Exit; } } switch( uiDataType) { case XFLM_NUMBER_TYPE: { if ( RC_BAD( rc = searchKey1.getUINT64( uiLoop2, &ui64Val1))) { outputCallback( (char*)"getUINT64 failed.", pvData); goto Exit; } if ( RC_BAD( rc = searchKey2.getUINT64( uiLoop2, &ui64Val2))) { outputCallback( (char*)"getUINT64 failed.", pvData); goto Exit; } if ( ui64Val1 != ui64Val2) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"UINT64 val mismatch.", pvData); goto Exit; } break; } case XFLM_BINARY_TYPE: { if ( RC_BAD( rc = searchKey1.getBinary( uiLoop2, pucVal1, &uiDataLen1))) { outputCallback( (char*)"getBinary failed.", pvData); goto Exit; } if ( RC_BAD( rc = searchKey2.getBinary( uiLoop2, pucVal2, &uiDataLen2))) { outputCallback( (char*)"getBinary failed.", pvData); goto Exit; } if ( f_memcmp( pucVal1, pucVal2, uiDataLen2) != 0) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"binary val mismatch.", pvData); goto Exit; } break; } case XFLM_TEXT_TYPE: { if ( RC_BAD( rc = searchKey1.getUTF8( uiLoop2, pucVal1, &uiDataLen1))) { outputCallback( (char*)"getUTF8 failed.", pvData); goto Exit; } if ( RC_BAD( rc = searchKey2.getUTF8( uiLoop2, pucVal2, &uiDataLen2))) { outputCallback( (char*)"getUTF8 failed.", pvData); goto Exit; } if ( f_strcmp( pucVal1, pucVal2) != 0) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"text val mismatch.", pvData); goto Exit; } break; } default: break; } } } Exit: if ( pucVal1) { f_free( &pucVal1); } if ( pucVal2) { f_free( &pucVal2); } return rc; }