/* Copyright (C) 2014 InfiniDB, Inc.
   Copyright (C) 2016 MariaDB Corporation

   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: replaytxnlog.cpp 2678 2007-06-14 03:41:50Z wweeks $
*
*************************/
#include <iostream>
#include <string>
#include <sstream>
#include <ctime>
#include <fstream>
#include <ctime>
#include <stdexcept>
#include <cstdlib>
using namespace std;

#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/algorithm/string/case_conv.hpp>
namespace fs = boost::filesystem;

#include "replaytxnlog.h"
#include "sessionmanager.h"
#include "liboamcpp.h"
#include "messagelog.h"
#include "messageids.h"

// Testing definitions.  These should never be commented out in checked in code.
#define OAM_AVAILABLE		// Comment out to skip the OAM calls to get log file locations and turn on/off logging.
#define LAST_TXN_AVAILABLE	// Comment out to use a test start txnId.
#define CALDB_AVAILABLE      	// Comment out to use against non-Calpont db (commits instead of calcommits).

ReplayTxnLog::ReplayTxnLog(const string& user, const string& password, const string& stopDateStr,
                           const bool& ignoreBulkLoad, const bool& reportMode) :
    fUser(user), fPassword(password), fStopDateStr(stopDateStr), fIgnoreBulkLoad(ignoreBulkLoad)
{
    // Initialize fCurrentYearStr.
    // WWW - Check for a better way to do this.
    time_t ttNow;
    time(&ttNow);
    struct tm tmNow;
    localtime_r(&ttNow, &tmNow);
    int year = tmNow.tm_year + 1900;
    ostringstream oss;
    oss << setw(4) << year;
    fCurrentYearStr = oss.str();

    // Set the program start time.
    time(&fProgramStarted);

    fDDLCount = 0;
    fCommitCount = 0;
    fRollbackCount = 0;
    fLastSchemaOwner = "";
};

/***************************************************************************
*
* Function:  validateInput
*
* Purpose:   Validates the user id, password, and stop date string.  Prompts
*	     for user id, password, and stop date if they were not passed in
*            with the constructor.  Attempts to start a database session using
*            the user and password.  Throws a runtime_error if the session
*	     can not be started.
*
****************************************************************************/
void ReplayTxnLog::validateInput()
{

    bool validStopDate = false;

    // Prompt for a user if not provided.
    /*
    	while(fUser.length() == 0) {
    		cout << "Enter User: ";
    		getline(cin, fUser);
    	}

    	// Prompt including commits and rollbacks.for a password if not provided.
    	// WWW - Make the password masked with *s.
    	while(fPassword.length() == 0 && !fReportMode) {
    		cout << "Enter Password: ";
    		getline(cin, fPassword);
    	}

    	if(!fReportMode) {
    		// @bug 2417.  Took out the convert user id to uppercase.  Leftover from a prior db front end.
    		// boost::algorithm::to_upper(fUser);
    		fSession.startSession(fUser, fPassword, fOracleSID);
    	}

    	// Validate the stop date.  Default to 'Now' if a stop date wasn't passed.
    	while(!validStopDate) {
    */
    if (fStopDateStr == "Now" || fStopDateStr.length() == 0)
    {
        validStopDate = true;
        time(&fStopDate);
    }
    else if (fStopDateStr.length() == 17)
    {
        if ((fStopDateStr[2] != '/') || (fStopDateStr[5] != '/') ||
                (fStopDateStr[8] != '@') || (fStopDateStr[11] != ':') ||
                (fStopDateStr[14] != ':'))
        {
            validStopDate = false;
        }
        else
        {
            tm lts;

            // Build date structure.
            lts.tm_isdst = -1;  // -1 for unknown whether date is during Daylight Saving Time.
            lts.tm_mon = atoi(fStopDateStr.substr(0, 2).c_str()) - 1;    // Month is 0..11
            lts.tm_mday = atoi(fStopDateStr.substr(3, 2).c_str());       // Day
            lts.tm_year = atoi(fStopDateStr.substr(6, 2).c_str()) + 100; // Year since 1900
            lts.tm_hour = atoi(fStopDateStr.substr(9, 2).c_str());       // Hour
            lts.tm_min = atoi(fStopDateStr.substr(12, 2).c_str());	     // Minute
            lts.tm_sec = atoi(fStopDateStr.substr(15, 2).c_str());	     // Second

            // Convert to a time_t object.
            fStopDate = mktime(&lts);

            validStopDate = true;
        }
    }

    if (!validStopDate)
    {
        if (fStopDateStr.length() > 0)
        {
            /*
            				cout << endl;
            				cout << "'" << fStopDateStr << "' is not a valid date and time." << endl;
            				cout << endl;
            */
            throw runtime_error(fStopDateStr + " is not a valid date and time.");

        }

        cout << "Enter Stop Date and Time as 'mm/dd/yy@hh:mm:ss' or 'Now': ";
        getline(cin, fStopDateStr);
    }

//	}
}

