/* 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_rbmetawriter.cpp 4737 2013-08-14 20:45:46Z bwilkinson $
*/

#include "we_rbmetawriter.h"

#include <cerrno>
#include <iostream>
#include <sstream>
#include <unistd.h>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/convenience.hpp>

#include "we_config.h"
#include "we_convertor.h"
#include "we_define.h"
#include "we_log.h"
#include "we_bulkrollbackmgr.h"
#include "idbcompress.h"
using namespace compress;
using namespace execplan;
#include "IDBDataFile.h"
#include "IDBFileSystem.h"
#include "IDBPolicy.h"
using namespace idbdatafile;

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

const char* VERSION3_REC    = "# VERSION: 3";
const int   VERSION3_REC_LEN = 12;
const char* VERSION4_REC    = "# VERSION: 4";
const int   VERSION4_REC_LEN = 12;
const char* COLUMN1_REC     = "COLUM1"; // HWM extent for a DBRoot
const int   COLUMN1_REC_LEN = 6;
const char* COLUMN2_REC     = "COLUM2"; // Placeholder for empty DBRoot
const int   COLUMN2_REC_LEN = 6;
const char* DSTORE1_REC     = "DSTOR1"; // HWM extent for a DBRoot
const int   DSTORE1_REC_LEN = 6;
const char* DSTORE2_REC     = "DSTOR2"; // Placeholder for empty DBRoot
const int   DSTORE2_REC_LEN = 6;

//--------------------------------------------------------------------------
// Local Function that prints contents of an RBChunkInfo object
//--------------------------------------------------------------------------
std::ostream& operator<<(std::ostream& os,
                         const WriteEngine::RBChunkInfo& chk)
{
    os << "OID-"    << chk.fOid       <<
       "; DBRoot-" << chk.fDbRoot    <<
       "; Part-"   << chk.fPartition <<
       "; Seg-"    << chk.fSegment   <<
       "; HWM-"    << chk.fHwm;

    return os;
}
}

