/*****************************************************************************
** (c) Copyright IBM Corp. 2008 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: dbredirect.sqc
** 
** PURPOSE: This sample demonstrates how to perform redirected restore of
**          a database
**
** PREREQUISITE: create 'test' database with AUTOMATIC STORAGE NO option
**               "db2 CREATE DATABASE test AUTOMATIC STORAGE NO"
** 
** EXECUTION: bldapp dbredirect test
**            dbredirect
**
**                           
** 
** APIs used:
**    db2Restore -- Restore database
**    db2Backup -- Backup database
**    db2CfgSet -- Set Configuration
**    sqlbstsc - Set tablespace containers
**
****************************************************************************
** For information on using SQL statements, see the SQL 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/ 
**
****************************************************************************
**
**  SAMPLE DESCRIPTION                                                      
**
****************************************************************************
** This sample demonstrates how to perform redirected restore of a database
** Following are the steps 
**
** 1. Backup 'test' database
** 2. Get tablespace and container information of 'test' database
** 3. Backup 'test' database
** 4. Start restore operation
** 5. set tablespace containers for redirected restored database
** 6. Continue restore operation
***************************************************************************/

#include <stdlib.h>
#include "utilrecov.c"
#include "utilemb.h"
#include <sqlutil.h>

struct tsinfo
{
	int tbspid;
	int cntid[5];
	int numContainers;
	char cntname[5][500];
	char tbspname[500];
      char cntype[5][500];
	int totalpages[5];
};

struct tsinfo ts[10];

/* local function prototypes */
int DbBackupAndRedirectedRestore(char *, char *, char *, char *, char *, char *, char *);

/* support function called by DbBackupAndRedirectedRestore() */
int InaccessibleContainersRedefine(char serverWorkingPath[], int numTablespaces);

EXEC SQL BEGIN DECLARE SECTION;
char tbspname[100];
char stmt[1000];
short tbspid;
char cntname[500];
short cntid;
char cntype[500];
short tpages;
EXEC SQL END DECLARE SECTION;

int main(int argc, char *argv[])
{
  int rc = 0;
  char nodeName[SQL_INSTNAME_SZ + 1] = { 0 };
  char serverWorkingPath[SQL_PATH_SZ + 1] = { 0 };
  sqluint16 savedLogRetainValue = 0;
  char redirectedRestoredDbAlias[SQL_ALIAS_SZ + 1] = { 0 };
  char dbAlias[SQL_ALIAS_SZ + 1] = { 0 };
  char user[USERID_SZ + 1] = { 0 };
  char pswd[PSWD_SZ + 1] = { 0 };
  char storPath1[SQL_PATH_SZ + 1] = { 0 };
  char storPath2[SQL_PATH_SZ + 1] = { 0 };
  char * home;
  char cmd1[256]; 
  char cmd2[256]; 
  char cmd3[256]; 
  char cmd4[256]; 

  /* check the command line arguments */
  rc = CmdLineArgsCheck3(argc, argv, dbAlias, nodeName, user, pswd);
  CHECKRC(rc, "CmdLineArgsCheck3");

  printf("\nTHIS SAMPLE SHOWS HOW TO PERFORM A REDIRECTED RESTORE\n");
  printf("FROM A DATABASE BACKUP.\n");



  strcpy(redirectedRestoredDbAlias, "RRDB");

  /* create directory for restore database */
  home = getenv("HOME");

  #if (defined(DB2NT))
    sprintf(storPath1, "%s\\restore1",home);
    sprintf(storPath2, "%s\\restore2",home);
    sprintf(cmd1, "rmdir /S /Q %s", storPath1);
    sprintf(cmd2, "rmdir /S /Q %s", storPath2);

  #else
    sprintf(storPath1, "%s/restore1",home);
    sprintf(storPath2, "%s/restore2",home);
    sprintf(cmd1, "rm -rf %s", storPath1);
    sprintf(cmd2, "rm -rf %s", storPath2);
  #endif

  sprintf(cmd3, "mkdir %s", storPath1);
  sprintf(cmd4, "mkdir %s", storPath2);

  rc = system(cmd1);
  rc = system(cmd2);
  rc = system(cmd3);
  rc = system(cmd4);

  /* attach to a local or remote instance */
  rc = InstanceAttach(nodeName, user, pswd);
  CHECKRC(rc, "Instance Attach");

  strcpy(dbAlias, "test");

  /* get the server working path */
  rc = ServerWorkingPathGet(dbAlias, serverWorkingPath);
  CHECKRC(rc, "ServerWorkingPathGet");

  printf("\nNOTE: Backup images will be created on the server\n");
  printf("      in the directory %s,\n", serverWorkingPath);
  printf("      and will not be deleted by the program.\n");

  /* save log retain value */
  rc = DbLogRetainValueSave(dbAlias, &savedLogRetainValue);
  CHECKRC(rc, "DbLogRetainValueSave");

  /* call the sample function */
  rc = DbRecoveryHistoryFilePrune(dbAlias, user, pswd);
  CHECKRC(rc, "DbRecoveryHistoryFilePrune");


  rc = DbBackupAndRedirectedRestore(dbAlias,
                                    redirectedRestoredDbAlias,
                                    user, pswd, serverWorkingPath,
                                    storPath1, storPath2);
  CHECKRC(rc, "DbBackupAndRedirectedRestore");

  /* restore logretain value */
  rc = DbLogRetainValueRestore(dbAlias, &savedLogRetainValue);
  CHECKRC(rc, "DbLogRetainValueRestore");

  /* detach from the local or remote instance */
  rc = InstanceDetach(nodeName);
  CHECKRC(rc, "InstanceDetach");

  return 0;
} /* end main */

