/**************************************************************************** ** (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: utilrecov.C ** ** SAMPLE: Utilities for the backup, restore and log file samples ** ** This set of utilities gets the server working path, prunes ** recovery history file, creates a database, backup a database, ** drop a database, saves and restores log retain values, displays ** the log buffer files. ** ** DB2 APIs USED: ** db2CfgGet -- Get Configuration ** db2CfgSet -- Set Configuration ** db2Prune -- Prune Recovery History File ** db2Backup -- Backup Database ** sqledrpd -- Drop and uncatalog a database ** sqlecrea -- creates a database ** ** STRUCTURES USED: ** sqlca ** sqledbdesc ** db2PruneStruct ** db2HistoryData ***************************************************************************** ** ** For more information on the sample programs, see the README file. ** ** For information on developing C++ applications, see the Application ** Development Guide. ** ** 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 ****************************************************************************/ #include <stdlib.h> #include <stdio.h> #include <sqlenv.h> #include <sqlutil.h> #include <sqlca.h> #include <db2ApiDf.h> #include <string.h> #include <ctype.h> #include <sqludf.h> #include "utilapi.h" #include "utilemb.h" #if ((__cplusplus >= 199711L) && !defined DB2HP && !defined DB2AIX) || \ (DB2LINUX && (__LP64__ || (__GNUC__ >= 3)) ) #include <iostream> using namespace std; #else #include <iostream.h> #endif #define ADD_BYTES 2 #define CHECKRC(x,y) \ if ((x) != 0) \ { \ printf("Non-zero rc from function %s.\n", (y)); \ return (x); \ } #define MIN(x,y) \ ((x)<(y)?(x):(y)) class UtilRecov { public: int ServerWorkingPathGet(DbEmb *, char *); int DbLogRetainValueSave(DbEmb *, sqluint16 *); int DbLogRetainValueRestore(DbEmb *, sqluint16 *); int DbRecoveryHistoryFilePrune(DbEmb *); int DbBackup(DbEmb *, char *, db2BackupStruct *); int DbCreate(char *, char *); int DbDrop(char *); }; class UtilLog { public: int LogBufferDisplay(char *, sqluint32, int); int LogRecordDisplay(char *, sqluint32, sqluint16, sqluint16); int SimpleLogRecordDisplay(sqluint16, sqluint16, char *, sqluint32); int ComplexLogRecordDisplay(sqluint16, sqluint16, char *, sqluint32, sqluint8, char *, sqluint32); int LogSubRecordDisplay(char *, sqluint16); int UserDataDisplay(char *, sqluint16); }; class RID { private: char ridParts[6]; char ridString[14]; void toString(); public: int size() { return 6; }; void set(char * buf ); char *getString(); }; void RID::toString() { char *ptrBuf = this->ridParts; sprintf( ridString, "x%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X", *ptrBuf, *(ptrBuf+1), *(ptrBuf+2), *(ptrBuf+3), *(ptrBuf+4), *(ptrBuf+5) ); } void RID::set( char *buf ) { strncpy( this->ridParts, buf, this->size() ); } char* RID::getString() { this->toString(); return ridString; } //************************************************************************** // ServerWorkingPathGet // Get the server working directory path where the backup images are kept //************************************************************************** int UtilRecov::ServerWorkingPathGet( DbEmb *db, char serverWorkingPath[] ) { int rc = 0; struct sqlca sqlca = { 0 }; char serverLogPath[SQL_PATH_SZ + 1] = { 0 }; db2CfgParam cfgParameters[1] = { 0 }; db2Cfg cfgStruct = { 0 }; int len = 0; // initialize cfgParameters cfgParameters[0].flags = 0; cfgParameters[0].token = SQLF_DBTN_LOGPATH; cfgParameters[0].ptrvalue = new char[SQL_PATH_SZ + 1]; // initialize cfgStruct cfgStruct.numItems = 1; cfgStruct.paramArray = cfgParameters; cfgStruct.flags = db2CfgDatabase; cfgStruct.dbname = db->getAlias(); cout << "\nUSE THE DB2 API:\n" << endl; cout << " db2CfgGet -- GET CONFIGURATION\n"; cout << "TO GET THE DATABASE CONFIGURATION AND DETERMINE\n"; cout << "THE SERVER WORKING PATH.\n" << endl; // get database configuration db2CfgGet( db2Version970, (void *)&cfgStruct, &sqlca ); DB2_API_CHECK("server log path -- get"); strncpy( serverLogPath, cfgParameters[0].ptrvalue, SQL_PATH_SZ ); delete [] cfgParameters[0].ptrvalue; // choose server working path // Let's say serverLogPath = "C:\DB2\NODE0001\....". // Keep for serverWorkingPath "C:\DB2" only. len = (int)(strstr(serverLogPath, "NODE") - serverLogPath - 1); memcpy( serverWorkingPath, serverLogPath, len ); serverWorkingPath[len] = '\0'; return 0; } // UtilRecov::ServerWorkingPathGet //************************************************************************** // DbLogRetainValueSave // Save LOGRETAIN value for the database //************************************************************************** int UtilRecov::DbLogRetainValueSave( DbEmb *db, sqluint16 *pLogRetainValue ) { int rc = 0; struct sqlca sqlca = { 0 }; db2CfgParam cfgParameters[1] = { 0 }; db2Cfg cfgStruct = { 0 }; // save log retain value cout << "\n******* Save LOGRETAIN for '" << db->getAlias() << "' database. *******" << endl; cfgParameters[0].flags = 0; cfgParameters[0].token = SQLF_DBTN_LOG_RETAIN; cfgParameters[0].ptrvalue = (char *)pLogRetainValue; // initialize cfgStruct cfgStruct.numItems = 1; cfgStruct.paramArray = cfgParameters; cfgStruct.flags = db2CfgDatabase; cfgStruct.dbname = db->getAlias(); // get database configuration db2CfgGet(db2Version970, (void *)&cfgStruct, &sqlca); DB2_API_CHECK("log retain value -- save"); return 0; } // UtilRecov::DbLogRetainValueSave //************************************************************************** // DbLogRetainValueRestore // Restore the LOGRETAIN value for the database //************************************************************************** int UtilRecov::DbLogRetainValueRestore( DbEmb *db, sqluint16 *pLogRetainValue ) { int rc = 0; struct sqlca sqlca = { 0 }; db2CfgParam cfgParameters[1] = { 0 }; db2Cfg cfgStruct = { 0 }; // restore the log retain value cout << "\n***** Restore LOGRETAIN for '" << db->getAlias() << "' database. *****" << endl; cfgParameters[0].flags = 0; cfgParameters[0].token = SQLF_DBTN_LOG_RETAIN; cfgParameters[0].ptrvalue = (char *)pLogRetainValue; // initialize cfgStruct cfgStruct.numItems = 1; cfgStruct.paramArray = cfgParameters; cfgStruct.flags = db2CfgDatabase | db2CfgDelayed; cfgStruct.dbname = db->getAlias(); // set database configuration db2CfgSet(db2Version970, (void *)&cfgStruct, &sqlca); DB2_API_CHECK("log retain value -- restore"); return 0; } // UtilRecov::DbLogRetainValueRestore //************************************************************************** // DbRecoveryHistoryFilePrune // Prunes the recovery history file by calling db2Prune API //************************************************************************** int UtilRecov::DbRecoveryHistoryFilePrune( DbEmb *db ) { int rc = 0; struct sqlca sqlca = { 0 }; struct db2PruneStruct histPruneParam = { 0 }; char timeStampPart[SQLU_TIME_STAMP_LEN + 1] = { 0 }; cout << "\n***************************************\n"; cout << "*** PRUNE THE RECOVERY HISTORY FILE ***\n"; cout << "***************************************\n"; cout << "\n-----------------------------------------------------------"; cout << "\nUSE THE DB2 API:" << endl; cout << " db2Prune -- PRUNE RECOVERY HISTORY FILE" << endl; cout << "AND THE SQL STATEMENTS:" << endl; cout << " CONNECT" << endl; cout << " CONNECT RESET" << endl; cout << "TO PRUNE THE RECOVERY HISTORY FILE." << endl; // connect to the database rc = db->Connect(); CHECKRC(rc, "db->Connect"); // prune the recovery history file cout << "\n Prune the recovery history file for '" << db->getAlias() << "' database." << endl; histPruneParam.piString = timeStampPart; strcpy(timeStampPart, "2010"); // year 2010 histPruneParam.iAction = DB2PRUNE_ACTION_HISTORY; histPruneParam.iOptions = DB2PRUNE_OPTION_FORCE; // Prune Recovery History File db2Prune(db2Version970, &histPruneParam, &sqlca); DB2_API_CHECK("recovery history file -- prune"); // disconnect from the database rc = db->Disconnect(); CHECKRC(rc, "db->Disconnect"); return 0; } // UtilRecov::DbRecoveryHistoryFilePrune //************************************************************************** // DbBackup // Performs the database backup //************************************************************************** int UtilRecov::DbBackup( DbEmb *db, char serverWorkingPath[], db2BackupStruct *backupStruct) { struct sqlca sqlca = { 0 }; db2TablespaceStruct tablespaceStruct = { 0 }; db2MediaListStruct mediaListStruct = { 0 }; //****************************** // BACK UP THE DATABASE //****************************** cout << "\n Backing up the \'" << db->getAlias() << "\' database...\n"; tablespaceStruct.tablespaces = NULL; tablespaceStruct.numTablespaces = 0; mediaListStruct.locations = &serverWorkingPath; mediaListStruct.numLocations = 1; mediaListStruct.locationType = SQLU_LOCAL_MEDIA; backupStruct->piDBAlias = db->getAlias(); backupStruct->piTablespaceList = &tablespaceStruct; backupStruct->piMediaList = &mediaListStruct; backupStruct->piUsername = db->getUser(); backupStruct->piPassword = db->getPswd(); backupStruct->piVendorOptions = NULL; backupStruct->iVendorOptionsSize = 0; backupStruct->iCallerAction = DB2BACKUP_BACKUP; backupStruct->iBufferSize = 16; /* 16 x 4KB */ backupStruct->iNumBuffers = 2; backupStruct->iParallelism = 1; backupStruct->iOptions = DB2BACKUP_OFFLINE | DB2BACKUP_DB; // The API db2Backup creates a backup copy of a database. // This API automatically establishes a connection to the specified // database. (This API can also be used to create a backup copy of a // table space). db2Backup(db2Version970, backupStruct, &sqlca); DB2_API_CHECK("Database -- Backup"); while (sqlca.sqlcode != 0) { // continue the backup operation // depending on the sqlca.sqlcode value, user action may be // required, such as mounting a new tape cout << "\n Continuing the backup process..." << endl; backupStruct->iCallerAction = DB2BACKUP_CONTINUE; db2Backup(db2Version970, backupStruct, &sqlca); DB2_API_CHECK("Database -- Backup"); } cout << " Backup finished." << endl; cout << " - backup image size : " << backupStruct->oBackupSize << " MB" << endl; cout << " - backup image path : " << mediaListStruct.locations[0] << endl; cout << " - backup image timestamp: " << backupStruct->oTimestamp << endl; return 0; } // UtilRecov::DbBackup //************************************************************************** // DbCreate // Create the specified database //************************************************************************** int UtilRecov::DbCreate( char existingDbAlias[], char newDbAlias[] ) { struct sqlca sqlca = { 0 }; char dbName[SQL_DBNAME_SZ + 1] = { 0 }; char dbLocalAlias[SQL_ALIAS_SZ + 1] = { 0 }; char dbPath[SQL_PATH_SZ + 1] = { 0 }; struct sqledbdesc dbDescriptor = { 0 }; SQLEDBTERRITORYINFO territoryInfo = { 0 }; struct db2CfgParam cfgParameters[2] = { 0 }; struct db2Cfg cfgStruct = { 0 }; cout << "\n Create '" << newDbAlias << "' empty db. with the same codeset as '" << existingDbAlias << "' db." << endl; // initialize cfgParameters cfgParameters[0].flags = 0; cfgParameters[0].token = SQLF_DBTN_TERRITORY; cfgParameters[0].ptrvalue = new char[SQL_LOCALE_SIZE + 1]; memset(cfgParameters[0].ptrvalue, '\0', SQL_LOCALE_SIZE + 1); cfgParameters[1].flags = 0; cfgParameters[1].token = SQLF_DBTN_CODESET; cfgParameters[1].ptrvalue = new char[SQL_CODESET_SIZE + 1]; memset(cfgParameters[1].ptrvalue, '\0', SQL_CODESET_SIZE + 1); // initialize cfgStruct cfgStruct.numItems = 2; cfgStruct.paramArray = cfgParameters; cfgStruct.flags = db2CfgDatabase; cfgStruct.dbname = existingDbAlias; // get two database configuration parameters db2CfgGet(db2Version970, (void *)&cfgStruct, &sqlca); DB2_API_CHECK("DB Config. -- Get"); // create a new database strcpy(dbName, newDbAlias); strcpy(dbLocalAlias, newDbAlias); strcpy(dbPath, ""); strcpy(dbDescriptor.sqldbdid, SQLE_DBDESC_2); dbDescriptor.sqldbccp = 0; dbDescriptor.sqldbcss = SQL_CS_NONE; strcpy(dbDescriptor.sqldbcmt, ""); dbDescriptor.sqldbsgp = 0; dbDescriptor.sqldbnsg = 10; dbDescriptor.sqltsext = -1; dbDescriptor.sqlcatts = NULL; dbDescriptor.sqlusrts = NULL; dbDescriptor.sqltmpts = NULL; strcpy(territoryInfo.sqldbcodeset, (char *)cfgParameters[1].ptrvalue); strcpy(territoryInfo.sqldblocale, (char *)cfgParameters[0].ptrvalue); // create database sqlecrea(dbName, dbLocalAlias, dbPath, &dbDescriptor, &territoryInfo, '\0', NULL, &sqlca); DB2_API_CHECK("Database -- Create"); // release the allocated memory delete [] cfgParameters[0].ptrvalue; delete [] cfgParameters[1].ptrvalue; return 0; } // UtilRecov::DbCreate //************************************************************************** // DbDrop // Drops and uncatalogs the specified database alias //************************************************************************** int UtilRecov::DbDrop( char dbAlias[] ) { struct sqlca sqlca = { 0 }; cout << "\n Drop the '" << dbAlias << "' database." << endl; // drop and uncatalog the database sqledrpd(dbAlias, &sqlca); DB2_API_CHECK("Database -- Drop"); return 0; } // UtilRecov::DbDrop //************************************************************************* // LogBufferDisplay // Displays the log buffer //************************************************************************* int UtilLog::LogBufferDisplay( char *logBuffer, sqluint32 numLogRecords, int conn) { int rc = 0; sqluint32 logRecordNb = 0; sqluint32 recordSize = 0; sqluint16 recordType = 0; sqluint16 recordFlag = 0; char *recordBuffer = NULL; int headerSize = 0; // initialize the recordBuffer if (logBuffer == NULL) { if (numLogRecords == 0) { /* there's nothing to do */ return 0; } else { /* we can't display NULL log records */ return 1; } } // If there is no connection to the database or if the iFilterOption // is OFF, the 8-byte LSN 'db2LSN' is prefixed to the log records. // If there is a connection to the database and the iFilterOption is // ON, the db2ReadLogFilterData structure will be prefixed to all // log records returned by the db2ReadLog API ( for compressed and // uncompressed data ) if (conn == 0) { headerSize = sizeof(db2LSN); } else { headerSize = sizeof(db2ReadLogFilterData); } recordBuffer = logBuffer; for (logRecordNb = 0; logRecordNb < numLogRecords; logRecordNb++) { if (conn == 1) { db2ReadLogFilterData *filterData = (db2ReadLogFilterData *)recordBuffer; printf("\nRLOG_FILTERDATA:\n"); printf(" recordLSN: %lu\n", filterData->recordLSN); printf(" realLogRecLen: %lu\n", filterData->realLogRecLen ); printf(" sqlcode: %d\n", filterData->sqlcode ); } recordBuffer += headerSize; recordSize = *(sqluint32 *) (recordBuffer); recordType = *(sqluint16 *) (recordBuffer + sizeof(sqluint32)); recordFlag = *(sqluint16 *) (recordBuffer + sizeof(sqluint32) + sizeof(sqluint16)); cout << " recordSize: " << recordSize << endl; rc = LogRecordDisplay(recordBuffer, recordSize, recordType, recordFlag); CHECKRC(rc, "LogRecordDisplay"); // update the recordBuffer recordBuffer += recordSize; } return 0; } // UtilLog::LogBufferDisplay //************************************************************************** // LogRecordDisplay // Displays the log records //************************************************************************** int UtilLog::LogRecordDisplay( char *recordBuffer, sqluint32 recordSize, sqluint16 recordType, sqluint16 recordFlag ) { int rc = 0; sqluint32 logManagerLogRecordHeaderSize = 0; char *recordDataBuffer = NULL; sqluint32 recordDataSize = 0; char *recordHeaderBuffer = NULL; sqluint8 componentIdentifier = 0; sqluint32 recordHeaderSize = 0; // determine the logManagerLogRecordHeaderSize logManagerLogRecordHeaderSize = 24; if( recordType == 0x0043 ) // compensation { logManagerLogRecordHeaderSize += sizeof(db2LSN); if( recordFlag & 0x0002 ) // propagatable { logManagerLogRecordHeaderSize += sizeof(db2LSN); } } switch (recordType) { case 0x008A: // Local Pending List case 0x0084: // Normal Commit case 0x0041: // Normal Abort recordDataBuffer = recordBuffer + logManagerLogRecordHeaderSize; recordDataSize = recordSize - logManagerLogRecordHeaderSize; rc = SimpleLogRecordDisplay( recordType, recordFlag, recordDataBuffer, recordDataSize ); CHECKRC(rc, "SimpleLogRecordDisplay"); break; case 0x004E: // Normal case 0x0043: // Compensation recordHeaderBuffer = recordBuffer + logManagerLogRecordHeaderSize; componentIdentifier = *(sqluint8 *) recordHeaderBuffer; switch (componentIdentifier) { case 1: // Data Manager Log Record recordHeaderSize = 6; break; default: cout << " Unknown complex log record: " << recordSize << " " << recordType << " " << componentIdentifier << endl; return 1; } recordDataBuffer = recordBuffer + logManagerLogRecordHeaderSize + recordHeaderSize; recordDataSize = recordSize - logManagerLogRecordHeaderSize - recordHeaderSize; rc = ComplexLogRecordDisplay( recordType, recordFlag, recordHeaderBuffer, recordHeaderSize, componentIdentifier, recordDataBuffer, recordDataSize ); CHECKRC(rc, "ComplexLogRecordDisplay"); break; default: cout << " Unknown log record: " << recordSize << " " << (char)recordType << endl; break; } return 0; } // UtilLog::LogRecordDisplay //************************************************************************** // SimpleLogRecordDisplay // Prints the minimum details of the log record //************************************************************************** int UtilLog::SimpleLogRecordDisplay( sqluint16 recordType, sqluint16 recordFlag, char *recordDataBuffer, sqluint32 recordDataSize ) { int rc = 0; sqluint32 timeTransactionCommited = 0; sqluint16 authIdLen = 0; char *authId = NULL; switch (recordType) { case 138: cout << "\n Record type: Local pending list" << endl; timeTransactionCommited = *(sqluint32 *) (recordDataBuffer); authIdLen = *(sqluint16 *) (recordDataBuffer + 2*sizeof(sqluint32)); authId = (char *)malloc(authIdLen + 1); memset(authId, '\0', (authIdLen + 1 )); memcpy(authId, (char *)(recordDataBuffer + 2*sizeof(sqluint32) + sizeof(sqluint16)), authIdLen); authId[authIdLen] = '\0'; cout << " UTC transaction committed(in secs since 70-01-01)" << ": " << dec << timeTransactionCommited << endl; cout << " authorization ID of the application: " << authId << endl; break; case 132: cout << "\n Record type: Normal commit" << endl; timeTransactionCommited = *(sqluint32 *) (recordDataBuffer); authIdLen = *(sqluint16 *) (recordDataBuffer + 2*sizeof(sqluint32)); authId = (char *)malloc(authIdLen + 1); memset( authId, '\0', (authIdLen + 1 )); memcpy(authId, (char *)(recordDataBuffer + 2*sizeof(sqluint32) + sizeof(sqluint16)), authIdLen); authId[authIdLen] = '\0'; cout << " UTC transaction committed(in secs since 70-01-01)" << ": " << dec << timeTransactionCommited << endl; cout << " authorization ID of the application: " << authId << endl; break; case 65: cout << "\n Record type: Normal abort" << endl; authIdLen = *(sqluint16 *) (recordDataBuffer); authId = (char *)malloc(authIdLen + 1); memset( authId, '\0', (authIdLen + 1 )); memcpy(authId, (char *)(recordDataBuffer + sizeof(sqluint16)), authIdLen); authId[authIdLen] = '\0'; cout << " authorization ID of the application: " << authId << endl; break; default: cout << " Unknown simple log record: " << (char)recordType << " " << recordDataSize << endl; break; } return 0; } // UtilLog::SimpleLogRecordDisplay //************************************************************************** // ComplexLogRecordDisplay // Prints a detailed information of the log record //************************************************************************** int UtilLog::ComplexLogRecordDisplay( sqluint16 recordType, sqluint16 recordFlag, char *recordHeaderBuffer, sqluint32 recordHeaderSize, sqluint8 componentIdentifier, char *recordDataBuffer, sqluint32 recordDataSize ) { int rc = 0; sqluint8 functionIdentifier = 0; // for insert, delete, undo delete RID recid; sqluint16 subRecordLen = 0; sqluint16 subRecordOffset = 0; char *subRecordBuffer = NULL; // for update RID newRID; sqluint16 newSubRecordLen = 0; sqluint16 newSubRecordOffset = 0; char *newSubRecordBuffer = NULL; RID oldRID; sqluint16 oldSubRecordLen = 0; sqluint16 oldSubRecordOffset = 0; char *oldSubRecordBuffer = NULL; // for alter table attributes sqluint64 alterBitMask = 0; sqluint64 alterBitValues = 0; switch( recordType ) { case 0x004E: cout << "\n Record type: Normal" << endl; break; case 0x0043: cout << "\n Record type: Compensation." << endl; break; default: cout << "\n Unknown complex log record type: " << recordType << endl; break; } switch (componentIdentifier) { case 1: cout << " component ID: DMS log record" << endl; break; default: cout << " unknown component ID: " << componentIdentifier << endl; break; } functionIdentifier = *(sqluint8 *) (recordHeaderBuffer + 1); switch (functionIdentifier) { case 161: cout << " function ID: Delete Record" << endl; subRecordLen = *( (sqluint16 *)( recordDataBuffer + sizeof(sqluint16) ) ); recid.set( recordDataBuffer + 3 * sizeof(sqluint16) ); subRecordOffset = *( (sqluint16 *)( recordDataBuffer + 3 * sizeof(sqluint16) + recid.size() ) ); cout << " RID: " << dec << recid.getString() << endl; cout << " subrecord length: " << subRecordLen << endl; cout << " subrecord offset: " << subRecordOffset << endl; subRecordBuffer = recordDataBuffer + 3 * sizeof(sqluint16) + recid.size() + sizeof(sqluint16); rc = LogSubRecordDisplay( subRecordBuffer, subRecordLen ); CHECKRC(rc, "LogSubRecordDisplay"); break; case 112: cout << " function ID: Undo Update Record" << endl; subRecordLen = *( (sqluint16 *)( recordDataBuffer + sizeof(sqluint16) ) ); recid.set( recordDataBuffer + 3 * sizeof(sqluint16) ); subRecordOffset = *( (sqluint16 *)( recordDataBuffer + 3 * sizeof(sqluint16) + recid.size() ) ); cout << " RID: " << dec << recid.getString() << endl; cout << " subrecord length: " << subRecordLen << endl; cout << " subrecord offset: " << subRecordOffset << endl; subRecordBuffer = recordDataBuffer + 3 * sizeof(sqluint16) + recid.size() + sizeof(sqluint16); rc = LogSubRecordDisplay( subRecordBuffer, subRecordLen ); CHECKRC(rc, "LogSubRecordDisplay"); break; case 110: cout << " function ID: Undo Insert Record" << endl; subRecordLen = *( (sqluint16 *)( recordDataBuffer + sizeof(sqluint16) ) ); recid.set( recordDataBuffer + 3 * sizeof(sqluint16) ); cout << " RID: " << dec << recid.getString() << endl; cout << " subrecord length: " << subRecordLen << endl; break; case 111: cout << " function ID: Undo Delete Record" << endl; subRecordLen = *( (sqluint16 *)( recordDataBuffer + sizeof(sqluint16) ) ); recid.set( recordDataBuffer + 3 * sizeof(sqluint16) ); subRecordOffset = *( (sqluint16 *)( recordDataBuffer + 3 * sizeof(sqluint16) + recid.size() ) ); cout << " RID: " << dec << recid.getString() << endl; cout << " subrecord length: " << subRecordLen << endl; cout << " subrecord offset: " << subRecordOffset << endl; subRecordBuffer = recordDataBuffer + 3 * sizeof(sqluint16) + recid.size() + sizeof(sqluint16); rc = LogSubRecordDisplay(subRecordBuffer, subRecordLen); CHECKRC(rc, "LogSubRecordDisplay"); break; case 162: cout << " function ID: Insert Record" << endl; subRecordLen = *( (sqluint16 *)( recordDataBuffer + sizeof(sqluint16) ) ); recid.set( recordDataBuffer + 3 * sizeof(sqluint16) ); subRecordOffset = *( (sqluint16 *)( recordDataBuffer + 3 * sizeof(sqluint16) + recid.size() ) ); cout << " RID: " << dec << recid.getString() << endl; cout << " subrecord length: " << subRecordLen << endl; cout << " subrecord offset: " << subRecordOffset << endl; subRecordBuffer = recordDataBuffer + 3 * sizeof(sqluint16) + recid.size() + sizeof(sqluint16); rc = LogSubRecordDisplay( subRecordBuffer, subRecordLen ); CHECKRC(rc, "LogSubRecordDisplay"); break; case 163: cout << " function ID: Update Record" << endl; newSubRecordLen = *( (sqluint16 *)( recordDataBuffer + sizeof(sqluint16) ) ); oldSubRecordLen = recordDataSize + 6 - //NEW 2 * 20 - newSubRecordLen; oldRID.set( recordDataBuffer + 3 * sizeof(sqluint16) ); oldSubRecordOffset = *( (sqluint16 *)( recordDataBuffer + 3 * sizeof(sqluint16) + oldRID.size() ) ); newRID.set( recordDataBuffer + 3 * sizeof(sqluint16) + oldRID.size() + sizeof(sqluint16) + oldSubRecordLen + recordHeaderSize + sizeof(sqluint16) ); newSubRecordOffset = *(sqluint16 *)( recordDataBuffer + 3 * sizeof(sqluint16) + oldRID.size() + sizeof(sqluint16) + oldSubRecordLen + recordHeaderSize + newRID.size() + sizeof(sqluint16) ); cout << " oldRID: " << dec << oldRID.getString() << endl; cout << " old subrecord length: " << oldSubRecordLen << endl; cout << " old subrecord offset: " << oldSubRecordOffset << endl; oldSubRecordBuffer = recordDataBuffer + 3 * sizeof(sqluint16) + oldRID.size() + sizeof(sqluint16); rc = LogSubRecordDisplay( oldSubRecordBuffer, oldSubRecordLen ); CHECKRC(rc, "LogSubRecordDisplay"); cout << " newRID: " << dec << newRID.getString() << endl; cout << " new subrecord length: " << newSubRecordLen << endl; cout << " new subrecord offset: " << newSubRecordOffset << endl; newSubRecordBuffer = recordDataBuffer + 3 * sizeof(sqluint16) + oldRID.size() + sizeof(sqluint16) + oldSubRecordLen + recordHeaderSize + 3 * sizeof(sqluint16) + newRID.size() + sizeof(sqluint16) ; rc = LogSubRecordDisplay( newSubRecordBuffer, newSubRecordLen ); CHECKRC(rc, "LogSubRecordDisplay"); break; case 165: cout << " function ID: Insert Record to Empty Page" << endl; subRecordLen = *( (sqluint16 *)( recordDataBuffer + sizeof(sqluint16) ) ); recid.set( recordDataBuffer + 3 * sizeof(sqluint16) ); subRecordOffset = *( (sqluint16 *)( recordDataBuffer + 3 * sizeof(sqluint16) + recid.size() ) ); cout << " RID: " << dec << recid.getString() << endl; cout << " subrecord length: " << subRecordLen << endl; cout << " subrecord offset: " << subRecordOffset << endl; subRecordBuffer = recordDataBuffer + 3 * sizeof(sqluint16) + recid.size() + sizeof(sqluint16); rc = LogSubRecordDisplay( subRecordBuffer, subRecordLen ); CHECKRC(rc, "LogSubRecordDisplay"); break; case 164: cout << " function ID: Delete Record to Empty Page" << endl; subRecordLen = *( (sqluint16 *)( recordDataBuffer + sizeof(sqluint16) ) ); recid.set( recordDataBuffer + 3 * sizeof(sqluint16) ); subRecordOffset = *( (sqluint16 *)( recordDataBuffer + 3 * sizeof(sqluint16) + recid.size() ) ); cout << " RID: " << dec << recid.getString() << endl; cout << " subrecord length: " << subRecordLen << endl; cout << " subrecord offset: " << subRecordOffset << endl; subRecordBuffer = recordDataBuffer + 3 * sizeof(sqluint16) + recid.size() + sizeof(sqluint16); rc = LogSubRecordDisplay( subRecordBuffer, subRecordLen ); CHECKRC(rc, "LogSubRecordDisplay"); break; case 166: cout << " function ID: Rollback delete Record to Empty Page" << endl; subRecordLen = *( (sqluint16 *)( recordDataBuffer + sizeof(sqluint16) ) ); recid.set( recordDataBuffer + 3 * sizeof(sqluint16) ); subRecordOffset = *( (sqluint16 *)( recordDataBuffer + 3 * sizeof(sqluint16) + recid.size() ) ); cout << " RID: " << dec << recid.getString() << endl; cout << " subrecord length: " << subRecordLen << endl; cout << " subrecord offset: " << subRecordOffset << endl; subRecordBuffer = recordDataBuffer + 3 * sizeof(sqluint16) + recid.size() + sizeof(sqluint16); rc = LogSubRecordDisplay( subRecordBuffer, subRecordLen ); CHECKRC(rc, "LogSubRecordDisplay"); break; case 124: cout << " function ID: Alter Table Attribute" << endl; alterBitMask = *(sqluint64 *) (recordDataBuffer); alterBitValues = *( (sqluint64 *)(recordDataBuffer + sizeof(sqluint64) ) ); if( alterBitMask & 0x00000001 ) { // Propagation attribute altered cout << " Propagation attribute is changed to "; if (alterBitValues & 0x00000001) { cout << "ON" << endl; } else { cout << "OFF" << endl; } } if (alterBitMask & 0x00000002) { // Check Pending attribute altered cout << " Check Pending attr. changed to: "; if (alterBitValues & 0x00000002) { cout << "ON" << endl; } else { cout << "OFF" << endl; } } if (alterBitMask & 0x00010000) { // Append Mode attribute altered cout << " Append Mode attr. changed to: "; if (alterBitValues & 0x00010000) { cout << "ON" << endl; } else { cout << "OFF" << endl; } } if (alterBitMask & 0x00200000) { // LF Propagation attribute altered cout << " LF Propagation attribute is changed to "; if (alterBitValues & 0x00200000) { cout << "ON" << endl; } else { cout << "OFF" << endl; } } if (alterBitMask & 0x00400000) { // LOB Propagation attribute altered cout << " LOB Propagation attr.changed to: "; if (alterBitValues & 0x00400000) { cout << "ON" << endl; } else { cout << "OFF" << endl; } } break; default: cout << " unknown function identifier: " << functionIdentifier << endl; break; } return 0; } // UtilLog::ComplexLogRecordDisplay /***************************************************************************/ /* LogSubRecordDisplay */ /* Prints the sub records for the log */ /***************************************************************************/ int UtilLog::LogSubRecordDisplay( char *recordBuffer, sqluint16 recordSize ) { int rc = 0; sqluint8 recordType = 0; sqluint8 updatableRecordType = 0; sqluint16 userDataFixedLength = 0; char *userDataBuffer = NULL; sqluint16 userDataSize = 0; recordType = *(sqluint8 *) (recordBuffer); if ((recordType != 0) && (recordType != 4) && (recordType != 16)) { cout << " Unknown subrecord type." << endl; } else if (recordType == 4) { cout << " subrecord type: Special control" << endl; } else // recordType == 0 or recordType == 16 // record Type 0 indicates a normal record // record Type 16, for the purposes of this program, should be treated // as type 0 { cout << " subrecord type: Updatable, "; updatableRecordType = *(sqluint8 *) (recordBuffer + 4); if (updatableRecordType != 1) { cout << "Internal control" << endl; } else { cout << "Formatted user data" << endl; userDataFixedLength = *(sqluint16 *) (recordBuffer + 6); cout << " user data fixed length: " << dec << userDataFixedLength << endl; userDataBuffer = recordBuffer + 8; userDataSize = recordSize - 8; rc = UserDataDisplay(userDataBuffer, userDataSize); CHECKRC(rc, "UserDataDisplay"); } } return 0; } // UtilLog::LogSubRecordDisplay //************************************************************************** // UserDataDisplay // Displays the user data section //************************************************************************** int UtilLog::UserDataDisplay( char *dataBuffer, sqluint16 dataSize ) { int rc = 0; sqluint16 line = 0; sqluint16 col = 0; const int rowLength = 10; cout << " user data:" << endl; for (line = 0; line * rowLength < dataSize; line = line + 1) { cout << " "; for (col = 0; col < rowLength; col = col + 1) { if (line * rowLength + col < dataSize) { cout.fill('0'); cout.width(2); cout.setf(ios::uppercase); cout << hex << (int)(dataBuffer[line * rowLength + col] & 0x0ff) << " "; } else { cout << " "; } } cout << "*"; for (col = 0; col < rowLength; col = col + 1) { if (line * rowLength + col < dataSize) { if (isalpha(dataBuffer[line * rowLength + col]) || isdigit(dataBuffer[line * rowLength + col])) { cout << dataBuffer[line * rowLength + col]; } else { cout << "."; } } else { cout << " "; } } cout << "*" << endl; } cout.setf(ios::dec); return 0; } // UtilLog::UserDataDisplay