namespace WriteEngine
{

//------------------------------------------------------------------------------
// Compare function used for set of RBChunkInfo objects.
//------------------------------------------------------------------------------
bool RBChunkInfoCompare::operator()
(const RBChunkInfo& lhs, const RBChunkInfo& rhs) const
{
    if (lhs.fOid < rhs.fOid)
    {
        return true;
    }

    if ((lhs.fOid == rhs.fOid) && (lhs.fSegment < rhs.fSegment))
    {
        return true;
    }

    return false;
}

//------------------------------------------------------------------------------
// RBMetaWriter constructor
//------------------------------------------------------------------------------
RBMetaWriter::RBMetaWriter (
    const std::string& appDesc,
    Log* logger ) : fMetaDataFile(NULL), fAppDesc(appDesc), fLog(logger), fCreatedSubDir(false)
{
}

//------------------------------------------------------------------------------
// Initialize this meta data file object using the specified table OID and name.
// We assume the application code calling this function, was able to acquire a
// table lock, meaning if there should happen to be any leftoever metadata files
// from a previous job, they can be deleted.
//------------------------------------------------------------------------------
void RBMetaWriter::init (
    OID tableOID,
    const std::string& tableName )
{
    fTableOID  = tableOID;
    fTableName = tableName;

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

    std::string metaFileName;
    std::ostringstream oss;
    oss << "/" << fTableOID;

    // Delete any files that collide with the file names we are going to need.
    // Construct the filenames; we will use a temporary file name until we are
    // finished creating, at which time we will rename the temp files.
    for (unsigned m = 0; m < dbRoots.size(); m++)
    {
        std::string bulkRollbackPath( Config::getDBRootByNum( dbRoots[m] ) );
        bulkRollbackPath += '/';
        bulkRollbackPath += DBROOT_BULK_ROLLBACK_SUBDIR;
        metaFileName      = bulkRollbackPath;
        metaFileName     += oss.str();

        std::string tmpMetaFileName = metaFileName;
        tmpMetaFileName += TMP_FILE_SUFFIX;

        // Delete any files that collide with the filenames we intend to use
        IDBPolicy::remove( metaFileName.c_str() );
        IDBPolicy::remove( tmpMetaFileName.c_str() );

        // Clear out any data subdirectory
        deleteSubDir( metaFileName );
    }
}

//------------------------------------------------------------------------------
// Saves snapshot of extentmap into a bulk rollback meta data file, for
// use in a bulk rollback.  Function was closely modeled after function
// of similar name in bulk/we_tableinfo.cpp.  API was modified to help
// facilitate its use by DML.
//
// columns - Column vector with information about column in table.
//           Includes information about the initial HWM extent, so that
//           the corresponding HWM chunk can be backed up.
// dctnryStoreOids - Dictionary store OIDs that correspond to columns.
// dbRootHWMInfoVecCol - vector of last local HWM info for each DBRoot
//     (asssigned to current PM) for each column in tblOid.
//------------------------------------------------------------------------------
void RBMetaWriter::saveBulkRollbackMetaData(
    const std::vector<Column>& columns,
    const std::vector<OID>&    dctnryStoreOids,
    const std::vector<BRM::EmDbRootHWMInfo_v>& dbRootHWMInfoVecCol )
{
    int rc = NO_ERROR;
    bool bOpenedFile = false;

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

        // Loop through DBRoot HWMs for this PM
        for (unsigned m = 0; m < dbRoots.size(); m++)
        {
            std::string metaFileName = openMetaFile( dbRoots[m] );
            bOpenedFile    = true;
            fCreatedSubDir = false;

            // Loop through the columns in the specified table
            for ( size_t i = 0; i < columns.size(); i++ )
            {
                const BRM::EmDbRootHWMInfo_v& dbRootHWMInfo =
                    dbRootHWMInfoVecCol[i];

                // Select dbRootHWMInfo that matches DBRoot for this iteration
                unsigned k = 0;

                for (; k < dbRootHWMInfo.size(); k++)
                {
                    if (dbRoots[m] == dbRootHWMInfo[k].dbRoot)
                        break;
                }

                if (k >= dbRootHWMInfo.size()) // logic error; should not happen
                {
                    std::ostringstream oss;
                    oss << "Error creating meta file; DBRoot" << dbRoots[m] <<
                        " listed in Calpont config file, but not in extentmap"
                        " for OID " << columns[i].dataFile.oid;
                    throw WeException( oss.str(), ERR_INVALID_PARAM );
                }

                uint16_t dbRoot    = dbRootHWMInfo[k].dbRoot;
                uint32_t partition = 0;
                uint16_t segment   = 0;
                HWM       localHWM  = 0;
                bool      bExtentWithData = false;

                // For empty DBRoot (totalBlocks == 0),
                // leave partition, segment, and HWM set to 0
                if ((dbRootHWMInfo[k].totalBlocks > 0) ||
                        (dbRootHWMInfo[k].status == BRM::EXTENTOUTOFSERVICE))
                {
                    partition = dbRootHWMInfo[k].partitionNum;
                    segment   = dbRootHWMInfo[k].segmentNum;
                    localHWM  = dbRootHWMInfo[k].localHWM;
                    bExtentWithData = true;
                }

                // Save column meta-data info to support bulk rollback
                writeColumnMetaData(
                    metaFileName,
                    bExtentWithData,
                    columns[i].dataFile.oid,
                    dbRoot,
                    partition,
                    segment,
                    localHWM,
                    columns[i].colDataType,
                    ColDataTypeStr[ columns[i].colDataType ],
                    columns[i].colWidth,
                    columns[i].compressionType );

                // Save dctnry store meta-data info to support bulk rollback
                if ( dctnryStoreOids[i] > 0 )
                {
                    std::vector<uint32_t> segList;
                    std::string segFileListErrMsg;

                    if (bExtentWithData)
                    {
                        std::string dirName;
                        FileOp fileOp(false);
                        rc = fileOp.getDirName( dctnryStoreOids[i],
                                                dbRoot, partition, dirName );

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

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

                        rc = BulkRollbackMgr::getSegFileList(dirName, false,
                                                             segList,
                                                             segFileListErrMsg);

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

                            throw WeException( oss.str(), rc );
                        }
                    }   // end of "if (bExtentWithData)"

                    if (segList.size() == 0)
                    {
                        writeDictionaryStoreMetaNoDataMarker(
                            columns[i].dataFile.oid,
                            dctnryStoreOids[i],
                            dbRoot,
                            partition,
                            0, // segment
                            columns[i].compressionType );
                    }
                    else
                    {
                        // Loop thru dictionary store seg files for this DBRoot
                        for (unsigned int kk = 0; kk < segList.size(); kk++)
                        {
                            unsigned int segDictionary = segList[kk];

                            // check HWM for dictionary store file
                            HWM dictHWMStore;
                            int extState;
                            rc = BRMWrapper::getInstance()->getLocalHWM(
                                     dctnryStoreOids[i],
                                     partition,
                                     segDictionary,
                                     dictHWMStore,
                                     extState );

                            if (rc != NO_ERROR)
                            {
                                WErrorCodes ec;
                                std::ostringstream oss;
                                oss << "Error getting rollback HWM for "
                                    "dictionary file " << dctnryStoreOids[i] <<
                                    "; partition-" << partition <<
                                    "; segment-"   << segDictionary <<
                                    "; " << ec.errorString(rc);
                                throw WeException( oss.str(), rc );
                            }

                            writeDictionaryStoreMetaData(
                                columns[i].dataFile.oid,
                                dctnryStoreOids[i],
                                dbRoot,
                                partition,
                                segDictionary,
                                dictHWMStore,
                                columns[i].compressionType );

                        } // loop thru dictionary store seg files in this DBRoot
                    }     // dictionary OID has 1 or more seg files in partition
                }         // if dictionary column

                // For a compressed column, backup the starting HWM chunk if the
                // starting HWM block is not on an empty DBRoot (or outOfSrvc)
                if ( (columns[i].compressionType) &&
                        (columns[i].dataFile.fDbRoot == dbRootHWMInfo[k].dbRoot) &&
                        (dbRootHWMInfo[k].totalBlocks > 0) &&
                        (dbRootHWMInfo[k].status != BRM::EXTENTOUTOFSERVICE) )
                {
                    backupColumnHWMChunk(
                        columns[i].dataFile.oid,
                        columns[i].dataFile.fDbRoot,
                        columns[i].dataFile.fPartition,
                        columns[i].dataFile.fSegment,
                        columns[i].dataFile.hwm );
                }

            }         // End of loop through columns

            // time to dump the string stream to file
            std::string data(fMetaDataStream.str());

            // this is to cover partical writes
            // no need for retry if low layer takes care partial writes.
            const char* p = data.c_str(); // buffer contents
            size_t      s = data.size();  // buffer size
            size_t      w = 0;            // total bytes written so far
            ssize_t     n = 0;            // bytes written in one write

            for (int i = 0; i < 10 && w < s; i++)
            {
                n = fMetaDataFile->write(p + w, s - w);

                if (n < 0)
                    break;

                w += n;
            }

            if (w != s)
            {
                int errRc = errno;
                std::ostringstream oss;
                oss << "Error writing bulk rollback meta-data file "
                    << metaFileName << "; written/expect:" << w << "/" << s
                    << "; err-" << errRc << "; " << strerror( errRc );
                throw WeException(oss.str(), ERR_FILE_WRITE);
            }

            fMetaDataStream.str("");
            closeMetaFile( );
            bOpenedFile = false;

        }   // End of loop through DBRoot HWMs for this PM

        renameMetaFile( ); // rename meta files from temporary filenames
    }
    catch (WeException& ex) // catch exception to close file, then rethrow
    {
        if (bOpenedFile)
            closeMetaFile( );

        // If any error occurred, then go back and try to delete all meta files.
        // We catch and drop any exception, and return the original exception,
        // since we are already in error-mode at this point.
        try
        {
            deleteFile( );
        }
        catch (...)
        {
        }

        throw WeException( ex.what(), ex.errorCode() );
    }
}