int DbBackupAndRedirectedRestore(char dbAlias[],
                                 char restoredDbAlias[],
                                 char user[],
                                 char pswd[], 
                                 char serverWorkingPath[],
                                 char storPath1[],
                                 char storPath2[])
{
  int rc = 0;
  struct sqlca sqlca = { 0 };
  db2CfgParam cfgParameters[1] = { 0 };
  db2Cfg cfgStruct = { 0 };
  unsigned short logretain = 0;
  
  char restoreTimestamp[SQLU_TIME_STAMP_LEN + 1] = { 0 };

  db2BackupStruct backupStruct = { 0 };
  db2TablespaceStruct tablespaceStruct = { 0 };
  db2MediaListStruct mediaListStruct = { 0 };
  db2Uint32 backupImageSize = 0;
  db2RestoreStruct restoreStruct = { 0 };
  db2TablespaceStruct rtablespaceStruct = { 0 };
  db2MediaListStruct rmediaListStruct = { 0 };
  db2StoragePathsStruct storagePathsStruct = { 0 };
  char * storagePaths[2];
  int numTbsps = 0;

  storagePaths[0]= (char *) malloc (sizeof(char)* (SQL_PATH_SZ + 1));
  storagePaths[1]= (char *) malloc (sizeof(char)* (SQL_PATH_SZ + 1));

  if (storagePaths[0] == NULL || storagePaths[1] == NULL)
  {
    printf("ERROR: Unable to allocate memory for storage paths.\n\n");
    return (1);
  }


  printf("\n**************************\n");
  printf("*** REDIRECTED RESTORE ***\n");
  printf("**************************\n");
  printf("\nUSE THE DB2 APIs:\n");
  printf("  db2CfgSet -- Update Configuration\n");
  printf("  db2Backup -- Backup Database\n");
  printf("  sqlecrea -- Create Database\n");
  printf("  db2Restore -- Restore Database\n");
  printf("  sqlbmtsq -- Tablespace Query\n");
  printf("  sqlbtcq -- Tablespace Container Query\n");
  printf("  sqlbstsc -- Set Tablespace Containers\n");
  printf("  sqlefmem -- Free Memory\n");
  printf("  sqledrpd -- Drop Database\n");
  printf("TO BACK UP AND DO A REDIRECTED RESTORE OF A DATABASE.\n");

  printf("\n  Update \'%s\' database configuration:\n", dbAlias);
  printf("    - Disable the database configuration parameter LOGRETAIN \n");
  printf("        i.e., set LOGRETAIN = OFF/NO\n");

  /* initialize cfgParameters */
  /* SQLF_DBTN_LOG_RETAIN is a token of the updatable database configuration
     parameter 'logretain'; it is used to update the database configuration
     file */
  cfgParameters[0].flags = 0;
  cfgParameters[0].token = SQLF_DBTN_LOG_RETAIN;
  cfgParameters[0].ptrvalue = (char *)&logretain;

  /* disable the database configuration parameter 'logretain' */
  logretain = SQLF_LOGRETAIN_DISABLE;

  /* initialize cfgStruct */
  cfgStruct.numItems = 1;
  cfgStruct.paramArray = cfgParameters;
  cfgStruct.flags = db2CfgDatabase | db2CfgDelayed;
  cfgStruct.dbname = dbAlias;

  /* get database configuration */
  db2CfgSet(db2Version970, (void *)&cfgStruct, &sqlca);
  DB2_API_CHECK("Db Log Retain -- Disable");

  /*******************************/
  /*  1. BACK UP THE DATABASE    */
  /*******************************/


  printf("backup database %s\n", dbAlias);

  /* Calling up the routine for database backup */
  rc = DbBackup(dbAlias, user, pswd, serverWorkingPath, &backupStruct);
  CHECKRC(rc, "DbBackup");

  /*********************************************/
  /*  2. GET TABLESPACE AND CONTAINER INFO      */
  /*********************************************/

  /* Get tablespaces and container info before starting restore operation */
  numTbsps = getTablespaceInfo(dbAlias, user, pswd);


  /***************************************/
  /*  3. START RESTORING THE DATABASE    */
  /***************************************/

  strcpy(restoreTimestamp, backupStruct.oTimestamp);

  /* The database is will be restored on new tablespace path 'tbspPath1'
     and 'tbspPath1'. */

  rtablespaceStruct.tablespaces = NULL;
  rtablespaceStruct.numTablespaces = 0;


  rmediaListStruct.locations = &serverWorkingPath;
  rmediaListStruct.numLocations = 1;
  rmediaListStruct.locationType = SQLU_LOCAL_MEDIA;

  restoreStruct.piSourceDBAlias = dbAlias;
  restoreStruct.piTargetDBAlias = restoredDbAlias;
  restoreStruct.piTimestamp = restoreTimestamp;
  restoreStruct.piTargetDBPath = NULL;
  restoreStruct.piReportFile = NULL;
  restoreStruct.piTablespaceList = &rtablespaceStruct;
  restoreStruct.piMediaList = &rmediaListStruct;
  restoreStruct.piUsername = user;
  restoreStruct.piPassword = pswd;
  restoreStruct.piNewLogPath = NULL;
  restoreStruct.piVendorOptions = NULL;
  restoreStruct.iVendorOptionsSize = 0;
  restoreStruct.iParallelism = 1;
  restoreStruct.iBufferSize = 1024;     /*  1024 x 4KB */
  restoreStruct.iNumBuffers = 2;
  restoreStruct.piTargetDBPath = storPath2 ;

  /* The database will be restored on new storage paths 'storPath1'
     and 'storPath2'. */
  strcpy( storagePaths[0], storPath1 );
  strcpy( storagePaths[1], storPath2 );

  /*
  storagePathsStruct.numStoragePaths = 2;
  storagePathsStruct.storagePaths = storagePaths;
  restoreStruct.piStoragePaths = &storagePathsStruct;

  */

  restoreStruct.iOptions = DB2RESTORE_OFFLINE | DB2RESTORE_DB | 
	                   DB2RESTORE_NODATALINK | DB2RESTORE_NOROLLFWD;

  printf("\n  Restoring a database ...\n");
  printf("    - source image alias     : %s\n", dbAlias);
  printf("    - source image time stamp: %s\n", restoreTimestamp);
  printf("    - target database        : %s\n", restoredDbAlias);

  restoreStruct.iCallerAction = DB2RESTORE_RESTORE_STORDEF;

  /* The API db2Restore is used to restore a database that has been backed
     up using the API db2Backup. */
  db2Restore(db2Version970, &restoreStruct, &sqlca);
  
  /* If restoring to a different database and restoreDbAlias already exists,
     SQLCODE 2529 is expected. */
  if (strcmp(dbAlias, restoredDbAlias))
  {
    printf("\n  SQLCODE 2529 is expected if target database '%s' already exists\n", 
          restoredDbAlias);
  }

  EXPECTED_WARN_CHECK("database restore -- start");

  while (sqlca.sqlcode != 0)
  {
    /* continue the restore operation */
    printf("\n  Continuing the restore operation...\n");

    /* depending on the sqlca.sqlcode value, user action may be
       required, such as mounting a new tape */


    /*********************************************************/
    /*  4. SET CONTAINERS FOR REDIRECTED RESTORE DATABASE    */
    /*********************************************************/

    if (sqlca.sqlcode == SQLUD_INACCESSABLE_CONTAINER)
    {
      /* redefine the table space container layout */
      printf("\n  Find and redefine inaccessable containers.\n");

      rc = InaccessibleContainersRedefine(serverWorkingPath, numTbsps);
      CHECKRC(rc, "InaccessibleContainersRedefine");
    }

    restoreStruct.iCallerAction = DB2RESTORE_CONTINUE;


    /***************************************/
    /*  5. CONTINUE RESTORE DATABASE       */
    /***************************************/

    /* restore the database */
    db2Restore(db2Version970, &restoreStruct, &sqlca);
    DB2_API_CHECK("database restore -- continue");
  }

  printf("\n  Restore finished.\n");

  /* Verify if tablespace container paths are set or not for  
   * restored database */
  numTbsps = getTablespaceInfo(restoredDbAlias, user, pswd);


  /* drop the restored database */
  rc = DbDrop(restoredDbAlias);
  CHECKRC(rc, "DbDrop");

  return 0;
} /* DbBackupAndRedirectedRestore */


