/****************************************************************************
** (c) Copyright IBM Corp. 2007 All rights reserved.
** 
** The following sample of source code ("Sample") is owned by International 
** Business Machines Corporation or one of its subsidiaries ("IBM") and is 
** copyrighted and licensed, not sold. You may use, copy, modify, and 
** distribute the Sample in any form without payment to IBM, for the purpose of 
** assisting you in the development of your applications.
** 
** The Sample code is provided to you on an "AS IS" basis, without warranty of 
** any kind. IBM HEREBY EXPRESSLY DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR 
** IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Some jurisdictions do 
** not allow for the exclusion or limitation of implied warranties, so the above 
** limitations or exclusions may not apply to you. IBM shall not be liable for 
** any damages you suffer as a result of using, copying, modifying or 
** distributing the Sample, even if IBM has been advised of the possibility of 
** such damages.
*****************************************************************************
**
** SOURCE FILE NAME: dbhistfile.sqC
**
** SAMPLE: How to read and update a database recovery file entry
**
** DB2 APIs USED:
**         db2HistoryOpenScan  --  OPEN RECOVERY HISTORY FILE SCAN
**         db2HistoryGetEntry  --  GET NEXT RECOVERY HISTORY FILE ENTRY
**         db2HistoryUpdate    --  UPDATE RECOVERY HISTORY FILE
**         db2HistoryCloseScan --  CLOSE RECOVERY HISTORY FILE SCAN
**
**                           
*****************************************************************************
**
** For more information on the sample programs, see the README file.
**
** For information on developing embedded SQL applications see the Developing Embedded SQL Applications book.
**
** For information on using SQL statements, see the SQL Reference.
**
** For information on DB2 APIs, see the Administrative API Reference.
**
** For the latest information on programming, building, and running DB2
** applications, visit the DB2 Information Center:
**     http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/index.jsp
****************************************************************************/
#ifdef DB2NT
#include "utilrecov.cxx"
#else
#include "utilrecov.C"
#endif

class DbHistFile: public UtilRecov
{ 
  public:
    int DbRecoveryHistoryFileRead(DbEmb *);
    int DbFirstRecoveryHistoryFileEntryUpdate(DbEmb *);

  private:
    int HistoryEntryDataFieldsAlloc(struct db2HistoryData *);
    int HistoryEntryDisplay(struct db2HistoryData);
    int HistoryEntryDataFieldsFree(struct db2HistoryData *);
};

//**************************************************************************
// HistoryEntryDataFieldsAlloc                                             
// Allocates memory for all the fields in a database recovery history      
// file entry                                                              
//**************************************************************************
int DbHistFile::
HistoryEntryDataFieldsAlloc(struct db2HistoryData *pHistEntryData)
{
  int rc = 0;
  sqluint32 tsNb;

  strcpy(pHistEntryData->ioHistDataID, "SQLUHINF");

  pHistEntryData->oObjectPart.pioData = new char[DB2HISTORY_OBJPART_SZ + 1];
  pHistEntryData->oObjectPart.iLength = DB2HISTORY_OBJPART_SZ + 1;

  pHistEntryData->oEndTime.pioData = new char[DB2HISTORY_TIMESTAMP_SZ + 1];
  pHistEntryData->oEndTime.iLength = DB2HISTORY_TIMESTAMP_SZ + 1;

  pHistEntryData->oFirstLog.pioData = new char[DB2HISTORY_LOGFILE_SZ + 1];
  pHistEntryData->oFirstLog.iLength = DB2HISTORY_LOGFILE_SZ + 1;

  pHistEntryData->oLastLog.pioData = new char[DB2HISTORY_LOGFILE_SZ + 1];
  pHistEntryData->oLastLog.iLength = DB2HISTORY_LOGFILE_SZ + 1;

  pHistEntryData->oID.pioData = new char[DB2HISTORY_ID_SZ + 1];
  pHistEntryData->oID.iLength = DB2HISTORY_ID_SZ + 1;

  pHistEntryData->oTableQualifier.pioData =
    new char[DB2HISTORY_TABLE_QUAL_SZ + 1];
  pHistEntryData->oTableQualifier.iLength = DB2HISTORY_TABLE_QUAL_SZ + 1;

  pHistEntryData->oTableName.pioData =
    new char[DB2HISTORY_TABLE_NAME_SZ + 1];
  pHistEntryData->oTableName.iLength = DB2HISTORY_TABLE_NAME_SZ + 1;

  pHistEntryData->oLocation.pioData = new char[DB2HISTORY_LOCATION_SZ + 1];
  pHistEntryData->oLocation.iLength = DB2HISTORY_LOCATION_SZ + 1;

  pHistEntryData->oComment.pioData = new char[DB2HISTORY_COMMENT_SZ + 1];
  pHistEntryData->oComment.iLength = DB2HISTORY_COMMENT_SZ + 1;

  pHistEntryData->oCommandText.pioData = new char[DB2HISTORY_COMMAND_SZ + 1];
  pHistEntryData->oCommandText.iLength = DB2HISTORY_COMMAND_SZ + 1;

  pHistEntryData->poEventSQLCA = new sqlca;

  pHistEntryData->poTablespace = new db2Char[10];

  for (tsNb = 0; tsNb < 10; tsNb = tsNb + 1)
  {
    pHistEntryData->poTablespace[tsNb].pioData = new char[18 + 1];
    pHistEntryData->poTablespace[tsNb].iLength = 18 + 1;
  }

  pHistEntryData->iNumTablespaces = 10;

  return 0;
} // DbHistFile::HistoryEntryDataFieldsAlloc

