/* Copyright (C) 2014 InfiniDB, Inc.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; version 2 of
   the License.

   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, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   MA 02110-1301, USA. */

/*
* $Id: we_bulkrollbackmgr.cpp 4737 2013-08-14 20:45:46Z bwilkinson $
*/

#include <sstream>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <boost/scoped_array.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/convenience.hpp>

#include "we_bulkrollbackmgr.h"

#include "we_define.h"
#include "we_brm.h"
#include "we_config.h"
#include "we_fileop.h"
#include "we_log.h"
#include "we_bulkrollbackfile.h"
#include "we_bulkrollbackfilecompressed.h"
#include "we_bulkrollbackfilecompressedhdfs.h"
#include "we_rbmetawriter.h"
#include "messageids.h"
#include "cacheutils.h"

using namespace execplan;

#include "IDBPolicy.h"
using namespace idbdatafile;

using namespace std;

namespace
{
const char* DATA_DIR_SUFFIX = "_data";
const char* TMP_FILE_SUFFIX = ".tmp";

const int BUF_SIZE = 1024;  // size of buffer used to read meta data records

const std::string DB_FILE_PREFIX   ("FILE");
const std::string DB_FILE_EXTENSION(".cdf");
const std::string DB_FILE_EXTENSION_ORIG(".orig");
const std::string DB_FILE_EXTENSION_TMP (".tmp" );
}