int InaccessibleContainersRedefine(char serverWorkingPath[], int numTablespaces)
{
  struct sqlca sqlca = { 0 };
  int tspNb = 0;
  int contNb = 0;
  char pathSep = '/';
  char temp[500];
  struct SQLB_TBSCONTQRY_DATA cnt = {0};


  /* refedine the inaccessable containers */
  for (tspNb = 0; tspNb < numTablespaces; tspNb++)
  {

    /* redefine inaccessable container  */
    printf("\n    Redefine inaccessable container:\n");     
    printf("      - table space name: %s\n", ts[tspNb].tbspname);
  
    for (contNb = 0; contNb < ts[tspNb].numContainers; contNb++)
    {

	if (strcmp(ts[tspNb].cntype[contNb], "PATH") == 0)
	{
         printf("      - default container name: %s\n", 
			   ts[tspNb].cntname[contNb]); 		
         sprintf(temp, "%s%cSQLTN%04d.%d", serverWorkingPath, 
			   pathSep,ts[tspNb].tbspid, ts[tspNb].cntid[contNb]);
	   cnt.contType = SQLB_CONT_PATH;
	   cnt.id = ts[tspNb].cntid[contNb];
	   cnt.nameLen = strlen(temp);
	   cnt.totalPages = ts[tspNb].totalpages[contNb];
	   strcpy(cnt.name, temp);

	   printf("      - new container name: %s\n",cnt.name);
    
	   sqlbstsc(&sqlca,
             SQLB_SET_CONT_FINAL_STATE,
             ts[tspNb].tbspid, ts[tspNb].numContainers, &cnt);
         DB2_API_CHECK("tablespace containers -- redefine");

	}
	else
	{
	   printf("Unknown container type\n");
	}

    }

  } 

  return 0;
} /* InaccessibleContainersRedefine */


