Files
mars-flaim/xflaim/util/dbdiff.cpp
ahodgkinson 12a621dc04 Changed license to LGPL.
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@1010 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2007-01-23 11:22:22 +00:00

775 lines
18 KiB
C++

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