//------------------------------------------------------------------------------
// Open a meta data file to save info about the specified table OID.
//------------------------------------------------------------------------------
std::string RBMetaWriter::openMetaFile ( uint16_t dbRoot )
{
    std::string bulkRollbackPath( Config::getDBRootByNum( dbRoot ) );
    bulkRollbackPath += '/';
    bulkRollbackPath += DBROOT_BULK_ROLLBACK_SUBDIR;

    if ( !IDBPolicy::exists( bulkRollbackPath.c_str() ) )
    {
        if ( IDBPolicy::mkdir( bulkRollbackPath.c_str() ) != 0 )
        {
            std::ostringstream oss;
            oss << "Error creating bulk rollback directory " <<
                bulkRollbackPath << ";" << std::endl;
            throw WeException( oss.str(), ERR_DIR_CREATE );
        }
    }

    // Open the file
    std::ostringstream oss;
    oss << "/" << fTableOID;
    std::string metaFileName( bulkRollbackPath );
    metaFileName += oss.str();
    fMetaFileNames.insert( make_pair(dbRoot, metaFileName) );

    std::string tmpMetaFileName( metaFileName );
    tmpMetaFileName += TMP_FILE_SUFFIX;
    fMetaDataFile = IDBDataFile::open(IDBPolicy::getType(tmpMetaFileName.c_str(),
                                      IDBPolicy::WRITEENG),
                                      tmpMetaFileName.c_str(), "wb", 0);

    if ( !fMetaDataFile )
    {
        int errRc = errno;
        std::ostringstream oss;
        std::string eMsg;
        Convertor::mapErrnoToString(errRc, eMsg);
        oss << "Error opening bulk rollback file " <<
            tmpMetaFileName << "; " << eMsg;
        throw WeException( oss.str(), ERR_FILE_OPEN );
    }

    { 
        std::ostringstream ossChown;
        idbdatafile::IDBFileSystem& fs = IDBPolicy::getFs(tmpMetaFileName.c_str());
        if (chownPath(ossChown, tmpMetaFileName, fs)
            || chownPath(ossChown, bulkRollbackPath, fs))
        {
            throw WeException(ossChown.str(), ERR_FILE_CHOWN);
        }
    }

    fMetaDataStream <<
                    "# VERSION: 4"             << std::endl <<
                    "# APPLICATION: " << fAppDesc << std::endl <<
                    "# PID:    " << ::getpid() << std::endl <<
                    "# TABLE:  " << fTableName << std::endl <<
                    "# COLUM1: coloid,"
                    "dbroot,part,seg,lastLocalHWM,type,typename,width,comp" <<
                    std::endl <<
                    "# COLUM2: coloid,"
                    "dbroot,part,seg,type,typename,width,comp" <<
                    std::endl <<
                    "# DSTOR1: coloid,dctoid,"
                    "dbroot,part,seg,localHWM,comp" << std::endl <<
                    "# DSTOR2: coloid,dctoid,"
                    "dbroot,part,seg,comp" << std::endl;

    // Clear out any data subdirectory
    // This is redundant because init() also calls deleteSubDir(), but it can't
    // hurt to call twice.  We "really" want to make sure we start with a clean
    // slate (no leftover backup chunk files from a previous import job).
    deleteSubDir( metaFileName );

    return metaFileName;
}

//------------------------------------------------------------------------------
// Close the currently open "temporary named" meta data file used during
// construction.  We will rename all the meta data files (for the various
// dbroots) to their eventual file names later, in renameMetaFile().
//------------------------------------------------------------------------------
void RBMetaWriter::closeMetaFile ( )
{
    delete fMetaDataFile;
    fMetaDataFile = NULL;
}

//------------------------------------------------------------------------------
// Rename temporary metafile names to their permanent name, taking file names
// from fMetaFileNames.  In the normal case there will be one file name per
// DBRoot for the local PM we are running on.
//------------------------------------------------------------------------------
void RBMetaWriter::renameMetaFile ( )
{
    for (std::map<uint16_t, std::string>::const_iterator iter =
                fMetaFileNames.begin(); iter != fMetaFileNames.end(); ++iter)
    {
        const std::string& metaFileName = iter->second;

        if (!metaFileName.empty())
        {
            std::string tmpMetaFileName = metaFileName;
            tmpMetaFileName += TMP_FILE_SUFFIX;

            if ( IDBPolicy::rename(tmpMetaFileName.c_str(), metaFileName.c_str()) )
            {
                int errRc = errno;
                std::ostringstream oss;
                std::string eMsg;
                Convertor::mapErrnoToString(errRc, eMsg);
                oss << "Error renaming meta data file-" <<
                    tmpMetaFileName << "; will be deleted; " << eMsg;
                throw WeException( oss.str(), ERR_METADATABKUP_FILE_RENAME );
            }
        }
    }
}

//------------------------------------------------------------------------------
// Delete the meta data files for the specified table OID.  We loop through all
// the DBRoots for the local PM, deleting the applicable meta data files.
// If the call to deleteSubDir() should throw an exception, we might not want
// to consider that a fatal error, but we go ahead and let the exception
// go up the call-stack so that the caller can log the corresponding message.
// The application code can then decide whether they want to consider this
// condition as fatal or not.
//------------------------------------------------------------------------------
void RBMetaWriter::deleteFile ( )
{
    for (std::map<uint16_t, std::string>::const_iterator iter =
                fMetaFileNames.begin(); iter != fMetaFileNames.end(); ++iter)
    {
        const std::string& metaFileName = iter->second;

        if (!metaFileName.empty())
        {
            std::string tmpMetaFileName = metaFileName;
            tmpMetaFileName += TMP_FILE_SUFFIX;

            IDBPolicy::remove( metaFileName.c_str() );
            IDBPolicy::remove( tmpMetaFileName.c_str() );

            deleteSubDir( metaFileName ); // can throw an exception
        }
    }

    fMetaFileNames.clear( );
}