int getTablespaceInfo(char dbAlias[], char user[], char pswd[])
{
  struct sqlca sqlca = { 0 };
  int tspNb = 0;
  int contNb = 0;
  int numTablespaces = 0;
  int rc = 0;
  
  /*****************************************************/
  /*   Get tablespace and container information        */
  /*****************************************************/

  /* connect to database */
  rc = DbConn(dbAlias, user, pswd);
  if(rc != 0)
  {
    return rc;
  }

  memset(stmt, '\0', sizeof(stmt));
  printf("\n\nGet tablespace information using MON_GET_TABLESPACE table function \n");
  strcpy(stmt, "SELECT tbsp_name, tbsp_id from TABLE(MON_GET_TABLESPACE('', -1))");


  EXEC SQL PREPARE st FROM :stmt;
  EMB_SQL_CHECK("PREPARE stmt");

  EXEC SQL DECLARE cur1 CURSOR FOR st;
  EMB_SQL_CHECK("DECLARE CURSOR cur1");

  EXEC SQL OPEN cur1;
  EMB_SQL_CHECK("OPEN cur1");

  EXEC SQL FETCH cur1 into :tbspname, :tbspid;
  EMB_SQL_CHECK("FETCH cur1");

  while(sqlca.sqlcode != 100)
  {
    ts[tspNb].tbspid = tbspid;     
    strcpy(ts[tspNb].tbspname, tbspname); 

    printf("\ntablespace id = %d\n", ts[tspNb].tbspid);
    printf("tablespace name = %s\n", ts[tspNb].tbspname);

    EXEC SQL FETCH cur1 into :tbspname, :tbspid;
    EMB_SQL_CHECK("FETCH cur1");

    tspNb++;
    
  }

  EXEC SQL CLOSE cur1;
  EMB_SQL_CHECK("CLOSE cursor");

  numTablespaces = tspNb;
  printf("\nNum of tablespaces = %d\n", numTablespaces);

  /* For each tablespace, get containers information */
  printf("\n\nGet containers information for each tablespace\n\n");
  for (tspNb = 0; tspNb < numTablespaces; tspNb++)
  {
    contNb = 0;

    strcpy(stmt, "SELECT tbsp_id, container_id,container_name,container_type, "
		 " total_pages from TABLE(MON_GET_CONTAINER('', -1))"
		 " WHERE tbsp_id = ?");

    EXEC SQL PREPARE st1 FROM :stmt;
    EMB_SQL_CHECK("PREPARE stmt");

    EXEC SQL DECLARE cur2 CURSOR FOR st1;
    EMB_SQL_CHECK("DECLARE CURSOR cur2");

    tbspid = ts[tspNb].tbspid;

    EXEC SQL OPEN cur2 using :tbspid;
    EMB_SQL_CHECK("OPEN cur2") ;

    EXEC SQL FETCH cur2 into :tbspid, :cntid, :cntname, :cntype, :tpages;
    EMB_SQL_CHECK("FETCH cur2");


    while(sqlca.sqlcode != 100)
    {
     
      ts[tspNb].cntid[contNb] = cntid;

      strcpy( ts[tspNb].cntname[contNb], cntname);
      printf("Container name = %s\n", ts[tspNb].cntname[contNb]);
      printf("Container id = %d\n", ts[tspNb].cntid[contNb]);

      strcpy(ts[tspNb].cntype[contNb], cntype);
      ts[tspNb].totalpages[contNb] = tpages;

      EXEC SQL FETCH cur2 into :tbspid, :cntid, :cntname, :cntype, :tpages;
      EMB_SQL_CHECK("FETCH cur1");

      contNb++;	    
    }

    ts[tspNb].numContainers = contNb;
    
    EXEC SQL CLOSE cur2;
    EMB_SQL_CHECK("CLOSE cursor");
  
  } 

  EXEC SQL CONNECT RESET;
  EMB_SQL_CHECK("CONNECT RESET");

  return numTablespaces;

}