//**************************************************************************
// HistoryEntryDisplay                                                     
// Displays the fields of an entry in the database recovery history file   
//**************************************************************************
int DbHistFile::HistoryEntryDisplay(struct db2HistoryData histEntryData)
{
  int rc = 0;
  int bufLen = 0;
  char *buf = NULL;
  sqluint32 tsNb = 0;

  bufLen =
    MIN(histEntryData.oObjectPart.oLength,
        histEntryData.oObjectPart.iLength);
  buf = new char[bufLen + 1];
  memcpy(buf, histEntryData.oObjectPart.pioData, bufLen);
  buf[bufLen] = '\0';
  cout << "    object part: " << buf << endl;
  delete [] buf;

  bufLen =
    MIN(histEntryData.oEndTime.oLength, histEntryData.oEndTime.iLength);
  buf = new char[bufLen + 1];
  memcpy(buf, histEntryData.oEndTime.pioData, bufLen);
  buf[bufLen] = '\0';
  cout << "    end time: " << buf << endl;
  delete [] buf;

  bufLen =
    MIN(histEntryData.oFirstLog.oLength, histEntryData.oFirstLog.iLength);
  buf = new char[bufLen + 1];
  memcpy(buf, histEntryData.oFirstLog.pioData, bufLen);
  buf[bufLen] = '\0';
  cout << "    first log: " << buf << endl;
  delete [] buf;

  bufLen =
    MIN(histEntryData.oLastLog.oLength, histEntryData.oLastLog.iLength);
  buf = new char[bufLen + 1];
  memcpy(buf, histEntryData.oLastLog.pioData, bufLen);
  buf[bufLen] = '\0';
  cout << "    last log: " << buf << endl;
  delete [] buf;

  bufLen = MIN(histEntryData.oID.oLength, histEntryData.oID.iLength);
  buf = new char[bufLen + 1];
  memcpy(buf, histEntryData.oID.pioData, bufLen);
  buf[bufLen] = '\0';
  cout << "    ID: " << buf << endl;
  delete [] buf;

  bufLen =
    MIN(histEntryData.oTableQualifier.oLength,
        histEntryData.oTableQualifier.iLength);
  buf = new char[bufLen + 1];
  memcpy(buf, histEntryData.oTableQualifier.pioData, bufLen);
  buf[bufLen] = '\0';
  cout << "    table qualifier: " << buf << endl;
  delete [] buf;

  bufLen =
    MIN(histEntryData.oTableName.oLength, histEntryData.oTableName.iLength);
  buf = new char[bufLen + 1];
  memcpy(buf, histEntryData.oTableName.pioData, bufLen);
  buf[bufLen] = '\0';
  cout << "    table name: " << buf << endl;
  delete [] buf;

  bufLen =
    MIN(histEntryData.oLocation.oLength, histEntryData.oLocation.iLength);
  buf = new char[bufLen + 1];
  memcpy(buf, histEntryData.oLocation.pioData, bufLen);
  buf[bufLen] = '\0';
  cout << "    location: " << buf << endl;
  delete [] buf;

  bufLen =
    MIN(histEntryData.oComment.oLength, histEntryData.oComment.iLength);
  buf = new char[bufLen + 1];
  memcpy(buf, histEntryData.oComment.pioData, bufLen);
  buf[bufLen] = '\0';
  cout << "    comment: " << buf << endl;
  delete [] buf;

  bufLen =
    MIN(histEntryData.oCommandText.oLength,
        histEntryData.oCommandText.iLength);
  buf = new char[bufLen + 1];
  memcpy(buf, histEntryData.oCommandText.pioData, bufLen);
  buf[bufLen] = '\0';
  cout << "    command text: " << buf << endl;
  delete [] buf;

  cout << "    history file entry ID: " << histEntryData.oEID.ioHID << endl;
  cout << "    tablespaces:" << endl;

  for (tsNb = 0; tsNb < histEntryData.oNumTablespaces; tsNb = tsNb + 1)
  {
    bufLen =
      MIN(histEntryData.poTablespace[tsNb].oLength,
          histEntryData.poTablespace[tsNb].iLength);
    buf = new char[bufLen + 1];
    memcpy(buf, histEntryData.poTablespace[tsNb].pioData, bufLen);
    buf[bufLen] = '\0';
    cout << "      " << buf << endl;
    delete [] buf;
  }
 
  cout << "    oLastLSN: " << histEntryData.oLastLSN.lsnU64 << endl; 
  cout << "    type of operation: " << histEntryData.oOperation << endl;
  cout << "    granularity of the operation: " << histEntryData.oObject << endl;
  cout << "    operation type: " << histEntryData.oOptype << endl;
  cout << "    entry status: " << histEntryData.oStatus << endl;
  cout << "    device type: " << histEntryData.oDeviceType << endl;
  cout << "    SQLCA:" << endl;
  cout << "      sqlcode: " << histEntryData.poEventSQLCA->sqlcode << endl;

  bufLen = SQLUDF_SQLSTATE_LEN;
  buf = new char[bufLen + 1];
  memcpy(buf, histEntryData.poEventSQLCA->sqlstate, bufLen);
  buf[bufLen] = '\0';
  cout << "      sqlstate: " << buf << endl;
  delete [] buf;

  bufLen = histEntryData.poEventSQLCA->sqlerrml;
  buf = new char[bufLen + 1];
  memcpy(buf, histEntryData.poEventSQLCA->sqlerrmc, bufLen);
  buf[bufLen] = '\0';
  cout << "      message: " << buf << endl;
  delete [] buf;

  return 0;
} // DbHistFile::HistoryEntryDisplay