//------------------------------------------------------------------------------
// New version of writeColumnMetaData for Shared-Nothing
//------------------------------------------------------------------------------
void RBMetaWriter::writeColumnMetaData (
    const std::string& metaFileName,
    bool               withHWM,
    OID                columnOID,
    uint16_t           dbRoot,
    uint32_t           partition,
    uint16_t           segment,
    HWM                lastLocalHwm,
    CalpontSystemCatalog::ColDataType colType,
    const std::string& colTypeName,
    int                colWidth,
    int                compressionType )
{
    if (withHWM)
    {
        fMetaDataStream  << "COLUM1: " <<
                         columnOID    << ' ' <<
                         dbRoot       << ' ' <<
                         partition    << ' ' <<
                         segment      << ' ' <<
                         lastLocalHwm << ' ' <<
                         colType      << ' ' <<
                         colTypeName  << ' ' <<
                         colWidth;
    }
    else
    {
        fMetaDataStream  << "COLUM2: " <<
                         columnOID    << ' ' <<
                         dbRoot       << ' ' <<
                         partition    << ' ' <<
                         segment      << ' ' <<
                         colType      << ' ' <<
                         colTypeName  << ' ' <<
                         colWidth;
    }

    if (compressionType)
        fMetaDataStream << ' ' << compressionType << ' ';

    fMetaDataStream << std::endl;

    // If column is compressed, then create directory for storing HWM chunks
    if (compressionType)
    {
        if (!fCreatedSubDir)
        {
            // @bug 5572 - Don't need db backup files for HDFS;
            //             use hdfs buffer file
            if (!IDBPolicy::useHdfs())
                createSubDir( metaFileName );
        }
    }
}

//------------------------------------------------------------------------------
// New version of writeDictionaryStoreMetaData for Shared-Nothing.
//------------------------------------------------------------------------------
void RBMetaWriter::writeDictionaryStoreMetaData (
    OID      columnOID,
    OID      dictionaryStoreOID,
    uint16_t dbRoot,
    uint32_t partition,
    uint16_t segment,
    HWM      localHwm,
    int      compressionType )
{
    fMetaDataStream        << "DSTOR1: " <<
                           columnOID          << ' ' <<
                           dictionaryStoreOID << ' ' <<
                           dbRoot             << ' ' <<
                           partition          << ' ' <<
                           segment            << ' ' <<
                           localHwm;

    if (compressionType)
        fMetaDataStream << ' ' << compressionType << ' ';

    fMetaDataStream << std::endl;

    // Save dictionary meta data for later use in backing up the HWM chunks
    if (compressionType)
    {
        RBChunkInfo chunkInfo(
            dictionaryStoreOID, dbRoot, partition, segment, localHwm);
        fRBChunkDctnrySet.insert( chunkInfo );

        if ( (fLog) && (fLog->isDebug(DEBUG_1)) )
            printDctnryChunkList(chunkInfo, "after adding ");
    }
}

//------------------------------------------------------------------------------
// New version of writeDictionaryStoreMetaNoDataMarker for Shared-Nothing.
//------------------------------------------------------------------------------
void RBMetaWriter::writeDictionaryStoreMetaNoDataMarker (
    OID      columnOID,
    OID      dictionaryStoreOID,
    uint16_t dbRoot,
    uint32_t partition,
    uint16_t segment,
    int      compressionType )
{
    fMetaDataStream        << "DSTOR2: " <<
                           columnOID          << ' ' <<
                           dictionaryStoreOID << ' ' <<
                           dbRoot             << ' ' <<
                           partition          << ' ' <<
                           segment;

    if (compressionType)
        fMetaDataStream << ' ' << compressionType << ' ';

    fMetaDataStream << std::endl;
}

//------------------------------------------------------------------------------
// Create the subdirectory we will use to backup data needed for rollback.
//------------------------------------------------------------------------------
void RBMetaWriter::createSubDir( const std::string& metaFileName )
{
    std::string bulkRollbackSubPath( metaFileName );
    bulkRollbackSubPath += DATA_DIR_SUFFIX;

    if ( IDBPolicy::mkdir( bulkRollbackSubPath.c_str() ) != 0 )
    {
        std::ostringstream oss;
        oss << "Error creating bulk rollback data subdirectory " <<
            bulkRollbackSubPath << ";";
        throw WeException( oss.str(), ERR_DIR_CREATE );
    }

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

    if ( IDBPolicy::remove( bulkRollbackSubPath.c_str() ) != 0 )
    {
        std::ostringstream oss;
        oss << "Error deleting bulk rollback data subdirectory " <<
            bulkRollbackSubPath << ";";
        throw WeException( oss.str(), ERR_FILE_DELETE );
    }
}

//------------------------------------------------------------------------------
// Backup the contents of the HWM chunk for the specified column OID extent,
// so that the chunk is available for bulk rollback.
// This operation is only performed for compressed columns.
//------------------------------------------------------------------------------
void RBMetaWriter::backupColumnHWMChunk(
    OID       columnOID,
    uint16_t  dbRoot,
    uint32_t  partition,
    uint16_t  segment,
    HWM       startingHWM)
{
    // @bug 5572 - Don't need db backup file for HDFS; we use hdfs buffer file
    if (!IDBPolicy::useHdfs())
    {
        backupHWMChunk( true,
                        columnOID, dbRoot, partition, segment, startingHWM );
    }
}

//------------------------------------------------------------------------------
// Backup the contents of the HWM chunk for the specified dictionary store OID
// extent, so that the chunk is available for bulk rollback.
// This operation is only performed for compressed columns.  Once the chunk is
// saved, we remove that OID, partition, and segment from the internal list
// (fRBChunkDctnrySet) that is maintained.
// Return value indicates whether the specified file needs to be backed up or
// not.
//
// This function MUST be maintained to be thread-safe so that multiple threads
// can concurrently call this function, with each thread managing a different
// dictionary column.
//------------------------------------------------------------------------------
// @bug 5572 - HDFS usage: add return flag to indicate backup status
bool RBMetaWriter::backupDctnryHWMChunk(
    OID       dctnryOID,
    uint16_t  dbRoot,
    uint32_t  partition,
    uint16_t  segment)
{
    bool bBackupApplies = false;

    if (fRBChunkDctnrySet.size() > 0)
    {
        RBChunkInfo chunkInfo(
            dctnryOID, 0, partition, segment, 0);
        RBChunkInfo chunkInfoFound(0, 0, 0, 0, 0);
        bool bFound = false;

        {
            // Use scoped lock to perform "find"
            boost::mutex::scoped_lock lock( fRBChunkDctnryMutex );

            if ( (fLog) && (fLog->isDebug(DEBUG_1)) )
                printDctnryChunkList(chunkInfo, "when searching ");

            RBChunkSet::iterator iter = fRBChunkDctnrySet.find ( chunkInfo );

            if (iter != fRBChunkDctnrySet.end())
            {
                bFound = true;
                chunkInfoFound = *iter;
            }
        }

        if (bFound)
        {
            if (chunkInfoFound.fPartition == partition)
            {
                // @bug 5572 - Don't need db backup file for HDFS;
                //             we use hdfs buffer file.  Set backup flag
                //             so application knows to use tmp buffer file.
                bBackupApplies = true;

                if (!IDBPolicy::useHdfs())
                {
                    backupHWMChunk(false, dctnryOID,
                                   dbRoot, partition, segment, chunkInfoFound.fHwm);
                }
            }
            else
            {
                // How could this happen?  Ended up asking for different
                // partition than expected for the first instance of this
                // OID and segment file.  Perhaps initial blockskipping
                // or something caused us to advance to another segment file
                // without ever changing the expected extent.  At any rate
                // we still fall through and delete our entry because we
                // apparently did not end up changing the chunk referenced
                // by this RBChunkInfo object.
            }

            {
                // Use scoped lock to perform "erase"
                boost::mutex::scoped_lock lock( fRBChunkDctnryMutex );
                fRBChunkDctnrySet.erase( chunkInfoFound );

                if ( (fLog) && (fLog->isDebug(DEBUG_1)) )
                    printDctnryChunkList(chunkInfoFound, "after deleting ");
            }
        }
    }

    return bBackupApplies;
}