namespace WriteEngine
{

//------------------------------------------------------------------------------
// BulkRollbackMgr constructor
//
// tableOID - OID of the table to be rolled back.
//------------------------------------------------------------------------------
BulkRollbackMgr::BulkRollbackMgr ( OID tableOID,
                                   uint64_t lockID,
                                   const std::string& tableName,
                                   const std::string& applName, Log* logger ) :
    fTableOID(tableOID),
    fLockID(lockID),
    fTableName(tableName),
    fProcessId(0),
    fMetaFile(NULL),
    fPendingDctnryStoreOID(0),
    fPendingDctnryStoreDbRoot(0),
    fSysLogger( logging::LoggingID( SUBSYSTEM_ID_WE ) ),
    fDebugConsole( false ),
    fLog(logger),
    fApplName(applName),
    fVersion(4)
{
}

//------------------------------------------------------------------------------
// Rolls back the state of the extentmap and database files for the table OID
// specified to the constructor, using the previously written meta-data file.
// The requiredMetaFile flag indicates whether a missing bulk rollback file
// should be considered an error or not; should probably only be 'true' if
// rolling back during a failed mode3 import, where we know when a metadata
// backup file has been created.
//
// returns:
//   NO_ERROR if rollback completed successfully
//------------------------------------------------------------------------------
int BulkRollbackMgr::rollback ( bool keepMetaFile )
{
    logAMessage( logging::LOG_TYPE_INFO,
                 logging::M0084, 0, fApplName );

    int rc = NO_ERROR;
    int dbRootRollbackCount = 0;

    try
    {
        // validate that BRM is in read/write mode so we can update extentmap
        rc = BRMWrapper::getInstance()->isReadWrite();

        if (rc != NO_ERROR)
        {
            WErrorCodes ec;
            std::ostringstream oss;
            oss << "Bulk rollback for table " << fTableName << " (OID-" <<
                fTableOID << ") not performed; " << ec.errorString(rc);

            throw WeException( oss.str(), rc );
        }

        std::vector<uint16_t> dbRoots;
        Config::getRootIdList( dbRoots );

        std::string emptyText0072;
        logAMessage( logging::LOG_TYPE_INFO,
                     logging::M0072, 0, emptyText0072 );

        // Loop through DBRoots for this PM
        for (unsigned m = 0; m < dbRoots.size(); m++)
        {
            std::istringstream metaDataStream;
            bool bPerformRollback = openMetaDataFile ( dbRoots[m],
                                    metaDataStream );

            // Call function to:
            // 1. read bulk rollback meta-data file
            // 2. rollback applicable extents from extentmap
            // 3. delete applicable extents from database files
            // 4. reinitialize blocks trailing the HWM block in the last extent
            //    of each segment file
            // ...
            if (bPerformRollback)
            {
                dbRootRollbackCount++;
                deleteExtents ( metaDataStream );
                closeMetaDataFile ( );
            }
            else // Skip any DBRoot not having a meta-data file
            {
                std::ostringstream msg0090Text;
                msg0090Text << dbRoots[m];
                logAMessage( logging::LOG_TYPE_INFO,
                             logging::M0090, 0, msg0090Text.str() );
            }
        }

        if (dbRootRollbackCount > 0)
        {
            // Notify PrimProc to flush FD cache.  If error occurs, we tell
            // the user but keep going.
            int flushFd_rc = cacheutils::dropPrimProcFdCache();

            if (flushFd_rc != 0)
            {
                std::ostringstream oss;
                oss << "ClearTableLock: Error flushing PrimProc "
                    "FD cache after rolling back data for table " <<
                    fTableName <<
                    " (OID-" << fTableOID << ");  rc-" << flushFd_rc;

                // If we have a logger, then use it to log to syslog, etc
                if (fLog)
                {
                    fLog->logMsg( oss.str(), MSGLVL_ERROR );
                }
                else // log message ourselves
                {
                    std::cout << oss.str() << std::endl;

                    logging::Message m( logging::M0010 );
                    logging::Message::Args args;
                    args.add( oss.str() );
                    m.format( args );
                    fSysLogger.logErrorMessage( m );
                }
            }

            // Notify PrimProc to flush block cache.  If error occurs, we tell
            // the user but keep going.
            std::vector<BRM::OID_t> allOIDs;
            std::set<OID>::const_iterator iter = fAllColDctOIDs.begin();
            cerr << "Rollback flushing: ";
            while (iter != fAllColDctOIDs.end())
            {
                cerr << *iter << ", ";
                //std::cout << "Flushing OID from PrimProc cache " << *iter <<
                //  std::endl;
                allOIDs.push_back(*iter);
                ++iter;
            }
            cerr << endl;

            int cache_rc = cacheutils::flushOIDsFromCache( allOIDs );

            if (cache_rc != 0)
            {
                std::ostringstream oss;
                oss << "ClearTableLock: Error flushing "
                    "PrimProc cache after rolling back data for table " <<
                    fTableName <<
                    " (OID-" << fTableOID << ");  rc-" << cache_rc;

                // If we have a logger, then use it to log to syslog, etc
                if (fLog)
                {
                    fLog->logMsg( oss.str(), MSGLVL_ERROR );
                }
                else // log message ourselves
                {
                    std::cout << oss.str() << std::endl;

                    logging::Message m( logging::M0010 );
                    logging::Message::Args args;
                    args.add( oss.str() );
                    m.format( args );
                    fSysLogger.logErrorMessage( m );
                }
            }
        }
    }
    catch (WeException& ex)
    {
        std::string msgText(fApplName);
        msgText += ". (rollback failed; ";
        msgText += ex.what();
        msgText += ')';
        logAMessage( logging::LOG_TYPE_ERROR,
                     logging::M0085, 0, msgText );

        fErrorMsg = ex.what();
        return ex.errorCode();
    }

    if (!keepMetaFile)
        deleteMetaDataFiles ( );

    if (dbRootRollbackCount > 0)
    {
        logAMessage( logging::LOG_TYPE_INFO,
                     logging::M0085, 0, fApplName );
    }
    else
    {
        std::string msgText(fApplName);
        msgText += ". (Nothing to rollback)";
        logAMessage( logging::LOG_TYPE_INFO,
                     logging::M0085, 0, msgText );
    }

    return rc;
}

//------------------------------------------------------------------------------
// Validate that all the bulk rollback meta files on all the local DBRoots
// exist.  This should apply for a cpimport.bin mode3 import for example.
// A mode1 distributed import on the other hand, might not have gotten far
// enough to have created a bulk rollback meta file on every PM, so
// validateAllMetaFilesExist() should probably only be called within the
// context of a mode3 import that has entered rollback mode.  In any other
// case, a missing bulk rollback meta file might be explainable.
//
// @bug 4496 3.0 Failover
// NOTE: Stopped using this function with 3.0, when it became possible for
//       DBRoots to move from 1 PM to another, in the middle of the job.
//       We simply perform a bulk rollback for any meta-data file we find,
//       and skip performing a bulk rollback for a dbroot that does not
//       have a meta-data file.  Kept the function around for the time being.
//------------------------------------------------------------------------------
void BulkRollbackMgr::validateAllMetaFilesExist (
    const std::vector<uint16_t>& dbRoots ) const
{
    // Loop through DBRoots for this PM
    for (unsigned m = 0; m < dbRoots.size(); m++)
    {
        std::string bulkRollbackPath( Config::getDBRootByNum(dbRoots[m]) );

        // Construct file name and check for it's existence
        std::ostringstream oss;
        oss << '/' << DBROOT_BULK_ROLLBACK_SUBDIR << '/' << fTableOID;
        std::string metaFileName  = bulkRollbackPath;
        metaFileName += oss.str();

        if ( !IDBPolicy::exists( metaFileName.c_str() ) )
        {
            std::ostringstream oss;
            oss << "Error opening bulk rollback meta-data file " <<
                metaFileName << "; File does not exist.";

            throw WeException( oss.str(), ERR_FILE_OPEN );
        }
    }
}

//------------------------------------------------------------------------------
// Open the meta-data file for fTableOID.  File contains information used in
// rolling back the table to a previous state.
// Returns true/false to indicate whether execution should continue if the
// meta-data file is missing.
//------------------------------------------------------------------------------
bool BulkRollbackMgr::openMetaDataFile ( uint16_t dbRoot,
        std::istringstream& metaDataStream )
{
    std::string bulkRollbackPath( Config::getDBRootByNum( dbRoot ) );

    // Construct file name and check for it's existence
    std::ostringstream oss;
    oss << '/' << DBROOT_BULK_ROLLBACK_SUBDIR << '/' << fTableOID;
    fMetaFileName  = bulkRollbackPath;
    fMetaFileName += oss.str();

    // Return if the meta-data file does not exist.  This could happen if we
    // are executing distributed rollback on several PMs, some of which may
    // have not even executed an import.
    // Also could happen if DBRoots are moved from 1 PM to another during a job.
    if ( !IDBPolicy::exists( fMetaFileName.c_str() ) )
    {
        return false;
    }

    // Open the file
    fMetaFile = IDBDataFile::open( IDBPolicy::getType(fMetaFileName.c_str(),
                                   IDBPolicy::WRITEENG),
                                   fMetaFileName.c_str(), "rb", 0);

    if ( !fMetaFile )
    {
        int errRc = errno;
        std::ostringstream oss;
        oss << "Error opening bulk rollback meta-data file " <<
            fMetaFileName << "; err-" <<
            errRc << "; " << strerror( errRc );

        throw WeException( oss.str(), ERR_FILE_OPEN );
    }

    fMetaFileNames.push_back( fMetaFileName );

    // First record in the file must be a Version record.
    char inBuf[ BUF_SIZE ];
    ssize_t metaFileSize = IDBPolicy::size( fMetaFileName.c_str() );
    boost::scoped_array<char> buf( new char[ metaFileSize ] );
    // retry 10 times for partial reads, just in case
    ssize_t readSofar = 0; // bytes read so far
    ssize_t bytes = 0;    // bytes read by one pread
    char* p = buf.get();

    for (int i = 0; i < 10 && readSofar < metaFileSize; i++)
    {
        bytes = fMetaFile->pread( p + readSofar, readSofar, metaFileSize - readSofar);

        if (bytes < 0)
            break;

        readSofar += bytes;
    }

    if ( readSofar != metaFileSize )
    {
        int errRc = errno;
        std::ostringstream oss;
        oss << "Error reading bulk rollback meta-data file "
            << fMetaFileName << "; read/expect:" << readSofar << "/" << metaFileSize
            << "; err-" << errRc << "; " << strerror( errRc );

        throw WeException( oss.str(), ERR_FILE_READ );
    }

    // put the data in a string stream
    metaDataStream.str( string( p, metaFileSize ) );
    buf.reset();

    // read data
    metaDataStream.getline( inBuf, BUF_SIZE );

    if (RBMetaWriter::verifyVersion3(inBuf))
    {
        fVersion = 3;
    }
    else if (RBMetaWriter::verifyVersion4(inBuf))
    {
        fVersion = 4;
    }
    else
    {
        std::ostringstream oss;
        oss << "Invalid version record in meta-data file " << fMetaFileName
            << "; record-<" << inBuf << ">" << std::endl;

        throw WeException( oss.str(), ERR_INVALID_PARAM );
    }

    return true;
}

//------------------------------------------------------------------------------
// Close the current meta-data file used in rolling back fTableOID.
//------------------------------------------------------------------------------
void BulkRollbackMgr::closeMetaDataFile ( )
{
    delete fMetaFile;
    fMetaFile = NULL;
}

//------------------------------------------------------------------------------
// Delete all the local meta-data files used in rolling back fTableOID.
//------------------------------------------------------------------------------
void BulkRollbackMgr::deleteMetaDataFiles ( )
{
    for (unsigned k = 0; k < fMetaFileNames.size(); k++)
    {
        IDBPolicy::remove( fMetaFileNames[k].c_str() ) ;

        // Unlink corresponding tmp file created by RBMetaWriter.
        std::string tmpMetaFileName = fMetaFileNames[k];
        tmpMetaFileName += TMP_FILE_SUFFIX;
        IDBPolicy::remove( tmpMetaFileName.c_str() );

        deleteSubDir( fMetaFileNames[k] );
    }
}

//------------------------------------------------------------------------------
// Delete the subdirectory used to backup data needed for rollback.
//------------------------------------------------------------------------------
void BulkRollbackMgr::deleteSubDir( const std::string& metaFileName )
{
    std::string bulkRollbackSubPath( metaFileName );
    bulkRollbackSubPath += DATA_DIR_SUFFIX;

    if ( IDBPolicy::remove( bulkRollbackSubPath.c_str() ) != 0 )
    {
        std::ostringstream oss;
        oss << "Warning: Error deleting bulk rollback data subdirectory " <<
            bulkRollbackSubPath << ";";

        if (fLog)
            fLog->logMsg( oss.str(), MSGLVL_WARNING );
        else
            std::cout << oss.str() << std::endl;
    }
}

//------------------------------------------------------------------------------
// Function that drives the rolling back or deletion of extents for a given
// database table as specified in a meta-data bulk rollback file.
//------------------------------------------------------------------------------
void BulkRollbackMgr::deleteExtents ( std::istringstream& metaDataStream )
{
    char  inBuf[ BUF_SIZE ];
    OID   columnOID     = 0;
    OID   storeOID      = 0;
    uint32_t dbRoot    = 0;

    // Loop through the records in the meta-data file
    while (metaDataStream.getline( inBuf, BUF_SIZE ))
    {
        // Restore extents for a DBRoot
        if (RBMetaWriter::verifyColumn1Rec(inBuf))
        {
            // Process any pending dictionary deletes
            if (fPendingDctnryExtents.size() > 0)
            {
                deleteDctnryExtents ( );
                deleteDbFiles( );
            }

            deleteColumn1Extents ( inBuf );
            deleteDbFiles( );
        }
        // Delete all extents from a formerly empty DBRoot
        else if (RBMetaWriter::verifyColumn2Rec(inBuf))
        {
            // Process any pending dictionary deletes
            if (fPendingDctnryExtents.size() > 0)
            {
                deleteDctnryExtents ( );
                deleteDbFiles( );
            }

            deleteColumn2Extents ( inBuf );
            deleteDbFiles( );
        }
        else if (RBMetaWriter::verifyDStore1Rec(inBuf) ||
                 RBMetaWriter::verifyDStore2Rec(inBuf))
        {
            if (fPendingDctnryExtents.size() > 0)
            {
                char recType[100];
                int numFields = sscanf(
                                    inBuf, "%s %u %u %d",
                                    recType, &columnOID, &storeOID, &dbRoot );

                if (numFields != 4)
                {
                    std::ostringstream oss;
                    oss << "Invalid record in meta-data file " << fMetaFileName
                        << "; record-<" << inBuf << ">" << std::endl;

                    throw WeException( oss.str(), ERR_INVALID_PARAM );
                }

                // Process any pending dictionary deletes
                if ((storeOID != fPendingDctnryStoreOID) ||
                        (dbRoot   != fPendingDctnryStoreDbRoot))
                {
                    deleteDctnryExtents ( );
                    deleteDbFiles( );
                }
            }

            readMetaDataRecDctnry ( inBuf );
        }
        else
        {
            // ignore unrecognized record type
        }
    } // end of loop through all the records in the meta-data input file

    // Process any pending dictionary deletes
    if (fPendingDctnryExtents.size() > 0)
    {
        deleteDctnryExtents ( );
        deleteDbFiles( );
    }
}

//------------------------------------------------------------------------------
// Read a meta-data dictionary record (DSTORE1 or DSTORE2) from meta-data file.
// Each record specifies the rollback point for a given dbroot, partition,
// segment number, and HWM for a certain dictionary store OID.
//
// inBuf - latest dictionary extent record to be parsed from meta-data file
//------------------------------------------------------------------------------
void BulkRollbackMgr::readMetaDataRecDctnry ( const char* inBuf )
{
    char      recType[100];
    OID       dColumnOID;
    OID       dStoreOID;
    uint32_t dbRootHwm;
    uint32_t partNumHwm;
    uint32_t segNumHwm;
    HWM       localHwm;
    int       compressionType = 0; // optional parameter

    sscanf(inBuf, "%s", recType);
    RollbackData rbData;

    // Process DSTORE1 records representing segment files with an HWM
    if (RBMetaWriter::verifyDStore1Rec(recType))
    {
        int numFields = sscanf(inBuf, "%s %u %u %u %u %u %u %d",
                               recType, &dColumnOID, &dStoreOID,
                               &dbRootHwm, &partNumHwm, &segNumHwm, &localHwm, &compressionType );

        if (numFields < 7) // compressionType optional
        {
            std::ostringstream oss;
            oss << "Invalid DSTOR1 record in meta-data file " <<
                fMetaFileName << "; record-<" << inBuf << ">" << std::endl;

            throw WeException( oss.str(), ERR_INVALID_PARAM );
        }

        rbData.fWithHwm = true;
        rbData.fHwm     = localHwm;
    }

    // Process DSTORE2 records representing segment files w/o HWM; meaning that
    // segment file did not previously exist and can be deleted if it was added
    else
    {
        int numFields = sscanf(inBuf, "%s %u %u %u %u %u %d",
                               recType, &dColumnOID, &dStoreOID,
                               &dbRootHwm, &partNumHwm, &segNumHwm, &compressionType );

        if (numFields < 6) // compressionType optional
        {
            std::ostringstream oss;
            oss << "Invalid DSTOR2 record in meta-data file " <<
                fMetaFileName << "; record-<" << inBuf << ">" << std::endl;

            throw WeException( oss.str(), ERR_INVALID_PARAM );
        }

        rbData.fWithHwm = false;
        rbData.fHwm     = 0;
    }

    rbData.fDbRoot  = dbRootHwm;
    rbData.fPartNum = partNumHwm;
    rbData.fSegNum  = segNumHwm;

    fPendingDctnryExtents.push_back( rbData );

    // OID and compression type should be the same for all store files relating
    // to the same dictionary column, but they are set for each record nonethe-
    // less since the format of the meta data file is a flat file format.
    // Likewise, the DBRoot will be the same for each collection of dictionary
    // extents that are processed as a group.
    fPendingDctnryStoreOID             = dStoreOID;
    fPendingDctnryStoreCompressionType = compressionType;
    fPendingDctnryStoreDbRoot          = dbRootHwm;
}

//------------------------------------------------------------------------------
// Delete column extents based on COLUMN1 record input
//------------------------------------------------------------------------------
void BulkRollbackMgr::deleteColumn1Extents ( const char* inBuf )
{
    if (fVersion == 3)
        deleteColumn1ExtentsV3( inBuf );
    else
        deleteColumn1ExtentsV4( inBuf );
}

//------------------------------------------------------------------------------
// Delete column extents based on COLUMN2 record input
//------------------------------------------------------------------------------
void BulkRollbackMgr::deleteColumn2Extents ( const char* inBuf )
{
    if (fVersion == 3)
        deleteColumn2ExtentsV3( inBuf );
    else
        deleteColumn2ExtentsV4( inBuf );
}

//------------------------------------------------------------------------------
// Delete all the column extents (from the extent map and the db files) that
// logically follow the HWM extent contained in inBuf; where inBuf is a
// COLUMN1 record read from a meta-data bulk rollback file.  This function
// is limited to rolling back the extent changes to a specific DBRoot.
//
// inBuf - latest column extent record to be parsed from meta-data file
//
// This function exists to handle version3 metadata input files, in case a user:
// 1. ungracefully took their system down with a table lock in place
// 2. upgraded to a release that supported version4 (used to support the new
//    segment file numbering).
// 3. then brought infinidb up, and DMLProc triggered this function to execute
//    a bulk rollback (during system startup) using a leftover version3
//    formatted meta data file.
//
// In the case of a COLUMN1 record, V3 and V4 are handled the same, so this
// adaptor function is a pass-thru to the V4 function.
//------------------------------------------------------------------------------
void BulkRollbackMgr::deleteColumn1ExtentsV3 ( const char* inBuf )
{
    deleteColumn1ExtentsV4( inBuf );
}

//------------------------------------------------------------------------------
// Delete all the column extents (from the extent map and the db files) that
// fall in a certain DBRoot; restoring the DBRoot to an "empty" state. inBuf
// is a COLUMN2 record read from a meta-data bulk rollback file.  This function
// is limited to rolling back the extent changes to a specific DBRoot.
//
// inBuf - latest column extent record to be parsed from meta-data file
//
// This function exists to handle version3 metadata input files, in case a user:
// 1. ungracefully took their system down with a table lock in place
// 2. upgraded to a release that supported version4 (used to support the new
//    segment file numbering).
// 3. then brought infinidb up, and DMLProc triggered this function to execute
//    a bulk rollback (during system startup) using a leftover version3
//    formatted meta data file.
//
// With Version3, we always wrote the expected starting segment number for an
// empty DBRoot, in a COLUMN2 record.  With Version4, we always write a 0 for
// the starting segment number, because the starting segment number is undeter-
// mined.  So this adaptor function changes any segment number for the COLUMN2
// record to a 0, to be 100% compatible with the Version4 format.
//------------------------------------------------------------------------------
void BulkRollbackMgr::deleteColumn2ExtentsV3 ( const char* inBuf )
{
    char        recType[100];
    OID         columnOID;
    uint32_t   dbRootHwm;
    uint32_t   partNumHwm;
    uint32_t   segNumHwm;
    int         colTypeInt;
    char        colTypeName[100];
    uint32_t   colWidth;
    int         compressionType = 0; // optional parameter

    // Read meta-data record
    int numFields = sscanf(inBuf, "%s %u %u %u %u %d %s %u %d",
                           recType, &columnOID,
                           &dbRootHwm, &partNumHwm, &segNumHwm,
                           &colTypeInt, colTypeName, &colWidth, &compressionType );

    if (numFields < 8) // compressionType is optional
    {
        std::ostringstream oss;
        oss << "Invalid COLUM2 record in meta-data file " <<
            fMetaFileName << "; record-<" << inBuf << ">" << std::endl;

        throw WeException( oss.str(), ERR_INVALID_PARAM );
    }

    std::ostringstream revisedBuf;
    uint32_t revisedSegNumHwm = 0;
    revisedBuf << recType     << ' ' <<
               columnOID   << ' ' <<
               dbRootHwm   << ' ' <<
               partNumHwm  << ' ' <<
               revisedSegNumHwm   << ' ' <<
               colTypeInt  << ' ' <<
               colTypeName << ' ' <<
               colWidth    << ' ';

    if (numFields > 8)
        revisedBuf << compressionType;

    deleteColumn2ExtentsV4( revisedBuf.str().c_str() );
}

//@bug 4091: V4 support for adding DBRoot
//------------------------------------------------------------------------------
// Delete all the column extents (from the extent map and the db files) that
// logically follow the HWM extent contained in inBuf; where inBuf is a
// COLUMN1 record read from a meta-data bulk rollback file.  This function
// is limited to rolling back the extent changes to a specific DBRoot.
//
// inBuf - latest column extent record to be parsed from meta-data file
//------------------------------------------------------------------------------
void BulkRollbackMgr::deleteColumn1ExtentsV4 ( const char* inBuf )
{
    char        recType[100];
    OID         columnOID;
    uint32_t   dbRootHwm;
    uint32_t   partNumHwm;
    uint32_t   segNumHwm;
    HWM         lastLocalHwm;
    int         colTypeInt;
    CalpontSystemCatalog::ColDataType colType;
    char        colTypeName[100];
    uint32_t   colWidth;
    int         compressionType = 0; // optional parameter

    // Read meta-data record
    int numFields = sscanf(inBuf, "%s %u %u %u %u %u %d %s %u %d",
                           recType, &columnOID,
                           &dbRootHwm, &partNumHwm, &segNumHwm, &lastLocalHwm,
                           &colTypeInt, colTypeName, &colWidth, &compressionType );
    colType = (CalpontSystemCatalog::ColDataType)colTypeInt;

    if (numFields < 9) // compressionType is optional
    {
        std::ostringstream oss;
        oss << "Invalid COLUM1 record in meta-data file " <<
            fMetaFileName << "; record-<" << inBuf << ">" << std::endl;

        throw WeException( oss.str(), ERR_INVALID_PARAM );
    }

    std::ostringstream msg0073Text;
    msg0073Text << "DBRoot-" << dbRootHwm << " (column extent)";
    logAMessage( logging::LOG_TYPE_INFO,
                 logging::M0073, columnOID, msg0073Text.str() );

    // Delete extents from the extentmap
    std::ostringstream msg0074Text;
    msg0074Text << "Restoring HWM column extent: " <<
                "dbRoot-"  << dbRootHwm  <<
                "; part#-" << partNumHwm <<
                "; seg#-"  << segNumHwm  <<
                "; hwm-"   << lastLocalHwm;
    logAMessage( logging::LOG_TYPE_INFO,
                 logging::M0074, columnOID, msg0074Text.str() );
    fAllColDctOIDs.insert( columnOID );

    // Create the object responsible for restoring the extents in the db files.
    BulkRollbackFile* fileRestorer = makeFileRestorer(compressionType);
    boost::scoped_ptr<BulkRollbackFile> refBulkRollbackFile(fileRestorer);

    // DMC-We should probably change this to build up a list of BRM changes,
    //     and wait to make the call(s) to rollback the BRM changes "after" we
    //     have restored the db files, and purged PrimProc FD and block cache.
    int rc = BRMWrapper::getInstance()->rollbackColumnExtents_DBroot (
                 columnOID,
                 false,                  // false -> Don't delete all extents (rollback
                 (uint16_t)dbRootHwm,   //    to specified dbroot, partition, etc.)
                 partNumHwm,
                 (uint16_t)segNumHwm,
                 lastLocalHwm );

    if (rc != NO_ERROR)
    {
        WErrorCodes ec;
        std::ostringstream oss;
        oss << "Error rolling back column extents from extent map for " <<
            columnOID <<
            "; dbRoot-"    << dbRootHwm    <<
            "; partition-" << partNumHwm   <<
            "; segment-"   << segNumHwm    <<
            "; hwm-"       << lastLocalHwm <<
            "; "           << ec.errorString(rc);

        throw WeException( oss.str(), ERR_BRM_BULK_RB_COLUMN );
    }

    // Determine the exact rollback point for the extents we are rolling back to
    const unsigned BLKS_PER_EXTENT =
        (BRMWrapper::getInstance()->getExtentRows() * colWidth) / BYTE_PER_BLOCK;
    uint32_t lastBlkOfCurrStripe = 0;
    uint32_t lastBlkOfPrevStripe = 0;

    if ((lastLocalHwm + 1) <= BLKS_PER_EXTENT)
    {
        lastBlkOfCurrStripe = BLKS_PER_EXTENT - 1;
    }
    else
    {
        lastBlkOfPrevStripe = lastLocalHwm -
                              (lastLocalHwm % BLKS_PER_EXTENT) - 1;
        lastBlkOfCurrStripe = lastBlkOfPrevStripe + BLKS_PER_EXTENT;
    }

    uint32_t dbRoot  = dbRootHwm;
    uint32_t partNum = partNumHwm;
    std::string segFileListErrMsg;

    // Delete extents from the database files.
    // Loop through all partitions (starting with the HWM partition partNumHwm),
    // deleting or restoring applicable extents.  We stay in loop till we
    // reach a partition that has no column segment files to roll back.
    bool useHdfs = IDBPolicy::useHdfs();

    while ( 1 )
    {
        std::vector<uint32_t> segList;
        std::string dirName;
        rc = fileRestorer->buildDirName( columnOID, dbRoot, partNum, dirName );

        if (rc != NO_ERROR)
        {
            WErrorCodes ec;
            std::ostringstream oss;
            oss << "Bulk rollback error constructing path for column " <<
                columnOID <<
                "; dbRoot-"    << dbRoot    <<
                "; partition-" << partNum   <<
                "; "           << ec.errorString(rc);

            throw WeException( oss.str(), rc );
        }

        rc = getSegFileList( dirName, useHdfs, segList, segFileListErrMsg );

        if (rc != NO_ERROR)
        {
            WErrorCodes ec;
            std::ostringstream oss;
            oss << "Bulk rollback error for column " << columnOID <<
                "; directory-" << dirName <<
                "; " << segFileListErrMsg <<
                "; " << ec.errorString(rc);

            throw WeException( oss.str(), rc );
        }

        if (segList.size() == 0)
            break; // Exit loop when we reach empty partition

        for (unsigned int kk = 0; kk < segList.size(); kk++)
        {
            uint32_t segNum = segList[kk];

            if ( partNum == partNumHwm )
            {
                // Don't rollback an OutOfService extent in the HWM partition
                bool bFound;
                int extState;
                rc = BRMWrapper::getInstance()->getExtentState(
                         columnOID, partNum, segNum, bFound, extState );

                if (rc != NO_ERROR)
                {
                    WErrorCodes ec;
                    std::ostringstream oss;
                    oss << "Bulk rollback error for column " << columnOID <<
                        "; Unable to get extent state for part-" << partNum <<
                        "; seg-" << segNum <<
                        "; " << ec.errorString(rc);

                    throw WeException( oss.str(), rc );
                }

                if ((bFound) && (extState == BRM::EXTENTOUTOFSERVICE))
                    continue;

                if ( segNum < segNumHwm )
                {
                    fileRestorer->truncateSegmentFile ( columnOID,
                                                        dbRoot,
                                                        partNum,
                                                        segNum,
                                                        (lastBlkOfCurrStripe + 1) );
                } // end of (segNum < segNumHwm)

                else if ( segNum > segNumHwm )
                {
                    if (lastBlkOfPrevStripe > 0)
                    {
                        fileRestorer->truncateSegmentFile ( columnOID,
                                                            dbRoot,
                                                            partNum,
                                                            segNum,
                                                            (lastBlkOfPrevStripe + 1) );
                    }
                    // lastBlkOfPrevStripe = 0, means there was no previous
                    // stripe in this partition.  The HWM block was in the
                    // first stripe.  In this case we can delete any segment
                    // files added to this partition that follow segNumHwm.
                    else
                    {
                        std::string segFileName;
                        fileRestorer->buildSegmentFileName ( columnOID,
                                                             true,    // column segment file
                                                             dbRoot,
                                                             partNum,
                                                             segNum,
                                                             segFileName );

                        createFileDeletionEntry( columnOID,
                                                 true, // column segment file
                                                 dbRoot, partNum, segNum, segFileName );
                    }
                } // end of (segNum > segNumHwm)

                else // segNum == segNumHwm
                {
                    if (lastBlkOfCurrStripe == lastLocalHwm)
                    {
                        fileRestorer->truncateSegmentFile ( columnOID,
                                                            dbRoot,
                                                            partNum,
                                                            segNum,
                                                            (lastBlkOfCurrStripe + 1) );
                    }
                    else
                    {
                        bool restoreChunk =
                            fileRestorer->doWeReInitExtent(columnOID,
                                                           dbRoot, partNum, segNum);

                        // For compressed data, if there is no backup chunk to
                        // restore (restoreChunk is false), we still restore
                        // the compressed headers to their previous setting.
                        // This would happen if DBRoot HWM was not on full ex-
                        // tent boundary when it was moved.  If/when cpimport
                        // reaches this migrated DBRoot in the middle of an
                        // import, we do not create a backup chunk file.  We
                        // instead only fill-in the HWM extent with empty row
                        // markers.  So no backup and restore is necessary. Only
                        // need to truncate any extra extents that were added.

                        // Reinit last extent and truncate the remainder,
                        // starting with the next block following the HWM block.
                        fileRestorer->reInitTruncColumnExtent ( columnOID,
                                                                dbRoot,
                                                                partNum,
                                                                segNum,
                                                                (lastLocalHwm + 1),
                                                                (lastBlkOfCurrStripe - lastLocalHwm),
                                                                colType,
                                                                colWidth,
                                                                restoreChunk );
                    }
                } // end of (segNum == segNumHwm)
            }
            else // ( partNum > partNumHwm )
            {
                // Delete any files added to subsequent partitions
                std::string segFileName;
                fileRestorer->buildSegmentFileName ( columnOID,
                                                     true,    // column segment file
                                                     dbRoot,
                                                     partNum,
                                                     segNum,
                                                     segFileName );

                createFileDeletionEntry( columnOID,
                                         true, // column segment file
                                         dbRoot, partNum, segNum, segFileName );
            }
        } // loop thru all the potential segment files in a partition

        partNum++;

    } // end of loop to go thru all partitions till we find last segment file
}

//@bug 4091: V4 support for adding DBRoot
//------------------------------------------------------------------------------
// Delete all the column extents (from the extent map and the db files) that
// fall in a certain DBRoot; restoring the DBRoot to an "empty" state. inBuf
// is a COLUMN2 record read from a meta-data bulk rollback file.  This function
// is limited to rolling back the extent changes to a specific DBRoot.
//
// inBuf - latest column extent record to be parsed from meta-data file
//------------------------------------------------------------------------------
void BulkRollbackMgr::deleteColumn2ExtentsV4 ( const char* inBuf )
{
    char        recType[100];
    OID         columnOID;
    uint32_t   dbRootHwm;
    uint32_t   partNumHwm;
    uint32_t   segNumHwm;
    HWM         lastLocalHwm = 0;
    int         colTypeInt;
    char        colTypeName[100];
    uint32_t   colWidth;
    int         compressionType = 0; // optional parameter

    // Read meta-data record
    int numFields = sscanf(inBuf, "%s %u %u %u %u %d %s %u %d",
                           recType, &columnOID,
                           &dbRootHwm, &partNumHwm, &segNumHwm,
                           &colTypeInt, colTypeName, &colWidth, &compressionType );

    if (numFields < 8) // compressionType is optional
    {
        std::ostringstream oss;
        oss << "Invalid COLUM2 record in meta-data file " <<
            fMetaFileName << "; record-<" << inBuf << ">" << std::endl;

        throw WeException( oss.str(), ERR_INVALID_PARAM );
    }

    std::ostringstream msg0073Text;
    msg0073Text << "DBRoot-" << dbRootHwm << " (column extent)";
    logAMessage( logging::LOG_TYPE_INFO,
                 logging::M0073, columnOID, msg0073Text.str() );

    // @bug 5644 - If user dropped all partitions in a dbroot, partNumHwm will
    // be 0, but we may start importing into part# > 0 (to line up with other
    // DBRoots).  Use extent map to find first partition added by this import.
    std::vector<struct BRM::EMEntry> extEntries;
    int rc = BRMWrapper::getInstance()->getExtents_dbroot( columnOID,
             extEntries, dbRootHwm );

    if (rc != NO_ERROR)
    {
        WErrorCodes ec;
        std::ostringstream oss;
        oss << "Error getting extent list from extent map for " <<
            columnOID <<
            "; dbRoot-"    << dbRootHwm    <<
            "; "           << ec.errorString(rc);

        throw WeException( oss.str(), ERR_BRM_BULK_RB_COLUMN );
    }

    uint32_t part1 = partNumHwm; // lowest part# for column and DBRoot

    if (extEntries.size() > 0)
    {
        part1 = extEntries[0].partitionNum;

        for (unsigned int kk = 0; kk < extEntries.size(); kk++)
        {
            if (extEntries[kk].partitionNum < part1)
                part1 = extEntries[kk].partitionNum;
        }
    }

    // Delete extents from the extentmap
    std::ostringstream msg0074Text;
    msg0074Text << "Restoring empty DBRoot. "
                "dbRoot-"  << dbRootHwm  <<
                "; part#-" << partNumHwm <<
                "; seg#-"  << segNumHwm  <<
                "; hwm-"   << lastLocalHwm <<
                "; delete starting at part#-" << part1;
    logAMessage( logging::LOG_TYPE_INFO,
                 logging::M0074, columnOID, msg0074Text.str() );
    fAllColDctOIDs.insert( columnOID );

    // Reset partNumHwm to partNum taken from extent map
    partNumHwm = part1;

    // Create the object responsible for restoring the extents in the db files.
    BulkRollbackFile* fileRestorer = makeFileRestorer(compressionType);
    boost::scoped_ptr<BulkRollbackFile> refBulkRollbackFile(fileRestorer);

    // DMC-We should probably change this to build up a list of BRM changes,
    //     and wait to make the call(s) to rollback the BRM changes "after" we
    //     have restored the db files, and purged PrimProc FD and block cache.
    rc = BRMWrapper::getInstance()->rollbackColumnExtents_DBroot (
             columnOID,
             true,           // true -> delete all extents (restore to empty DBRoot)
             (uint16_t)dbRootHwm,
             partNumHwm,
             (uint16_t)segNumHwm,
             lastLocalHwm );

    if (rc != NO_ERROR)
    {
        WErrorCodes ec;
        std::ostringstream oss;
        oss << "Error rolling back column extents from extent map for " <<
            columnOID <<
            "; dbRoot-"    << dbRootHwm    <<
            "; partition-" << partNumHwm   <<
            "; segment-"   << segNumHwm    <<
            "; hwm-"       << lastLocalHwm <<
            "; "           << ec.errorString(rc);

        throw WeException( oss.str(), ERR_BRM_BULK_RB_COLUMN );
    }

    uint32_t dbRoot  = dbRootHwm;
    uint32_t partNum = partNumHwm;
    std::string segFileListErrMsg;

    // Delete extents from the database files.
    // Loop through all partitions (starting with the HWM partition partNumHwm),
    // deleting or restoring applicable extents.  We stay in loop till we
    // reach a partition that has no column segment files to roll back.
    bool useHdfs = IDBPolicy::useHdfs();

    while ( 1 )
    {
        std::vector<uint32_t> segList;
        std::string dirName;
        rc = fileRestorer->buildDirName( columnOID, dbRoot, partNum, dirName );

        if (rc != NO_ERROR)
        {
            WErrorCodes ec;
            std::ostringstream oss;
            oss << "Bulk rollback error constructing path for column " <<
                columnOID <<
                "; dbRoot-"    << dbRoot    <<
                "; partition-" << partNum   <<
                "; "           << ec.errorString(rc);

            throw WeException( oss.str(), rc );
        }

        rc = getSegFileList( dirName, useHdfs, segList, segFileListErrMsg );

        if (rc != NO_ERROR)
        {
            WErrorCodes ec;
            std::ostringstream oss;
            oss << "Bulk rollback error for column " << columnOID <<
                "; directory-" << dirName <<
                "; " << segFileListErrMsg <<
                "; " << ec.errorString(rc);

            throw WeException( oss.str(), rc );
        }

        if (segList.size() == 0)
            break; // Exit loop when we reach empty partition

        for (unsigned int kk = 0; kk < segList.size(); kk++)
        {
            uint32_t segNum = segList[kk];

            // Delete any files added to subsequent partitions
            std::string segFileName;
            fileRestorer->buildSegmentFileName ( columnOID,
                                                 true,    // column segment file
                                                 dbRoot,
                                                 partNum,
                                                 segNum,
                                                 segFileName );

            createFileDeletionEntry( columnOID,
                                     true, // column segment file
                                     dbRoot, partNum, segNum, segFileName );
        } // loop thru all the potential segment files in a partition

        partNum++;

    } // end of loop to go thru all partitions till we find last segment file
}

//------------------------------------------------------------------------------
// Delete dictionary store extents based on COLUMN1 record input
//------------------------------------------------------------------------------
void BulkRollbackMgr::deleteDctnryExtents ( )
{
    if (fVersion == 3)
        deleteDctnryExtentsV3( );
    else
        deleteDctnryExtentsV4( );
}

//------------------------------------------------------------------------------
// Delete all the dictionary store extents (from the extent map and db files)
// that logically follow the extents contained in fPendingDctnryExtents; where
// fPendingDctnryExtents is a vector of DSTORE1 and DSTORE2 records (for a
// specific column OID), read from a meta-data bulk rollback file.  This
// function is limited to rolling back the extent changes to a specific DBRoot.
//
// This function exists to handle version3 metadata input files, in case a user:
// 1. ungracefully took their system down with a table lock in place
// 2. upgraded to a release that supported ersion4 (used to support the new
//    segment file numbering).
// 3. then brought infinidb up, and DMLProc triggered this function to execute
//    a bulk rollback (during system startup) using a leftover version3
//    formatted meta data file.
//
// With Version3, we always wrote out DSTORE1 record for each existing store
// file, and a DSTORE2 record for each store file not present in the relevant
// partition.  For an empty DBRoot, a DSTORE2 record was written for all the
// store files not present in the relevant partition.
// With Version4, no trailing DSTORE2 records are written for the store files
// not present.  A Single DSTORE2 record is still written for an empty DBRoot.
// So this adaptor function strips out the unnecessary trailing DSTORE2
// records, to be compatible with the Version4 format.
//------------------------------------------------------------------------------
void BulkRollbackMgr::deleteDctnryExtentsV3 ( )
{
    for (unsigned i = 0; i < fPendingDctnryExtents.size(); ++i)
    {
        if (!fPendingDctnryExtents[i].fWithHwm)
        {
            if (i == 0)
            {
                fPendingDctnryExtents[i].fSegNum = 0;
                fPendingDctnryExtents.resize(1);
            }
            else
            {
                fPendingDctnryExtents.resize(i);
            }

            break;
        }
    }

    deleteDctnryExtentsV4( );
}

//@bug 4091: V4 support for adding DBRoot
//------------------------------------------------------------------------------
// Delete all the dictionary store extents (from the extent map and db files)
// that logically follow the extents contained in fPendingDctnryExtents; where
// fPendingDctnryExtents is a vector of DSTORE1 and DSTORE2 records (for a
// specific column OID), read from a meta-data bulk rollback file.  This
// function is limited to rolling back the extent changes to a specific DBRoot.
//------------------------------------------------------------------------------
void BulkRollbackMgr::deleteDctnryExtentsV4 ( )
{
    std::ostringstream msg0073Text;
    msg0073Text << "DBRoot-" << fPendingDctnryStoreDbRoot <<
                " (dictionary extent)";
    logAMessage( logging::LOG_TYPE_INFO,
                 logging::M0073, fPendingDctnryStoreOID, msg0073Text.str() );

    if (fPendingDctnryExtents.size() == 0)
        return;

    std::vector<uint16_t>  segNums;
    std::vector<BRM::HWM_t> hwms;

    // Build up list of HWM's to be sent to DBRM for extentmap rollback
    for (unsigned i = 0; i < fPendingDctnryExtents.size(); i++)
    {
        if ( !fPendingDctnryExtents[i].fWithHwm )
            break;

        segNums.push_back( fPendingDctnryExtents[i].fSegNum );
        hwms.push_back(    fPendingDctnryExtents[i].fHwm    );
    }

    // @bug 5644 - If user dropped all partitions in a dbroot, fPartNum will
    // be 0, but we may start importing into part# > 0 (to line up with other
    // DBRoots).  Use extent map to find first partition added by this import.
    uint32_t part1 = fPendingDctnryExtents[0].fPartNum; // lowest part# for

    // OID and DBRoot
    if (hwms.size() == 0) // empty DBRoot case
    {
        std::vector<struct BRM::EMEntry> extEntries;
        int rc = BRMWrapper::getInstance()->getExtents_dbroot(
                     fPendingDctnryStoreOID,
                     extEntries,
                     fPendingDctnryStoreDbRoot );

        if (rc != NO_ERROR)
        {
            WErrorCodes ec;
            std::ostringstream oss;
            oss << "Error getting extent list from extent map for " <<
                fPendingDctnryStoreOID <<
                "; dbRoot-"    << fPendingDctnryStoreDbRoot <<
                "; "           << ec.errorString(rc);

            throw WeException( oss.str(), ERR_BRM_BULK_RB_COLUMN );
        }

        if (extEntries.size() > 0)
        {
            part1 = extEntries[0].partitionNum;

            for (unsigned int kk = 0; kk < extEntries.size(); kk++)
            {
                if (extEntries[kk].partitionNum < part1)
                    part1 = extEntries[kk].partitionNum;
            }
        }
    }

    // Delete extents from the extentmap using hwms vector
    std::ostringstream msg0074Text;
    msg0074Text << "Restoring HWM dictionary store extents: "
                "dbRoot-" << fPendingDctnryStoreDbRoot         <<
                "; part#-" << fPendingDctnryExtents[0].fPartNum <<
                "; HWM(s): ";

    for (unsigned int k = 0; k < hwms.size(); k++)
    {
        if (k > 0)
            msg0074Text << ", ";

        msg0074Text << hwms[k];
    }

    if (hwms.size() == 0)
        msg0074Text << "; delete starting at part#-" << part1;

    logAMessage( logging::LOG_TYPE_INFO,
                 logging::M0074, fPendingDctnryStoreOID, msg0074Text.str() );
    fAllColDctOIDs.insert( fPendingDctnryStoreOID );

    // Reset partNum to partNum taken from extent map
    uint32_t partNum = part1;

    // Create the object responsible for restoring the extents in the db files.
    BulkRollbackFile* fileRestorer = makeFileRestorer(
                                         fPendingDctnryStoreCompressionType);
    boost::scoped_ptr<BulkRollbackFile> refBulkRollbackFile(fileRestorer);

    // DMC-We should probably change this to build up a list of BRM changes,
    //     and wait to make the call(s) to rollback the BRM changes "after" we
    //     have restored the db files, and purged PrimProc FD and block cache.
    int rc = BRMWrapper::getInstance()->rollbackDictStoreExtents_DBroot (
                 fPendingDctnryStoreOID,
                 (uint16_t)fPendingDctnryStoreDbRoot,
                 partNum,
                 segNums,
                 hwms );

    if (rc != NO_ERROR)
    {
        WErrorCodes ec;
        std::ostringstream oss;
        oss << "Error rolling back dictionary extents from extent map for " <<
            fPendingDctnryStoreOID <<
            "; partNum-" << partNum <<
            "; "         << ec.errorString(rc);

        throw WeException( oss.str(), ERR_BRM_BULK_RB_DCTNRY );
    }

    // Assign constants used later in calculating exact rollback point
    const unsigned COL_WIDTH       = 8;
    const unsigned ROWS_PER_EXTENT = BRMWrapper::getInstance()->getExtentRows();
    const unsigned BLKS_PER_EXTENT =
        (ROWS_PER_EXTENT * COL_WIDTH) / BYTE_PER_BLOCK;

    uint32_t dbRoot  = fPendingDctnryStoreDbRoot;
    std::string segFileListErrMsg;

    // Delete extents from the database files.
    // Loop through all partitions (starting with the HWM partition fPartNum),
    // deleting or restoring applicable extents.  We stay in loop till we
    // reach a partition that has no dctnry store segment files to roll back.
    bool useHdfs = IDBPolicy::useHdfs();

    while ( 1 )
    {
        std::vector<uint32_t> segList;
        std::string dirName;
        rc = fileRestorer->buildDirName( fPendingDctnryStoreOID,
                                         dbRoot, partNum, dirName );

        if (rc != NO_ERROR)
        {
            WErrorCodes ec;
            std::ostringstream oss;
            oss << "Bulk rollback error constructing path for dictionary " <<
                fPendingDctnryStoreOID <<
                "; dbRoot-"    << dbRoot    <<
                "; partition-" << partNum   <<
                "; "           << ec.errorString(rc);

            throw WeException( oss.str(), rc );
        }

        rc = getSegFileList( dirName, useHdfs, segList, segFileListErrMsg );

        if (rc != NO_ERROR)
        {
            WErrorCodes ec;
            std::ostringstream oss;
            oss << "Bulk rollback error for dictionary " <<
                fPendingDctnryStoreOID    <<
                "; directory-" << dirName <<
                "; " << segFileListErrMsg <<
                "; " << ec.errorString(rc);

            throw WeException( oss.str(), rc );
        }

        if (segList.size() == 0)
            break;

        for (unsigned int kk = 0; kk < segList.size(); kk++)
        {
            uint32_t segNum = segList[kk];
            bool      reInit = false;
            uint32_t segIdx = 0;

            // For each segment file found in dirName, see if the file is in
            // the list of pending dictionary files to be rolled back; if the
            // file is found, then roll it back, if not found, then delete it.
            for (unsigned nn = 0; nn < fPendingDctnryExtents.size(); nn++)
            {
                if ((fPendingDctnryExtents[nn].fPartNum == partNum) &&
                        (fPendingDctnryExtents[nn].fSegNum  == segNum))
                {
                    if (fPendingDctnryExtents[nn].fWithHwm)
                    {
                        segIdx = nn; // found corresponding file in pending list
                        reInit = true;
                    }

                    break;
                }
            }

            if (reInit)
            {
                // Don't rollback an OutOfService extent in the HWM partition
                bool bFound;
                int extState;
                rc = BRMWrapper::getInstance()->getExtentState(
                         fPendingDctnryStoreOID, partNum, segNum, bFound, extState );

                if (rc != NO_ERROR)
                {
                    WErrorCodes ec;
                    std::ostringstream oss;
                    oss << "Bulk rollback error for dctnry store " <<
                        fPendingDctnryStoreOID <<
                        "; Unable to get extent state for part-" << partNum <<
                        "; seg-" << segNum <<
                        "; " << ec.errorString(rc);

                    throw WeException( oss.str(), rc );
                }

                if ((bFound) && (extState == BRM::EXTENTOUTOFSERVICE))
                    continue;

                HWM hwm = fPendingDctnryExtents[segIdx].fHwm;

                // Determine the exact rollback point for the extent
                // we are rolling back to
                uint32_t lastBlkOfCurrStripe = hwm -
                                               (hwm % BLKS_PER_EXTENT) + BLKS_PER_EXTENT - 1;

                // Reinit last extent and truncate the remainder,
                // starting with the next block following the HWM block.
                fileRestorer->reInitTruncDctnryExtent (
                    fPendingDctnryStoreOID,
                    dbRoot,
                    partNum,
                    segNum,
                    (hwm + 1),
                    (lastBlkOfCurrStripe - hwm));
            }
            else // don't keep this segment file
            {
                std::string segFileName;
                fileRestorer->buildSegmentFileName ( fPendingDctnryStoreOID,
                                                     false,    // not a column segment file
                                                     dbRoot,
                                                     partNum,
                                                     segNum,
                                                     segFileName );

                createFileDeletionEntry( fPendingDctnryStoreOID,
                                         false, // not a column segment file
                                         dbRoot, partNum, segNum,
                                         segFileName );
            }
        } // loop thru all the potential segment files in a partition

        partNum++;

    } //end of loop to go thru all partitions till we find last segment file

    fPendingDctnryExtents.clear ( );
}

//------------------------------------------------------------------------------
// Add specified segment file to the list of files to be deleted.  We are
// accumulating the list of file names so that they can be deleted in reverse
// order.  (See deleteDbFiles()).
//------------------------------------------------------------------------------
//@bug 4241 Delete files in reverse order
void BulkRollbackMgr::createFileDeletionEntry(
    OID       columnOID,
    bool      fileTypeFlag,
    uint32_t dbRoot,
    uint32_t partNum,
    uint32_t segNum,
    const std::string& segFileName )
{
    File f;
    f.oid          = columnOID;
    f.fid          = ((fileTypeFlag) ? 1 : 0); // use fid for file type flag
    f.fPartition   = partNum;
    f.fSegment     = segNum;
    f.fDbRoot      = dbRoot;
    f.fSegFileName = segFileName;
    fPendingFilesToDelete.push_back( f );
}

//------------------------------------------------------------------------------
// Delete db files for a column and DBRoot that are waiting to be deleted.
// Files are deleted in reverse order, (see Bug 4241) to facilitate partial
// bulk rollback failures and retries.
//------------------------------------------------------------------------------
//@bug 4241 Delete files in reverse order
void BulkRollbackMgr::deleteDbFiles( )
{
    // Okay to use a BulkRollbackFile object, because we are only calling the
    // deleteSegmentFile() method which is a base class function with no
    // polymorphic behavior.  (In other words, we don't need to worry about
    // employing a BulkRollbackFileCompressed object for a compressed column.)
    BulkRollbackFile fileRestorer( this );

    unsigned int fileCount = fPendingFilesToDelete.size();

    for (int i = fileCount - 1; i >= 0; --i)
    {
        fileRestorer.deleteSegmentFile(
            fPendingFilesToDelete[i].oid,
            ((fPendingFilesToDelete[i].fid > 0) ? true : false),
            fPendingFilesToDelete[i].fDbRoot,
            fPendingFilesToDelete[i].fPartition,
            fPendingFilesToDelete[i].fSegment,
            fPendingFilesToDelete[i].fSegFileName );
    }

    fPendingFilesToDelete.clear();
}

//------------------------------------------------------------------------------
// Get list of segment files found in the specified db directory path.
// If bIncludeAlternateSegFileNames is false, then only "FILENNN.cdf" files
// will be considered.
// If bIncludeAlternateSegFileNames is true, then in addition to "FILENNN.cdf"
// files, "FILENNN.cdf.orig" and "FILENNN.cdf.tmp" files will be considered.
// The latter case (flag set to true) is used on connection with HDFS.
//------------------------------------------------------------------------------
/* static */
int BulkRollbackMgr::getSegFileList(
    const std::string& dirName,
    bool bIncludeAlternateSegFileNames,
    std::vector<uint32_t>& segList,
    std::string& errMsg )
{
    const unsigned int DB_FILE_PREFIX_LEN = DB_FILE_PREFIX.length();
    segList.clear();
    std::set<uint32_t> segSet;

    // Return no segment files if partition directory path does not exist
    if ( !IDBPolicy::isDir( dirName.c_str() ) )
        return NO_ERROR;

    list<string> dircontents;

    if ( IDBPolicy::listDirectory( dirName.c_str(), dircontents ) == 0 )
    {
        list<string>::iterator iend = dircontents.end();

        for ( list<string>::iterator i = dircontents.begin(); i != iend; ++i )
        {
            boost::filesystem::path filepath( *i );

#if BOOST_VERSION >= 105200
            //@bug 4989 - stem() and extension() return a temp path object by
            // value so be sure to store in a string and not a string reference.
            const std::string fileBase = filepath.stem().generic_string();
            const std::string fileExt  = filepath.extension().generic_string();
#else
            const std::string& fileBase = filepath.stem();
            const std::string& fileExt  = filepath.extension();
#endif
            //std::cout << "getSegFileList: " << fileBase << " / " <<
            //  fileExt << std::endl;

            // Select files of interest ("FILE*.cdf")
            bool bMatchFound = false;
            unsigned int segNumStrLen = 0;

            if (fileBase.compare(0, DB_FILE_PREFIX_LEN, DB_FILE_PREFIX) == 0)
            {
                segNumStrLen = fileBase.length() - DB_FILE_PREFIX_LEN;

                // Select primary "FILE*.cdf" files
                if (fileExt == DB_FILE_EXTENSION)
                {
                    bMatchFound  = true;
                    //std::cout << "getSegFileList: match *.cdf" << std::endl;
                }
                // Select alternate files of interest ("FILE*.cdf.orig" and
                // "FILE*.cdf.tmp") used for HDFS backup and rollback.
                else if (bIncludeAlternateSegFileNames)
                {
                    if ((fileExt == DB_FILE_EXTENSION_ORIG) ||
                            (fileExt == DB_FILE_EXTENSION_TMP))
                    {
                        //std::cout << "getSegFileList: match *.tmp or *.orig"<<
                        //    std::endl;
                        unsigned int extLen = DB_FILE_EXTENSION.length();

                        if ((fileBase.length() >= extLen) &&
                                (fileBase.compare((fileBase.length() - extLen),
                                                  extLen,
                                                  DB_FILE_EXTENSION) == 0))
                        {
                            //std::cout << "getSegFileList: match *cdf.tmp or "
                            //  "*cdf.orig" << std::endl;
                            bMatchFound   = true;
                            segNumStrLen -= extLen;
                        }
                    }
                }
            } // if fileBase.compare() shows filename starting with "FILE"

            if (bMatchFound)
            {
                const std::string& fileSeg = fileBase.substr(
                                                 DB_FILE_PREFIX_LEN, segNumStrLen);
                bool bDbFile = true;

                const unsigned fileSegLen = fileSeg.length();

                if (fileSegLen >= 3)
                {
                    for (unsigned int k = 0; k < fileSegLen; k++)
                    {
                        if ( !isdigit(fileSeg[k]) )
                        {
                            bDbFile = false;
                            break;
                        }
                    }

                    if (bDbFile)
                    {
                        uint32_t segNum = atoi( fileSeg.c_str() );
                        segSet.insert( segNum );
                    }
                } // filename must have 3 or more digits representing seg number
            } // found "FILE*.cdf", "FILE*.cdf.orig", or "FILE*.cdf.tmp" file
        } // for each file in directory

        if (segSet.size() > 0)
        {
            std::set<uint32_t>::const_iterator iter = segSet.begin();

            while (iter != segSet.end())
            {
                //std::cout << "getSegFileList: Adding segnum " << *iter <<
                //    " to the segment list" << std::endl;
                segList.push_back(*iter);
                ++iter;
            }
        }

        return NO_ERROR;

    } // if listDirectory() success
    else
    {
        segList.clear();
        errMsg = "Unable to listDirectory in getSegFileList()";
        return ERR_BULK_ROLLBACK_SEG_LIST;
    }
}

//------------------------------------------------------------------------------
// Make/return the applicable BulkRollbackFile object used to rollback/restore
// a db file, based on the compression type and HDFS setting.
//------------------------------------------------------------------------------
BulkRollbackFile* BulkRollbackMgr::makeFileRestorer(int compressionType)
{
    BulkRollbackFile* fileRestorer = 0;

    if (compressionType)
    {
        if (IDBPolicy::useHdfs())
            fileRestorer = new BulkRollbackFileCompressedHdfs(this);
        else
            fileRestorer = new BulkRollbackFileCompressed(this);
    }
    else
    {
        fileRestorer = new BulkRollbackFile(this);
    }

    return fileRestorer;
}

//------------------------------------------------------------------------------
// Log a message to syslog.  columnOID and text are used depending on the msgId.
//
// logType   - type of message (debug, critical, etc)
// msgId     - message ID
// columnOID - column OID associated with this rollback message
// text      - message text
//------------------------------------------------------------------------------
void BulkRollbackMgr::logAMessage (
    logging::LOG_TYPE           logType,
    logging::Message::MessageID msgId,
    OID                         columnOID,
    const std::string&          text )
{
    logging::Message m( msgId );
    logging::Message::Args args;

    std::ostringstream ossTbl;
    ossTbl << fTableName << " (OID-" << fTableOID << ")";
    args.add( ossTbl.str() );

    if (msgId >= logging::M0073)
    {
        switch (msgId)
        {
            case logging::M0073:
            case logging::M0074:
            case logging::M0075:
            {
                args.add( (uint64_t)columnOID );
                break;
            }

            case logging::M0084:
            {
                args.add( (uint64_t)fLockID );
                break;
            }

            case logging::M0085:
            {
                args.add( (uint64_t)fLockID );
                break;
            }

            case logging::M0090:
            {
                // no other arg applicable for this message
                break;
            }
        }

        args.add( text );
    }

    m.format( args );

    // Log to syslog
    // Note that WARNING, ERROR and CRITICAL are logged to INFO as well as
    // their respective log files, so that the message will appear in context
    // with all the other INFO msgs used to track the flow of the rollback.
    switch (logType)
    {
        case logging::LOG_TYPE_DEBUG:
        {
            fSysLogger.logDebugMessage( m );
            break;
        }

        case logging::LOG_TYPE_INFO:
        {
            fSysLogger.logInfoMessage( m );
            break;
        }

        case logging::LOG_TYPE_WARNING:
        {
            fSysLogger.logWarningMessage( m );
            fSysLogger.logInfoMessage   ( m );
            break;
        }

        case logging::LOG_TYPE_ERROR:
        {
            fSysLogger.logErrorMessage( m );
            fSysLogger.logInfoMessage ( m );
            break;
        }

        default: // LOG_TYPE_CRITICAL
        {
            fSysLogger.logCriticalMessage( m );
            fSysLogger.logInfoMessage    ( m );
            break;
        }
    }

    // If fLog is defined then log to there, else log to cout.
    // Currently log msg0074 and msg0075 to console only if debug is enabled
    switch (msgId)
    {
        // Log the name of the table to be rolled back or restored
        case logging::M0072:
        {
            if (fLog)
            {
                std::ostringstream oss;
                oss << "Rolling back extents for table " <<
                    fTableName << " (OID-" << fTableOID << ")";
                fLog->logMsg( oss.str(), MSGLVL_INFO2 );
            }
            else
            {
                std::cout << "Rolling back extents for table " <<
                          fTableName << " (OID-" << fTableOID << ")" << std::endl;
            }

            break;
        }

        // Log the name of the table and column to be rolled back or restored
        case logging::M0073:
        {
            if (fLog)
            {
                std::ostringstream oss;
                oss << "Rolling back extents for table " <<
                    fTableName << " (OID-" << fTableOID <<
                    "); column " << columnOID << "; " << text;
                fLog->logMsg( oss.str(), MSGLVL_INFO2 );
            }
            else
            {
                std::cout << "Rolling back extents for table " <<
                          fTableName << " (OID-" << fTableOID <<
                          "); column " << columnOID << "; " << text << std::endl;
            }

            break;
        }

        // Log the rolling back of extent(s) from the extent map
        case logging::M0074:
        {
            if (fLog)
            {
                std::ostringstream oss;
                oss << "Rolling back extent map for table " <<
                    fTableName << " (OID-" << fTableOID <<
                    "); column " << columnOID << "; " << text;
                fLog->logMsg( oss.str(), MSGLVL_INFO2 );
            }
            else
            {
                if ( fDebugConsole )
                {
                    std::cout << "Rolling back extent map for table " <<
                              fTableName << " (OID-" << fTableOID <<
                              "); column " << columnOID << "; " << text << std::endl;
                }
            }

            break;
        }

        // Log the rolling back of extent(s) from the DB
        case logging::M0075:
        {
            if (fLog)
            {
                std::ostringstream oss;
                oss << "Rolling back db file for table " <<
                    fTableName << " (OID-" << fTableOID <<
                    "); column " << columnOID << "; " << text;
                fLog->logMsg( oss.str(), MSGLVL_INFO2 );
            }
            else
            {
                if ( fDebugConsole )
                {
                    std::cout << "Rolling back db file for table " <<
                              fTableName << " (OID-" << fTableOID <<
                              "); column " << columnOID << "; " << text << std::endl;
                }
            }

            break;
        }

        // Log the start of a bulk rollback
        case logging::M0084:
        {
            if (fLog)
            {
                std::ostringstream oss;
                oss << "Starting bulk rollback for table " <<
                    fTableName << " (OID-" << fTableOID <<
                    ", Lock-" << fLockID << ") in " << text;
                fLog->logMsg( oss.str(), MSGLVL_INFO2 );
            }
            else
            {
                std::cout << "Starting bulk rollback for table " <<
                          fTableName << " (OID-" << fTableOID <<
                          ", Lock-" << fLockID << ") in " << text << std::endl;
            }

            break;
        }

        // Log the end of a bulk rollback
        case logging::M0085:
        {
            if (fLog)
            {
                std::ostringstream oss;
                oss << "Ending bulk rollback for table " <<
                    fTableName << " (OID-" << fTableOID <<
                    ", Lock-" << fLockID << ") in " << text;
                fLog->logMsg( oss.str(), MSGLVL_INFO2 );
            }
            else
            {
                std::cout << "Ending bulk rollback for table " <<
                          fTableName << " (OID-" << fTableOID <<
                          ", Lock-" << fLockID << ") in " << text << std::endl;
            }

            break;
        }

        // Log skipping a DBRoot rollback because no meta-data file
        case logging::M0090:
        {
            if (fLog)
            {
                std::ostringstream oss;
                oss << "Nothing to rollback for table " <<
                    fTableName << " (OID-" << fTableOID <<
                    ") on DBRoot" << text;
                fLog->logMsg( oss.str(), MSGLVL_INFO2 );
            }
            else
            {
                std::cout << "Nothing to rollback for table " <<
                          fTableName << " (OID-" << fTableOID <<
                          ") on DBRoot" << text << std::endl;
            }

            break;
        }

        default:
        {
            break;
        }
    }
}

//------------------------------------------------------------------------------
// Standalone utility that can be used to delete bulk rollback files.
// WARNING: this function can return an exception (from call to boost
// remove_all() )
//------------------------------------------------------------------------------
/* static */
void BulkRollbackMgr::deleteMetaFile( OID tableOID )
{
    std::vector<uint16_t> dbRoots;
    Config::getRootIdList( dbRoots );

    // Loop through DBRoots for this PM
    for (unsigned m = 0; m < dbRoots.size(); m++)
    {
        std::string bulkRollbackPath( Config::getDBRootByNum(dbRoots[m]) );

        std::ostringstream oss;
        oss << '/' << DBROOT_BULK_ROLLBACK_SUBDIR << '/' << tableOID;
        std::string metaFileName = bulkRollbackPath;
        metaFileName            += oss.str();

        // Delete the main bulk rollback file
        IDBPolicy::remove( metaFileName.c_str() );

        // Unlink corresponding tmp file created by RBMetaWriter.
        std::string tmpMetaFileName = metaFileName;
        tmpMetaFileName += TMP_FILE_SUFFIX;
        IDBPolicy::remove( tmpMetaFileName.c_str() );

        // Recursively delete any HWM chunk backup files
        std::string bulkRollbackSubPath( metaFileName );
        bulkRollbackSubPath += DATA_DIR_SUFFIX;

        IDBPolicy::remove( bulkRollbackSubPath.c_str() );
    }
}

} //end of namespace