//**************************************************************************
// HistoryEntryDataFieldsFree                                              
// Deallocates the memory for database recovery history file structures    
//**************************************************************************
int DbHistFile::
HistoryEntryDataFieldsFree(struct db2HistoryData *pHistEntryData)
{
  int rc = 0;
  sqluint32 tsNb = 0;

  delete [] pHistEntryData->oObjectPart.pioData;
  delete [] pHistEntryData->oEndTime.pioData;
  delete [] pHistEntryData->oFirstLog.pioData;
  delete [] pHistEntryData->oLastLog.pioData;
  delete [] pHistEntryData->oID.pioData;
  delete [] pHistEntryData->oTableQualifier.pioData;
  delete [] pHistEntryData->oTableName.pioData;
  delete [] pHistEntryData->oLocation.pioData;
  delete [] pHistEntryData->oComment.pioData;
  delete [] pHistEntryData->oCommandText.pioData;
  delete pHistEntryData->poEventSQLCA;
  for (tsNb = 0; tsNb < 10; tsNb = tsNb + 1)
  {
    delete [] pHistEntryData->poTablespace[tsNb].pioData;
  }
  delete [] pHistEntryData->poTablespace;

  return 0;
} // DbHistFile::HistoryEntryDataFieldsFree

//**************************************************************************
// DbRecoveryHistoryFileRead                                       
// Reads the database recovery history file    
//**************************************************************************
int DbHistFile::DbRecoveryHistoryFileRead(DbEmb *db)
{
  int rc = 0;
  struct sqlca sqlca = { 0 };
  struct db2HistoryOpenStruct dbHistoryOpenParam = { 0 };
  sqluint32 numEntries = 0;
  sqluint16 recoveryHistoryFileHandle = 0;
  sqluint32 entryNb = 0;
  struct db2HistoryGetEntryStruct dbHistoryEntryGetParam = { 0 };
  struct db2HistoryData histEntryData = { 0 };

  cout << "\n*********************************************\n";
  cout << "*** READ A DATABASE RECOVERY HISTORY FILE ***\n";
  cout << "*********************************************\n";
  
  cout << "\nUSE THE DB2 APIs:" << endl;
  cout << "  db2HistoryOpenScan -- OPEN RECOVERY HISTORY FILE SCAN" << endl;
  cout << "  db2HistoryGetEntry -- GET NEXT RECOVERY HISTORY FILE ENTRY"
    << endl;
  cout << "  db2HistoryCloseScan -- CLOSE RECOVERY HISTORY FILE SCAN"
    << endl;
  cout << "TO READ A DATABASE RECOVERY HISTORY FILE." << endl;

  // Initialize data structures
  dbHistoryOpenParam.piDatabaseAlias = db->getAlias();
  dbHistoryOpenParam.piTimestamp = NULL;
  dbHistoryOpenParam.piObjectName = NULL;
  dbHistoryOpenParam.iCallerAction = DB2HISTORY_LIST_HISTORY;
  dbHistoryEntryGetParam.pioHistData = &histEntryData;
  dbHistoryEntryGetParam.iCallerAction = DB2HISTORY_GET_ALL;
  rc = HistoryEntryDataFieldsAlloc(&histEntryData);
  CHECKRC(rc, "HistoryEntryDataFieldsAlloc");

  // Open database recovery history file
  cout << "\n  Open the recovery history file for '" << db->getAlias()
    << "' database." << endl;

  // OPEN RECOVERY HISTORY FILE SCAN
  db2HistoryOpenScan(db2Version970, &dbHistoryOpenParam, &sqlca);
  DB2_API_CHECK("database recovery history file -- open");

  numEntries = dbHistoryOpenParam.oNumRows;
  recoveryHistoryFileHandle = dbHistoryOpenParam.oHandle;

  // Read and display the history file entries
  dbHistoryEntryGetParam.iHandle = recoveryHistoryFileHandle;

  for (entryNb = 0; entryNb < numEntries; entryNb = entryNb + 1)
  {
    // Read the recovery history file entry
    cout << "\n  Read entry number " << entryNb << "." << endl;

    // GET NEXT RECOVERY HISTORY FILE ENTRY
    db2HistoryGetEntry(db2Version970, &dbHistoryEntryGetParam, &sqlca);
    DB2_API_CHECK("database recovery history file entry -- read")
      // display the recovery history file entry
      cout << "\n  Display entry number " << entryNb << "." << endl;
    rc = HistoryEntryDisplay(histEntryData);
    CHECKRC(rc, "HistoryEntryDisplay");
  }

  // close the database recovery history file
  cout << "\n  Close recovery history file for '" << db->getAlias()
    << "' database." << endl;

  // CLOSE RECOVERY HISTORY FILE SCAN
  db2HistoryCloseScan(db2Version970, &recoveryHistoryFileHandle, &sqlca);
  DB2_API_CHECK("database recovery history file -- close");

  // Release the memory allocated
  rc = HistoryEntryDataFieldsFree(&histEntryData);
  CHECKRC(rc, "HistoryEntryDataFieldsFree");

  return 0;
} // DbHistFile::DbRecoveryHistoryFileRead