//------------------------------------------------------------------------------
// Backup entire contents of HWM file for the specified columnOID,dbRoot,etc,
// so that the file is available for bulk rollback.  This function is used for
// HDFS files only.  This operation is only performed for compressed columns.
//
// This function MUST be kept thread-safe in support of backupDctnryHWMChunk().
// See that function description for more details.  This is the reason
// backupHWMChunk() has to have a local FileOp object.  We can't share/reuse
// a FileOp data member variable unless we want to employ a mutex.
//------------------------------------------------------------------------------
// @bug 5572 - Stopped using backupHWMFile().
// Don't need db backup file for HDFS; we use hdfs buffer file
void RBMetaWriter::backupHWMFile(
    bool      bColumnFile, // is this a column (vs dictionary) file
    OID       columnOID,   // OID of column or dictionary store
    uint16_t  dbRoot,      // DB Root for db segment file
    uint32_t  partition,   // partition for db segment file
    uint16_t  segment,     // segment for db segment file
    HWM       startingHWM) // starting HWM for db segment file
{
    std::string fileType("column");

    if (!bColumnFile)
        fileType = "dictionary";

    FileOp fileOp; // @bug 4960: to keep thread-safe, we use local FileOp

    // Construct file name for db file to be backed up
    char dbFileName[FILE_NAME_SIZE];
    int rc = fileOp.getFileName( columnOID, dbFileName,
                                 dbRoot, partition, segment );

    if (rc != NO_ERROR)
    {
        std::ostringstream oss;
        oss << "Error creating backup "   << fileType <<
            " file for OID " << columnOID <<
            "; Can't construct file name for DBRoot"  << dbRoot <<
            "; partition-"   << partition <<
            "; segment-"     << segment;
        throw WeException( oss.str(), rc );
    }

    // Construct file name for backup copy of db file
    std::ostringstream ossFile;
    ossFile << "/" << columnOID << ".p" << partition << ".s" << segment;
    std::string backupFileName;
    rc = getSubDirPath( dbRoot, backupFileName );

    if (rc != NO_ERROR)
    {
        std::ostringstream oss;
        oss << "Error creating backup "   << fileType <<
            " file for OID " << columnOID <<
            "; Can't find matching meta file for DBRoot" << dbRoot;
        throw WeException( oss.str(), rc );
    }

    backupFileName += ossFile.str();

    std::string backupFileNameTmp = backupFileName;
    backupFileNameTmp += TMP_FILE_SUFFIX;

    //if ( (fLog) && (fLog->isDebug(DEBUG_1)) )
    if (fLog)
    {
        std::ostringstream oss;
        oss << "Backing up HWM file for "      << fileType <<
            " file for OID " << columnOID      <<
            "; file-"     << backupFileNameTmp <<
            "; HWM-"      << startingHWM;
        fLog->logMsg( oss.str(), MSGLVL_INFO2 );
    }

    // Copy the db file to a temporary name
    IDBFileSystem& fs = IDBPolicy::getFs( backupFileNameTmp.c_str() );

    if ( !fs.exists(dbFileName) )
    {
        std::ostringstream oss;
        oss << "Error creating backup "   << fileType <<
            " file for OID " << columnOID <<
            "; dbfile does not exist for DBRoot" << dbRoot <<
            "; partition-"   << partition <<
            "; segment-"     << segment;
        throw WeException( oss.str(), ERR_FILE_NOT_EXIST );
    }

    rc = fs.copyFile( dbFileName, backupFileNameTmp.c_str() );

    if (rc != 0)
    {
        std::ostringstream oss;
        oss << "Error copying backup for " << fileType <<
            " OID-"        << columnOID <<
            "; DBRoot-"    << dbRoot    <<
            "; partition-" << partition <<
            "; segment-"   << segment   <<
            "; rc-"        << rc;

        fs.remove( backupFileNameTmp.c_str() );
        throw WeException( oss.str(), ERR_METADATABKUP_COMP_WRITE_BULK_BKUP );
    }

    // Rename temporary named backup file to final name
    rc = fs.rename( backupFileNameTmp.c_str(), backupFileName.c_str() );

    if (rc != 0)
    {
        std::ostringstream oss;
        oss << "Error renaming temp backup for " << fileType <<
            " OID-"        << columnOID <<
            "; DBRoot-"    << dbRoot    <<
            "; partition-" << partition <<
            "; segment-"   << segment   <<
            "; rc-"        << rc;

        fs.remove( backupFileNameTmp.c_str() );
        fs.remove( backupFileName.c_str() );
        throw WeException( oss.str(), ERR_METADATABKUP_COMP_RENAME );
    }
}