/***************************************************************************
*
* Function:  split
*
* Purpose:   Split a string on a given character into a vector of strings.
*	     The vector is passed by reference and cleared each time.
*	     The number of strings split out is returned.
*
****************************************************************************/
int ReplayTxnLog::split(vector<string>& v, const string& str, const char& c)
{
    v.clear();
    string::const_iterator s = str.begin();

    while (true)
    {
        string::const_iterator begin = s;

        while (*s != c && s != str.end())
        {
            ++s;
        }

        v.push_back(string(begin, s));

        if (s == str.end())
        {
            break;
        }

        if (++s == str.end())
        {
            v.push_back("");
            break;
        }
    }

    return v.size();
}

/***************************************************************************
*
* Function:  convertLogDateStr
*
* Purpose:   Converts a string formatted as "Apr 30 16:10:49" to a time_t.
*	     The date is passed in as three string parameters, such as
*	     "Apr", "30", "16:10:49".
*
* Note:	     There is no year included in the syslog time stamp.  This
*	     function returns the date in the current year if the date is
*	     at or before the current date and time.  If the date on a
*	     after current date / time, it returns the date in the prior year.
*
****************************************************************************/
time_t ReplayTxnLog::convertLogDateStr(const string& sMth, const string& sDay, const string& sTime)
{
    time_t tt;
    tm ltm = {0, 0, 0, 0, 0, 0, 0, 0, -1}; // -1 is tm_dst unknown.

    // Convert the log time to a time_t using the current year.
    string dt = fCurrentYearStr + "-" + sMth + "-" + sDay + "-" + sTime;
    strptime(dt.c_str(), "%Y-%b-%d-%H:%M:%S", &ltm);
    tt = mktime(&ltm);

    // If the log time is in the future, change it to the prior year.
    if (tt > fProgramStarted)
    {
        ltm.tm_year--;
        tt = mktime(&ltm);
    }

    return tt;
}

/***************************************************************************
*
	* Function:  getNextCalpontLogEntry
*
* Purpose:   Reads the passed filestream until a Calpont log entry is found.
*	     The log entry is assigned in the pass by reference argument.
*	     A bool is returned indicating whether a Calpont entry was found.
*
****************************************************************************/
bool ReplayTxnLog::getNextCalpontLogEntry(ifstream& dataFile, LogEntry& logEntry)
{
    bool found = false;
    string text;
    string::size_type pos;
    vector<string> v;
    vector<string> vFirstSection;
    /*
    Sample log entries:
    Mar 11 01:54:04 srvswdev3 Calpont[29720]: 04.372658 |5|1|0| C 00 CAL0018: DDL|WALT|CREATE TABLE TEST (XXX CHAR(8));
    Mar 11 02:40:22 srvswdev3 Calpont[29720]: 04.373627 |7|2|0| C 00 CAL0017: DML|WALT|INSERT INTO TEST (XXX) values ('TEST');
    Mar 11 02:40:24 srvswdev3 Calpont[29720]: 04.373429 |5|1|0| C 00 CAL0019: CMD|COMMIT;
    */

    while (!found && getline(dataFile, text))
    {
        split(v, text, '|');

        if (v.size() >= 5)
        {

            // Read the log entry type.
            if (v[4].find("CAL0017") != string::npos)
            {
                logEntry.type = DML;
            }
            else if (v[4].find("CAL0019") != string::npos)
            {
                if (v[5].find("COMMIT") != string::npos)
                {
                    logEntry.type = XCOMMIT; // WWW - COMMIT caused compiler error,
                }
                else if (v[5].find("ROLLBACK") != string::npos)
                {
                    logEntry.type = ROLLBACK;
                }
            }
            else if (v[4].find("CAL0018") != string::npos)
            {
                logEntry.type = DDL;
            }
            else if (v[4].find("CAL0008") != string::npos)
            {
                logEntry.type = BULKLOAD;
                logEntry.fSchemaOwner = v[5]; // steal schema owner for job number
                logEntry.fSqlStatement = v[6];

            }
            else if (v[4].find("CAL0022") != string::npos)
            {
                if (!fIgnoreBulkLoad)
                {
                    cout << endl;
                    cout << "Encountered the following bulk load log entry." << endl;
                    cout << text << endl;
                    cout << "Press Enter to continue." << endl;
                    getline(cin, text);
                    cout << endl;
                    continue;
                }
            }
            else
            {
                continue;
            }

            // Get the versionID.
            logEntry.fVersionID = atoi(v[2].c_str());

            // Get the current schema and sql for DML or DDL.
            if (logEntry.type == DML or logEntry.type == DDL)
            {
                logEntry.fSchemaOwner = v[5];

                // Parse out the sql statement.
                if (v.size() == 7)
                {
                    logEntry.fSqlStatement = v[6];
                }
                // Sql contains one or more "|" (separator) characters.
                else
                {
                    pos = text.find(v[6]);
                    logEntry.fSqlStatement = text.substr(pos, text.size() - pos);
                }
            }

            // Parse out the date and time.
            split(vFirstSection, v[0], ' ');

            if (vFirstSection.size() < 3)
            {
                continue;
            }
            else
            {

                // Quick hack for extra spaces showing up in the date such as:
                // Mar  7 21:28:36 srvperf6 DDLProc[7964]: 36.179609 |1|86|0| C 15 CAL0018: DDL ||CREATE TABLE Y (K NUMERIC)ENGINE=INFINIDB;
                // This can be stripped out when the log is no longer using syslog.
                if (vFirstSection[1].size() == 0)
                    logEntry.fLogTime = convertLogDateStr(vFirstSection[0], vFirstSection[2], vFirstSection[3]);
                else
                    logEntry.fLogTime = convertLogDateStr(vFirstSection[0], vFirstSection[1], vFirstSection[2]);
            }

            found = true;
        }
    }

    return found;
}