//**************************************************************************
// DbFirstRecoveryHistoryFileEntryUpdate                                           
// Updates the first entry in the database recovery history file    
//**************************************************************************
int DbHistFile::DbFirstRecoveryHistoryFileEntryUpdate(DbEmb *db)
{
  int rc = 0;
  struct sqlca sqlca = { 0 };
  struct db2HistoryOpenStruct dbHistoryOpenParam = { 0 };
  sqluint16 recoveryHistoryFileHandle = 0;
  struct db2HistoryGetEntryStruct dbHistoryEntryGetParam = { 0 };
  struct db2HistoryData histEntryData = { 0 };
  char newLocation[DB2HISTORY_LOCATION_SZ + 1] = { 0 };
  char newComment[DB2HISTORY_COMMENT_SZ + 1] = { 0 };
  struct db2HistoryUpdateStruct dbHistoryUpdateParam = { 0 };

  cout << "\n*****************************************************\n";
  cout << "*** UPDATE A DATABASE RECOVERY HISTORY FILE ENTRY ***\n";
  cout << "*****************************************************\n";
  
  cout << "\nUSE THE DB2 APIs:" << endl;
  cout << "  db2HistoryOpenScan -- OPEN RECOVERY HISTORY FILE SCAN" << endl;
  cout << "  db2HistoryGetEntry -- GET NEXT RECOVERY HISTORY FILE ENTRY"
    << endl;
  cout << "  db2HistoryUpdate -- UPDATE RECOVERY HISTORY FILE" << endl;
  cout << "  db2HistoryCloseScan -- CLOSE RECOVERY HISTORY FILE SCAN"
    << endl;
  cout << "TO UPDATE A DATABASE RECOVERY HISTORY FILE ENTRY." << endl;

  // Initialize the data structures
  dbHistoryOpenParam.piDatabaseAlias = db->getAlias();
  dbHistoryOpenParam.piTimestamp = NULL;
  dbHistoryOpenParam.piObjectName = NULL;
  dbHistoryOpenParam.iCallerAction = DB2HISTORY_LIST_HISTORY;
  dbHistoryEntryGetParam.pioHistData = &histEntryData;
  dbHistoryEntryGetParam.iCallerAction = DB2HISTORY_GET_ALL;
  rc = HistoryEntryDataFieldsAlloc(&histEntryData);
  CHECKRC(rc, "HistoryEntryDataFieldsAlloc");

  // Open the database recovery history file
  cout << "\n  Open the recovery history file for '" << db->getAlias()
    << "' database." << endl;

  // OPEN RECOVERY HISTORY FILE SCAN
  db2HistoryOpenScan(db2Version970, &dbHistoryOpenParam, &sqlca);
  DB2_API_CHECK("database recovery history file -- open");

  recoveryHistoryFileHandle = dbHistoryOpenParam.oHandle;

  // Read and display first history file entry
  dbHistoryEntryGetParam.iHandle = recoveryHistoryFileHandle;
  cout << "\n  Read the first entry in the recovery history file." << endl;

  // GET NEXT RECOVERY HISTORY FILE ENTRY
  db2HistoryGetEntry(db2Version970, &dbHistoryEntryGetParam, &sqlca);
  DB2_API_CHECK("first recovery history file entry -- read");
  // Check if the recovery history file is empty
  if (sqlca.sqlcode == SQLE_RC_NOMORE)
  {
    cout << "  The History File is Empty! " << endl;
    cout << "  No data to return. " << endl << endl;  
    return 1;
  }

  cout << "\n  Display the first entry." << endl;
  rc = HistoryEntryDisplay(histEntryData);
  CHECKRC(rc, "HistoryEntryDisplay");

  // Update the first history file entry
  rc = db->Connect();
  CHECKRC(rc, "db->Connect");

  strcpy(newLocation, "this is the NEW LOCATION");
  strcpy(newComment, "this is the NEW COMMENT");
  cout << "\n  Update the first entry in the history file:" << endl;
  cout << "    new location = '" << newLocation << "'" << endl;
  cout << "    new comment = '" << newComment << "'" << endl;
  dbHistoryUpdateParam.piNewLocation = newLocation;
  dbHistoryUpdateParam.piNewDeviceType = NULL;
  dbHistoryUpdateParam.piNewComment = newComment;
  dbHistoryUpdateParam.iEID.ioNode = histEntryData.oEID.ioNode;
  dbHistoryUpdateParam.iEID.ioHID = histEntryData.oEID.ioHID;

  // UPDATE RECOVERY HISTORY FILE
  db2HistoryUpdate(db2Version970, &dbHistoryUpdateParam, &sqlca);
  DB2_API_CHECK("first history file entry -- update");

  rc = db->Disconnect();
  CHECKRC(rc, "db->Disconnect");

  // Read and dispaly the first history file entry again
  cout << "\n  Close recovery history file for '" << db->getAlias()
    << "' database." << endl;

  // CLOSE RECOVERY HISTORY FILE SCAN
  db2HistoryCloseScan(db2Version970, &recoveryHistoryFileHandle, &sqlca);
  DB2_API_CHECK("database recovery history file -- close");

  cout << "\n  Open the recovery history file for '" << db->getAlias()
    << "' database." << endl;

  // OPEN RECOVERY HISTORY FILE SCAN
  db2HistoryOpenScan(db2Version970, &dbHistoryOpenParam, &sqlca);
  DB2_API_CHECK("database recovery history file -- open");

  recoveryHistoryFileHandle = dbHistoryOpenParam.oHandle;

  dbHistoryEntryGetParam.iHandle = recoveryHistoryFileHandle;
  cout << "\n  Read the first entry in the recovery history file." << endl;

  // GET NEXT RECOVERY HISTORY FILE ENTRY
  db2HistoryGetEntry(db2Version970, &dbHistoryEntryGetParam, &sqlca);
  DB2_API_CHECK("first recovery history file entry -- read");

  cout << "\n  Display the first entry." << endl;
  rc = HistoryEntryDisplay(histEntryData);
  CHECKRC(rc, "HistoryEntryDisplay");

  // Close the database recovery history file
  cout << "\n  Close the recovery history file for '" << db->getAlias()
    << "' database." << endl;

  // CLOSE RECOVERY HISTORY FILE SCAN
  db2HistoryCloseScan(db2Version970, &recoveryHistoryFileHandle, &sqlca);
  DB2_API_CHECK("database recovery history file -- close");

  // Release the allocated memory
  rc = HistoryEntryDataFieldsFree(&histEntryData);
  CHECKRC(rc, "HistoryEntryDataFieldsFree");

  return 0;
} // DbHistFile::DbFirstRecoveryHistoryFileEntryUpdate