//------------------------------------------------------------------------------
// Backup the contents of the HWM chunk for the specified columnOID,dbRoot,etc,
// so that the chunk is available for bulk rollback.  This function is used for
// non-hdfs files.  This operation is only performed for compressed columns.
//
// This function MUST be kept thread-safe in support of backupDctnryHWMChunk().
// See that function description for more details.  This is the reason
// backupHWMChunk() has to have a local FileOp object.  We can't share/reuse
// a FileOp data member variable unless we want to employ a mutex.
//------------------------------------------------------------------------------
void RBMetaWriter::backupHWMChunk(
    bool      bColumnFile, // is this a column (vs dictionary) file
    OID       columnOID,   // OID of column or dictionary store
    uint16_t  dbRoot,      // DB Root for db segment file
    uint32_t  partition,   // partition for db segment file
    uint16_t  segment,     // segment for db segment file
    HWM       startingHWM) // starting HWM for db segment file
{
    std::string fileType("column");

    if (!bColumnFile)
        fileType = "dictionary";

    // Open the applicable database column segment file
    std::string segFile;
    FileOp fileOp; // @bug 4960: to keep thread-safe, we use local FileOp
    IDBDataFile* dbFile = fileOp.openFile( columnOID,
                                           dbRoot,
                                           partition,
                                           segment,
                                           segFile,
                                           "rb" );

    if ( !dbFile )
    {
        std::ostringstream oss;
        oss << "Backup error opening " << fileType <<
            " file for OID-" << columnOID <<
            "; DBRoot-"      << dbRoot    <<
            "; partition-"   << partition <<
            "; segment-"     << segment;
        throw WeException( oss.str(), ERR_FILE_OPEN );
    }

    // Get the size of the file, so we know where to truncate back to.
    long long fileSizeBytes;
    int rc = fileOp.getFileSize( dbFile, fileSizeBytes);

    if (rc != NO_ERROR)
    {
        WErrorCodes ec;
        std::ostringstream oss;
        oss << "Backup error getting file size for " << fileType <<
            " OID-"        << columnOID <<
            "; DBRoot-"    << dbRoot    <<
            "; partition-" << partition <<
            "; segment-"   << segment   <<
            "; " << ec.errorString(rc);
        fileOp.closeFile( dbFile );
        throw WeException( oss.str(), rc );
    }

    // Read Control header
    char controlHdr[ IDBCompressInterface::HDR_BUF_LEN ];
    rc = fileOp.readFile( dbFile, (unsigned char*)controlHdr,
                          IDBCompressInterface::HDR_BUF_LEN );

    if (rc != NO_ERROR)
    {
        WErrorCodes ec;
        std::ostringstream oss;
        oss << "Backup error reading " << fileType <<
            " file control hdr for OID-" << columnOID <<
            "; DBRoot-"          << dbRoot    <<
            "; partition-"       << partition <<
            "; segment-"         << segment   <<
            "; " << ec.errorString(rc);
        fileOp.closeFile( dbFile );
        throw WeException( oss.str(), rc );
    }

    IDBCompressInterface compressor;
    int rc1 = compressor.verifyHdr( controlHdr );

    if (rc1 != 0)
    {
        rc = ERR_METADATABKUP_COMP_VERIFY_HDRS;

        WErrorCodes ec;
        std::ostringstream oss;
        oss << "Backup error verifying " << fileType <<
            " file control hdr for OID-" << columnOID <<
            "; DBRoot-"          << dbRoot    <<
            "; partition-"       << partition <<
            "; segment-"         << segment   <<
            "; " << ec.errorString(rc)        <<
            "; rc: "             << rc1;
        fileOp.closeFile( dbFile );
        throw WeException( oss.str(), rc );
    }

    // Read Pointer header data
    uint64_t hdrSize    = compressor.getHdrSize(controlHdr);
    uint64_t ptrHdrSize = hdrSize - IDBCompressInterface::HDR_BUF_LEN;
    char* pointerHdr    = new char[ptrHdrSize];
    rc = fileOp.readFile( dbFile, (unsigned char*)pointerHdr, ptrHdrSize );

    if (rc != NO_ERROR)
    {
        WErrorCodes ec;
        std::ostringstream oss;
        oss << "Backup error reading " << fileType <<
            " file pointer hdr for OID-" << columnOID <<
            "; DBRoot-"          << dbRoot    <<
            "; partition-"       << partition <<
            "; segment-"         << segment   <<
            "; " << ec.errorString(rc);
        delete[] pointerHdr;
        fileOp.closeFile( dbFile );
        throw WeException( oss.str(), rc );
    }

    CompChunkPtrList     chunkPtrs;
    rc = compressor.getPtrList(pointerHdr, ptrHdrSize, chunkPtrs );
    delete[] pointerHdr;

    if (rc != 0)
    {
        std::ostringstream oss;
        oss << "Backup error getting " << fileType <<
            " file hdr for OID-" << columnOID <<
            "; DBRoot-"          << dbRoot    <<
            "; partition-"       << partition <<
            "; segment-"         << segment;
        fileOp.closeFile( dbFile );
        throw WeException( oss.str(), ERR_METADATABKUP_COMP_PARSE_HDRS );
    }

    // Locate HWM chunk
    unsigned int chunkIndex             = 0;
    unsigned int blockOffsetWithinChunk = 0;
    unsigned char* buffer               = 0;
    uint64_t chunkSize                  = 0;
    compressor.locateBlock(startingHWM, chunkIndex, blockOffsetWithinChunk);

    if (chunkIndex < chunkPtrs.size())
    {
        chunkSize = chunkPtrs[chunkIndex].second;

        // Read the HWM chunk
        rc = fileOp.setFileOffset(dbFile, chunkPtrs[chunkIndex].first, SEEK_SET);

        if (rc != NO_ERROR)
        {
            WErrorCodes ec;
            std::ostringstream oss;
            oss << "Backup error seeking in " << fileType <<
                " file for OID-" << columnOID <<
                "; DBRoot-"      << dbRoot    <<
                "; partition-"   << partition <<
                "; segment-"     << segment   <<
                "; " << ec.errorString(rc);
            fileOp.closeFile( dbFile );
            throw WeException( oss.str(), rc );
        }

        buffer = new unsigned char[ chunkPtrs[chunkIndex].second ];
        rc = fileOp.readFile( dbFile, buffer, chunkPtrs[chunkIndex].second );

        if (rc != NO_ERROR)
        {
            WErrorCodes ec;
            std::ostringstream oss;
            oss << "Backup error reading in " << fileType <<
                " file for OID-" << columnOID <<
                "; DBRoot-"      << dbRoot    <<
                "; partition-"   << partition <<
                "; segment-"     << segment   <<
                "; " << ec.errorString(rc);
            delete []buffer;
            fileOp.closeFile( dbFile );
            throw WeException( oss.str(), rc );
        }
    }
    else if (startingHWM == 0)
    {
        // Okay to proceed.  Empty file with no chunks.  Save 0 length chunk.
    }
    else
    {
        rc = ERR_METADATABKUP_COMP_CHUNK_NOT_FOUND;

        WErrorCodes ec;
        std::ostringstream oss;
        oss << "Backup error for " << fileType <<
            " file for OID-" << columnOID <<
            "; DBRoot-"      << dbRoot    <<
            "; partition-"   << partition <<
            "; segment-"     << segment   <<
            "; hwm-"         << startingHWM <<
            "; chunkIdx-"    << chunkIndex  <<
            "; numPtrs-"     << chunkPtrs.size() <<
            "; not in hdrPtrs"  <<
            "; " << ec.errorString(rc);
        fileOp.closeFile( dbFile );
        throw WeException( oss.str(), rc );
    }

    // Backup the HWM chunk
    std::string errMsg;
    rc = writeHWMChunk(bColumnFile, columnOID, dbRoot, partition, segment,
                       buffer, chunkSize, fileSizeBytes, startingHWM, errMsg);

    if (rc != NO_ERROR)
    {
        std::ostringstream oss;
        oss << "Backup error writing backup for " << fileType <<
            " OID-"        << columnOID <<
            "; DBRoot-"    << dbRoot    <<
            "; partition-" << partition <<
            "; segment-"   << segment   <<
            "; "           << errMsg;
        delete []buffer;
        fileOp.closeFile( dbFile );
        throw WeException( oss.str(), rc );
    }

    // Close the applicable database column segment file and free memory
    delete []buffer;
    fileOp.closeFile( dbFile );
}