/***************************************************************************
*
* Function:  getLogFileNames
*
* Purpose:   Returns the list of files that contain the transactions that will
*	     be replayed.
*
* NOTE:      Sorting the files by name must put the files in
*	     date sorted order or this will not work.
*
****************************************************************************/
list<string> ReplayTxnLog::getLogFileNames()
{
    oam::Oam oam;
    string::size_type pos;
    string fullLogFileName;
    string dir;
    string archiveDir;
    string logFileName;

    // Get the log file name, directory, and archive file directory.
    /*
    	#ifdef OAM_AVAILABLE
    		oam.getLogFile("pm1", "data", fullLogFileName);
    	#else
    */
    fullLogFileName = "/var/log/mariadb/columnstore/data/data_mods.log";
    /*
    	#endif
    */
    pos = fullLogFileName.rfind("/");

    if (pos == string::npos)
    {
        throw runtime_error("Unable to parse log file path - " + fullLogFileName);
    }

    dir = fullLogFileName.substr(0, pos);
    logFileName = fullLogFileName.substr(pos + 1, fullLogFileName.size() - pos + 1);
    archiveDir = dir + "/archive";

    // Read the archive directory and get a list of archived log file names.
    list<string> dbFileNames;
    string fileName;
    fs::path sourceDir(archiveDir);
    fs::directory_iterator iter(sourceDir);
    fs::directory_iterator end_iter;

    while (iter != end_iter)
    {
        fs::path source = *iter;

        if (!fs::is_directory(source) )
        {
#if BOOST_VERSION >= 105200
            fileName = source.filename().c_str();
#else
            fileName = iter->leaf();
#endif

            if (fileName.find(logFileName, 0) == 0)
            {
                fileName = archiveDir + "/" + fileName;
                dbFileNames.push_back(fileName);
            }
        }

        ++iter;
    }

    // Sort the list by file name.  The files are named such that sorting them by name will be the
    // same as sorting them by the order they were created.
    dbFileNames.sort();

    // Make sure the current log file exists and add it to the end of the list.
    if (!fs::exists(fullLogFileName))
    {
        throw runtime_error("Current log file not found:  " + fullLogFileName);
    }
    else
    {
        dbFileNames.push_back(fullLogFileName);
    }

    // WWW - Warn the user and prompt to exit if there are missing file names.

    // If there is more than one log file, Read through the first transaction id in each file until
    // we find the one that begins with an id greater than the one we are starting with.  Once we've
    // found that, we know we can start with the previous file.
    list<string>::iterator curFile;
    list<string>::iterator emptyFile;
    vector<string> v;

    if (dbFileNames.size() > 1)
    {
        curFile = dbFileNames.begin();
        curFile++;
        bool done = false;
        bool validLogEntry = false;
        LogEntry logEntry;

        while (!done && curFile != dbFileNames.end())
        {
            fileName = *curFile;
            ifstream dataFile(fileName.c_str());
            validLogEntry = getNextCalpontLogEntry(dataFile, logEntry);
            dataFile.close();

            if (!validLogEntry)  // Empty file
            {
                emptyFile = curFile;
                curFile++;
                dbFileNames.erase(emptyFile); // Zap the empty file from the list.
            }
            else if (logEntry.fVersionID > fStartVersionID)
            {
                done = true;
            }
            else
            {

                // This file starts with an id less than our start id, so the prior file
                // contains only transactions from before the backup was restored.  Throw
                // the prior file away and keep looking.
                dbFileNames.pop_front();
                curFile++;
            }
        }
    }


    return dbFileNames;
}