int main(int argc, char *argv[])
{
  int rc = 0;
  CmdLineArgs check;
  char serverWorkingPath[SQL_PATH_SZ + 1] = { 0 };
  Instance inst;
  DbEmb db;
  DbHistFile histFile;

  // Check the command line arguments
  rc = check.CmdLineArgsCheck3(argc, argv, db, inst);
  CHECKRC(rc, "check.CmdLineArgsCheck3");
  
  cout << "\nTHIS SAMPLE SHOWS HOW TO READ A DATABASE RECOVERY HISTORY FILE \n";
  cout << "AND UPDATE A RECOVERY HISTORY FILE ENTRY. \n";

  // Attach to a local or remote instance
  rc = inst.Attach();
  CHECKRC(rc, "inst.Attach");

  // Get a server working path
  rc = histFile.ServerWorkingPathGet(&db, serverWorkingPath);
  CHECKRC(rc, "histFile.ServerWorkingPathGet");

  // Call the sample functions
  rc = histFile.DbRecoveryHistoryFileRead(&db);
  CHECKRC(rc, "histFile.DbRecoveryHistoryFileRead");
  rc = histFile.DbFirstRecoveryHistoryFileEntryUpdate(&db);
  CHECKRC(rc, "histFile.DbFirstRecoveryHistoryFileEntryUpdate");
    
  // Detach from the local or remote instance
  rc = inst.Detach();
  CHECKRC(rc, "inst.Detach");

  return 0;
} // main