//------------------------------------------------------------------------------
// Writes out the specified HWM chunk to disk, in case we need it for bulk
// rollback.  If an error occurs, errMsg will contain the error message.
// This function is careful not to create a corrupt file (should the system
// crash in the middle of writing the file for example).  It's imperative
// that during a failure of any kind, that we not "accidentally" create and
// leave around a corrupt or incomplete HWM backup file that could cause a
// bulk rollback to fail, and eventually corrupt a data base file.
// So this function first creates the HWM backup file to a temp file, and
// after it is successfully created, it is it renamed to the final destination.
// If anything goes wrong, we try to delete any files we were creating.
//
// This function MUST be kept thread-safe in support of backupDctnryHWMChunk().
// See that function description for more details.
//------------------------------------------------------------------------------
int RBMetaWriter::writeHWMChunk(
    bool                 bColumnFile, // is this a column (vs dictionary) file
    OID                  columnOID,   // OID of column or dictionary store
    uint16_t             dbRoot,      // dbroot for db segment file
    uint32_t             partition,   // partition for db segment file
    uint16_t             segment,     // segment for db segment file
    const unsigned char* compressedOutBuf, // compressed chunk to be written
    uint64_t             chunkSize,   // number of bytes in compressedOutBuf
    uint64_t             fileSize,    // size of file in bytes
    HWM                  chunkHWM,    // HWM in the chunk being written
    std::string&         errMsg) const// error msg if error occurs
{
    std::ostringstream ossFile;
    ossFile << "/" << columnOID << ".p" << partition << ".s" << segment;
    std::string fileName;
    std::string dirPath;
    int rc = getSubDirPath( dbRoot, fileName );

    if (rc != NO_ERROR)
    {
        std::ostringstream oss;
        oss << "Error creating backup file for OID " << columnOID <<
            "; Can't find matching meta file for DBRoot" << dbRoot;
        errMsg = oss.str();
        return ERR_METADATABKUP_COMP_OPEN_BULK_BKUP;
    }

    dirPath = fileName;

    fileName += ossFile.str();

    std::string fileNameTmp = fileName;
    fileNameTmp += TMP_FILE_SUFFIX;

    //if ( (fLog) && (fLog->isDebug(DEBUG_1)) )
    if (fLog)
    {
        std::string fileType("column");

        if (!bColumnFile)
            fileType = "dictionary";

        std::ostringstream oss;
        oss << "Backing up HWM chunk for " << fileType <<
            " OID-"       << columnOID <<
            "; file-"     << fileNameTmp <<
            "; HWM-"      << chunkHWM    <<
            "; bytes-"    << chunkSize   <<
            "; fileSize-" << fileSize;
        fLog->logMsg( oss.str(), MSGLVL_INFO2 );
    }

    IDBDataFile*  backupFile = IDBDataFile::open(
                                   IDBPolicy::getType( fileNameTmp.c_str(), IDBPolicy::WRITEENG ),
                                   fileNameTmp.c_str(),
                                   "w+b",
                                   0 );

    if (!backupFile)
    {
        int errRc = errno;
        WErrorCodes ec;
        std::ostringstream oss;
        std::string eMsg;
        Convertor::mapErrnoToString(errRc, eMsg);
        oss << ec.errorString(ERR_METADATABKUP_COMP_OPEN_BULK_BKUP) <<
            "; " << eMsg;
        errMsg = oss.str();
        return ERR_METADATABKUP_COMP_OPEN_BULK_BKUP;
    }

    IDBFileSystem& fs = IDBPolicy::getFs( fileNameTmp.c_str() );

    // Format of backup compressed chunk file:
    //   8 byte unsigned int carrying chunk size
    //   8 byte unsigned int carrying original file size
    //   N bytes containing compressed chunk
    uint64_t sizeHdr[2];
    sizeHdr[0] = chunkSize;
    sizeHdr[1] = fileSize;
    size_t itemsWritten = backupFile->write(sizeHdr, sizeof(uint64_t) * 2) / (sizeof(uint64_t) * 2);

    if (itemsWritten != 1)
    {
        int errRc = errno;
        WErrorCodes ec;
        std::ostringstream oss;
        std::string eMsg;
        Convertor::mapErrnoToString(errRc, eMsg);
        oss << ec.errorString(ERR_METADATABKUP_COMP_WRITE_BULK_BKUP) <<
            "; " << eMsg;
        errMsg = oss.str();

        delete backupFile;
        fs.remove( fileNameTmp.c_str() );
        return ERR_METADATABKUP_COMP_WRITE_BULK_BKUP;
    }

    if (chunkSize > 0)
    {
        itemsWritten = backupFile->write(compressedOutBuf, chunkSize ) / chunkSize;

        if (itemsWritten != 1)
        {
            int errRc = errno;
            WErrorCodes ec;
            std::ostringstream oss;
            std::string eMsg;
            Convertor::mapErrnoToString(errRc, eMsg);
            oss << ec.errorString(ERR_METADATABKUP_COMP_WRITE_BULK_BKUP) <<
                "; " << eMsg;
            errMsg = oss.str();

            delete backupFile;
            fs.remove( fileNameTmp.c_str() );
            return ERR_METADATABKUP_COMP_WRITE_BULK_BKUP;
        }
    }

    backupFile->flush();
//   IDBDataFile flush() does a sync where appropriate
    delete backupFile;

#ifdef _MSC_VER
    //Windows rename() behaves differently from Linux: it will return an error
    // if the target exists
    //FIXME: The Linux version seems a bit safer, perhaps implement a better
    // Windows port?
    unlink(fileName.c_str());
#endif

    // Rename HWM backup file to final name.
    if ( fs.rename(fileNameTmp.c_str(), fileName.c_str()) )
    {
        int errRc = errno;
        WErrorCodes ec;
        std::ostringstream oss;
        std::string eMsg;
        Convertor::mapErrnoToString(errRc, eMsg);
        oss << ec.errorString(ERR_METADATABKUP_COMP_RENAME) << "; " << eMsg;
        errMsg = oss.str();

        fs.remove( fileNameTmp.c_str() );
        fs.remove( fileName.c_str() );
        return ERR_METADATABKUP_COMP_RENAME;
    }

    { 
        std::ostringstream ossChown;
        idbdatafile::IDBFileSystem& fs = IDBPolicy::getFs(fileName.c_str());
        if (chownPath(ossChown, fileName, fs)
            || chownPath(ossChown, dirPath, fs))
        {
            throw WeException(ossChown.str(), ERR_FILE_CHOWN);
        }
    }

    return NO_ERROR;
}
//------------------------------------------------------------------------------
// Returns the directory path to be used for storing any backup data files.
//
// This function MUST be kept thread-safe in support of backupDctnryHWMChunk().
// See that function description for more details.
//------------------------------------------------------------------------------
int RBMetaWriter::getSubDirPath( uint16_t dbRoot,
                                 std::string& bulkRollbackSubPath ) const
{
    std::map<uint16_t, std::string>::const_iterator iter =
        fMetaFileNames.find( dbRoot );

    if (iter == fMetaFileNames.end())
    {
        return ERR_INVALID_PARAM;
    }

    bulkRollbackSubPath  = iter->second;
    bulkRollbackSubPath += DATA_DIR_SUFFIX;

    return NO_ERROR;
}