/***************************************************************************
*
* Function:  processStatement
*
* Purpose:   Receives a versionID and a statement.
*
*	     For DML statements, the statement is added to the transaction if
*	     it's a subsequent statement in an active transaction.  If it's for a
*	     new transaction, the transactioni is first added to the vector of
*	     active transactions, then the statement is added to the transaction's
*	     list of statements.
*
*	     For Rollbacks, the transaction is zapped from the vector of active
*	     transactions.  No database rollback is done because the statements
*	     within the transaction never hit the database.
*
*	     For Commits, the transaction's statements are issued and committed
*	     to the database, then the transaction is removed from the vector.
*
*	     For DDL statements, the statement is executed immediately.  Each
*	     DDL statement is it's own transaction.
****************************************************************************/
void ReplayTxnLog::processStatement(const LogEntry& logEntry)
{

    vector<ActiveTxn>::iterator curTxn;
    bool found = false;

    // First, see if transaction already active.
    curTxn = fActiveTxns.begin();

    while (!found && (curTxn != fActiveTxns.end()))
    {
        if (curTxn->fVersionID == logEntry.fVersionID)
        {
            found = true;
        }
        else
        {
            curTxn++;
        }
    }


    // If it's a commit or DDL statement, issue each statement for the transaction, then
    // process the DLL statement or issue the commit.
    if (logEntry.type == XCOMMIT || logEntry.type == DDL)
    {

        replaytxnlog::DBSession dbSession;

        // Track the first committed txn and last committed txn.  DDL statements cause
        // an auto commit, so consider those to be commits.
        if (fLogEntryFirstCommit.fLogTime == 0)
        {
            fLogEntryFirstCommit = logEntry;
        }

        fLogEntryLastCommit = logEntry;

        // Increment commit count or ddl count.
        if (logEntry.type == XCOMMIT)
        {
            fCommitCount++;
        }
        else
        {
            fDDLCount++;
        }

        cout << "-- Transaction " << logEntry.fVersionID << " committed " <<
             ctime(&logEntry.fLogTime) ;
        dbSession.startSession(logEntry.fSchemaOwner);

        if (found)
        {

            // Loop through the statements and execute each one.
            vector<LogEntry>::iterator curStatement;

            for (curStatement = curTxn->fStatements.begin();
                    curStatement != curTxn->fStatements.end(); curStatement++)
            {
                dbSession.issueStatement(curStatement->fSqlStatement);
            }

            // Commit.
            curTxn->fStatements.clear();

            if (logEntry.type == XCOMMIT)
            {
                dbSession.commit();
            }

            // Clear the transaction from the vector.
            fActiveTxns.erase(curTxn);
        }

        // If it's DDL, issue the statement.
        if (logEntry.type == DDL)
        {
            dbSession.issueStatement(logEntry.fSqlStatement);
        }

        dbSession.endSession();
    }

    else if (logEntry.type == BULKLOAD)
    {
        cout << "-- Import completed " << ctime(&logEntry.fLogTime);
        cout << "-- " << logEntry.fSqlStatement << endl;
        cout << "-- " << logEntry.fSchemaOwner << endl << endl;
    }

    // If it's a rollback, clear the transaction from the vector.  No need to do any database work.
    else if (logEntry.type == ROLLBACK)
    {
        fRollbackCount++;

        if (found)
        {

            // Throw away the transaction.
            curTxn->fStatements.clear();
            fActiveTxns.erase(curTxn);
        }
    }

    // If it's a DML statement, add it to the vector of statements for the transaction.
    else if (logEntry.type == DML)
    {
        if (found)
        {
            curTxn->fStatements.push_back(logEntry);
        }
        else
        {
            ActiveTxn newTxn;
            newTxn.fVersionID = logEntry.fVersionID;
            newTxn.fStatements.push_back(logEntry);
            fActiveTxns.push_back(newTxn);
        }
    }

}