//------------------------------------------------------------------------------
// Prints list of compressed dictionary HWM chunks that we are tracking,
// in order to backup to disk as needed, before we start adding rows to a
// previously existing chunk.
//------------------------------------------------------------------------------
void RBMetaWriter::printDctnryChunkList(
    const RBChunkInfo& rbChk,
    const char* assocAction)
{
    if (fLog)
    {
        std::ostringstream oss;
        oss << "Dumping metaDictHWMChunks " << assocAction <<
            rbChk << ":";

        if (fRBChunkDctnrySet.size() > 0)
        {
            RBChunkSet::iterator iter = fRBChunkDctnrySet.begin();
            int k = 1;

            while (iter != fRBChunkDctnrySet.end())
            {
                oss << std::endl;
                oss << '\t' << k << ". " << *iter;

                ++k;
                ++iter;
            }
        }
        else
        {
            oss << std::endl;
            oss << '\t' << "Empty list";
        }

        fLog->logMsg( oss.str(), MSGLVL_INFO2 );
    }
}

//------------------------------------------------------------------------------
// Verify that specified string represents Version 3 file format
//------------------------------------------------------------------------------
/* static */
bool RBMetaWriter::verifyVersion3(const char* versionRec)
{
    if (strncmp(versionRec, VERSION3_REC, VERSION3_REC_LEN) == 0)
        return true;
    else
        return false;
}

//------------------------------------------------------------------------------
// Verify that specified string represents Version 4 file format
//------------------------------------------------------------------------------
/* static */
bool RBMetaWriter::verifyVersion4(const char* versionRec)
{
    if (strncmp(versionRec, VERSION4_REC, VERSION4_REC_LEN) == 0)
        return true;
    else
        return false;
}

//------------------------------------------------------------------------------
// Verify that specified record type is a Column1 record
//------------------------------------------------------------------------------
/* static */
bool RBMetaWriter::verifyColumn1Rec(const char* recType)
{
    if (strncmp(recType, COLUMN1_REC, COLUMN1_REC_LEN) == 0)
        return true;
    else
        return false;
}

//------------------------------------------------------------------------------
// Verify that specified record type is a Column2 record
//------------------------------------------------------------------------------
/* static */
bool RBMetaWriter::verifyColumn2Rec(const char* recType)
{
    if (strncmp(recType, COLUMN2_REC, COLUMN2_REC_LEN) == 0)
        return true;
    else
        return false;
}

//------------------------------------------------------------------------------
// Verify that specified record type is a DStore1 record
//------------------------------------------------------------------------------
/* static */
bool RBMetaWriter::verifyDStore1Rec(const char* recType)
{
    if (strncmp(recType, DSTORE1_REC, DSTORE1_REC_LEN) == 0)
        return true;
    else
        return false;
}

//------------------------------------------------------------------------------
// Verify that specified record type is a DStore2 record
//------------------------------------------------------------------------------
/* static */
bool RBMetaWriter::verifyDStore2Rec(const char* recType)
{
    if (strncmp(recType, DSTORE2_REC, DSTORE2_REC_LEN) == 0)
        return true;
    else
        return false;
}

} // end of namespace