/***************************************************************************
*
* Function:  displaySummaryAndConfirm
*
* Purpose:   Displays a message with the date and time of the first transaction
*	     to be replayed and the date and time of the last transaction.
****************************************************************************/
bool ReplayTxnLog::displaySummaryAndConfirm(const LogEntry& logEntry)
{
    string replyYN;

    /*
    	// Show summary.
    	cout << endl;
    	cout << "--" << endl;
    	cout << "-- Summary" << endl;
    	cout << "--" << endl;
    	cout << "-- First Transaction   :  " << logEntry.fVersionID << endl;
    	cout << "-- Start Date and Time :  " << ctime(&logEntry.fLogTime);
    	cout << "-- Stop Date and Time  :  " << ctime(&fStopDate) << endl;
    	cout << "-- Transactions that were committed before the stop date and time will be reported." << endl;
    	cout << "--" << endl;

    	cout << endl;

    	// Prompt to continue.
    	do {
    		cout << "Would you like to continue (Y/N)?";
    		getline(cin, replyYN);
    	}
    	while ((replyYN != "Y") && (replyYN != "N"));
    	cout << endl;
    	if(replyYN == "Y") {
    		return true;
    	}
    	return false;
    */
    return true;
}

/***************************************************************************
*
* Function:  displayProgress
*
* Purpose:   Outputs progress message.
****************************************************************************/
void ReplayTxnLog::displayProgress(const string& fileName)
{
    // WWW - Check into having this stay on the same line like dbBuilder.
    /*
    	if(fLogEntryLastCommit.fLogTime > 0) {
    		cout << "Last transaction committed " << fLogEntryLastCommit.fVersionID << " from " <<
    			ctime(&fLogEntryLastCommit.fLogTime);
    	}
    	cout << endl;
    	cout << "Now processing file " << fileName << endl;
    */
}

/***************************************************************************
*
* Function:  replayTransactions
*
* Purpose:   Reads the qualifying files and processes the transactions
*	     starting with the first one with an id greater than the last
*	     id handed out before the backup and ending with the last
*	     transaction committed before the stop date.
****************************************************************************/
void ReplayTxnLog::replayTransactions(list<string>& fileNames)
{
    list<string>::iterator curFile;
    vector<string> v;
    curFile = fileNames.begin();
    LogEntry logEntry;
    string statement;
    bool done = false;
    bool confirmed = true;
    oam::Oam oam;

    curFile = fileNames.begin();

//	cout << "Searching files for first transaction to process." << endl << endl;

    while (!done && curFile != fileNames.end())
    {
        ifstream dataFile(curFile->c_str());

        if (confirmed)
        {
            displayProgress(*curFile);
        }

        while (!done && getNextCalpontLogEntry(dataFile, logEntry))
        {
            if (logEntry.fLogTime >= fStopDate)
            {
                done = true;
            }
            else if (logEntry.fVersionID > fStartVersionID)
            {
                if (confirmed)
                {
                    processStatement(logEntry);
                }

                /*
                				else if(displaySummaryAndConfirm(logEntry)) {
                					confirmed = true;
                					if(!fReportMode) {

                						// Log start message.
                						ostringstream oss;
                						oss << "First transaction ID to be processed is " << logEntry.fVersionID << ".";
                						string msg = oss.str();
                						logging::logEventToDataLog(logging::M0019, msg);

                						// Turn off DML/DDL logging to keep from the statements from
                						// being logged again.
                						#ifdef OAM_AVAILABLE
                						cout << endl << "Turning off DML/DDL logging." << endl << endl;
                						oam.updateLog(oam::MANDISABLEDSTATE, "system", "data");
                						#endif
                					}
                					displayProgress(*curFile);
                					processStatement(logEntry);
                				}
                				else {
                					done = true;
                				}
                */
            }
        }

        dataFile.close();
        curFile++;
    }

    // Turn DML/DDL logging back on.
    /*
    	if(!fReportMode && confirmed) {
    		#ifdef OAM_AVAILABLE
    		cout << endl << "Turning DML/DDL logging back on." << endl << endl;
    		oam.updateLog(oam::ENABLEDSTATE, "system", "data");
    		#endif

    		// Log complete message.
    		ostringstream oss;
    		oss << "Last transaction ID processed was " << fLogEntryLastCommit.fVersionID << ".";
    		string msg = oss.str();
    		logging::logEventToDataLog(logging::M0020, msg);
    	}
    */
}

/***************************************************************************
*
* Function:  checkDefinitions
*
* Purpose:   Reminder to uncomment required #defines.
****************************************************************************/
void ReplayTxnLog::checkDefinitions()
{
#ifndef OAM_AVAILABLE
    cout << "Warning!!!  Uncomment #define OAM_AVAILABLE before checking in." << endl;
#endif
#ifndef LAST_TXN_AVAILABLE
    cout << "Warning!!!  Uncomment #define LAST_TXN_AVAILABLE before checking in."  << endl;
#endif
#ifndef CALDB_AVAILABLE
    cout << "Warning!!!  Uncomment #define CALDB_AVAILABLE before checking in." << endl;
#endif
}

/***************************************************************************
*
* Function:  displayFinalSummary
*
* Purpose:   Shows totals at end of program.
****************************************************************************/
void ReplayTxnLog::displayFinalSummary()
{
    cout << endl;

    if (fCommitCount == 0 && fRollbackCount == 0 && fDDLCount == 0)
    {
        cout << "No transactions were processed." << endl;
    }
    else
    {
        /*
        		cout << "---------------------------------------------------------------------------------------" << endl;
        		cout << "-- Summary of transaction reported." << endl;
        		cout << "--" << endl;
        		cout << "-- Commits        :  " << fCommitCount << endl;
        //		cout << "-- Rollbacks      :  " << fRollbackCount << endl;
        		cout << "-- DDL Statements :  " << fDDLCount << endl;
        		cout << "--" << endl;
        		cout << "-- First Commit :  " << "Transaction " << fLogEntryFirstCommit.fVersionID << " originally " <<
        			"committed " << ctime(&fLogEntryFirstCommit.fLogTime);
        		cout << "-- Last Commit  :  " << "Transaction " << fLogEntryLastCommit.fVersionID << " originally " <<
        			"committed " << ctime(&fLogEntryLastCommit.fLogTime) ;
        		int openCount = fActiveTxns.size();
        		if(openCount > 0) {
        			cout << "-- " << endl;
        			cout << "-- There were " << openCount << " transactions that were open at the stop time." << endl;
        		}
        		cout << "---------------------------------------------------------------------------------------" << endl;
        		cout << endl;
        */
    }
}

/***************************************************************************
*
* Function:  process
*
* Purpose:   The main function.  Receives and validates the user's input and
*	     and processes the transactions contained in the log files.
****************************************************************************/
void ReplayTxnLog::process()
{
    list<string> dbFileNames;

    // Output a warning to the developer if #defines are commented out.
    checkDefinitions();

    // Validate the user id, password, and stop date.
    validateInput(); // Starts the session

    // Get the last transaction id.
    execplan::SessionManager mgr;

    // Get the last version from issued before the backup.
#ifdef LAST_TXN_AVAILABLE
    fStartVersionID = mgr.verID().currentScn;
#else
    fStartVersionID = 0;
#endif

    cout << "-- The current database version ID is " << fStartVersionID << "." << endl << endl;

    // Get the log file names.
    dbFileNames = getLogFileNames();

    // Replay the transactions.
    replayTransactions(dbFileNames);

    // Display summary of transactions processed.
    displayFinalSummary();

    // Warn the developer again if #defines are commented out.
    checkDefinitions();

//	cout << "Program completed successfully." << endl;
}

namespace replaytxnlog
{

DBSession::DBSession()
{
    fConnectedUser = "";
}

// Begins an Oracle session.
void DBSession::startSession(const std::string& connectAsUser)
{
    cout << "use " << connectAsUser << ";" << endl;
    fConnectedUser = connectAsUser;
}

void DBSession::issueStatement(const std::string& stmt)
{

    try
    {

        cout << stmt << endl;

    }
    catch (std::runtime_error e)
    {
        throw std::runtime_error("Error executing Statement:  " + stmt + "\n" + e.what());
    }
}


void DBSession::endSession()
{
    cout << endl;
}

void DBSession::commit()
{
    cout << "commit;" << endl << endl;
}

void DBSession::rollback()
{
    cout << "rollback;" << endl << endl;
}

std::string DBSession::getConnectedUser()
{
    return fConnectedUser;
}

} //namespace replaytxnlog

